| // Copyright 2020 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_HEAP_GC_CALLBACKS_H_ |
| #define V8_HEAP_GC_CALLBACKS_H_ |
| |
| #include <algorithm> |
| #include <tuple> |
| #include <vector> |
| |
| #include "include/v8-callbacks.h" |
| #include "src/base/logging.h" |
| #include "src/common/assert-scope.h" |
| |
| namespace v8::internal { |
| |
| class RootVisitor; |
| |
| class GCCallbacks final { |
| public: |
| using CallbackType = void (*)(v8::Isolate*, GCType, GCCallbackFlags, void*); |
| |
| void Add(CallbackType callback, v8::Isolate* isolate, GCType gc_type, |
| void* data) { |
| DCHECK_NOT_NULL(callback); |
| DCHECK_EQ(callbacks_.end(), FindCallback(callback, data)); |
| callbacks_.emplace_back(callback, isolate, gc_type, data); |
| } |
| |
| void Remove(CallbackType callback, void* data) { |
| auto it = FindCallback(callback, data); |
| DCHECK_NE(callbacks_.end(), it); |
| *it = callbacks_.back(); |
| callbacks_.pop_back(); |
| } |
| |
| void Invoke(GCType gc_type, GCCallbackFlags gc_callback_flags) const { |
| AllowGarbageCollection scope; |
| for (const CallbackData& callback_data : callbacks_) { |
| if (gc_type & callback_data.gc_type) { |
| callback_data.callback(callback_data.isolate, gc_type, |
| gc_callback_flags, callback_data.user_data); |
| } |
| } |
| } |
| |
| bool IsEmpty() const { return callbacks_.empty(); } |
| |
| private: |
| struct CallbackData final { |
| CallbackData(CallbackType callback, v8::Isolate* isolate, GCType gc_type, |
| void* user_data) |
| : callback(callback), |
| isolate(isolate), |
| gc_type(gc_type), |
| user_data(user_data) {} |
| |
| CallbackType callback; |
| v8::Isolate* isolate; |
| GCType gc_type; |
| void* user_data; |
| }; |
| |
| std::vector<CallbackData>::iterator FindCallback(CallbackType callback, |
| void* data) { |
| return std::find_if(callbacks_.begin(), callbacks_.end(), |
| [callback, data](CallbackData& callback_data) { |
| return callback_data.callback == callback && |
| callback_data.user_data == data; |
| }); |
| } |
| |
| std::vector<CallbackData> callbacks_; |
| }; |
| |
| // This interface allows to extend the GC to consider more roots during a GC |
| // cycle. This allows us to avoid the use of handles in C++ objects. |
| // |
| // During a GC the `Iterate()` method will be invoked with the GC's RootVisitor |
| // passed in as argument. The implementer of this interface needs to invoke the |
| // visitor on each unhandlified pointer in the object. The GC will both keep the |
| // referenced object alive and also updates its address if the object gets |
| // relocated. Note that in order to do so `Iterate()` may be invoked more than |
| // once per GC. |
| // |
| // Note that an object implementing GCRootsProvider still needs to be registered |
| // with a thread (=LocalHeap/LocalIsolate) using GCRootsProviderScope. |
| class GCRootsProvider { |
| public: |
| virtual ~GCRootsProvider() {} |
| |
| virtual void Iterate(RootVisitor* v) = 0; |
| }; |
| |
| class GCCallbacksInSafepoint final { |
| public: |
| using CallbackType = void (*)(void*); |
| |
| enum GCType { kLocal = 1 << 0, kShared = 1 << 1, kAll = kLocal | kShared }; |
| |
| void Add(CallbackType callback, void* data, GCType gc_type) { |
| DCHECK_NOT_NULL(callback); |
| DCHECK_EQ(callbacks_.end(), FindCallback(callback, data)); |
| callbacks_.emplace_back(callback, data, gc_type); |
| } |
| |
| void Remove(CallbackType callback, void* data) { |
| auto it = FindCallback(callback, data); |
| DCHECK_NE(callbacks_.end(), it); |
| *it = callbacks_.back(); |
| callbacks_.pop_back(); |
| } |
| |
| void Invoke(GCType gc_type) const { |
| DisallowGarbageCollection scope; |
| for (const CallbackData& callback_data : callbacks_) { |
| if (callback_data.gc_type_ & gc_type) |
| callback_data.callback(callback_data.user_data); |
| } |
| } |
| |
| bool IsEmpty() const { return callbacks_.empty(); } |
| |
| private: |
| struct CallbackData final { |
| CallbackData(CallbackType callback, void* user_data, GCType gc_type) |
| : callback(callback), user_data(user_data), gc_type_(gc_type) {} |
| |
| CallbackType callback; |
| void* user_data; |
| GCType gc_type_; |
| }; |
| |
| std::vector<CallbackData>::iterator FindCallback(CallbackType callback, |
| void* data) { |
| return std::find_if(callbacks_.begin(), callbacks_.end(), |
| [callback, data](CallbackData& callback_data) { |
| return callback_data.callback == callback && |
| callback_data.user_data == data; |
| }); |
| } |
| |
| std::vector<CallbackData> callbacks_; |
| }; |
| |
| // GCRootsProviderScope registers a C++ object implementing the GCRootsProvider |
| // with the current thread (=LocalHeap/LocalIsolate) for the duration of the |
| // scope. |
| // |
| // In every GC happening inside this scope, the GC will invoke the registered |
| // GCRootsProvider for all threads. See GCRootsProvider for more details. |
| class V8_NODISCARD GCRootsProviderScope final { |
| public: |
| V8_INLINE GCRootsProviderScope(LocalIsolate* local_isolate, |
| GCRootsProvider* provider); |
| V8_INLINE GCRootsProviderScope(LocalHeap* local_heap, |
| GCRootsProvider* provider); |
| V8_INLINE ~GCRootsProviderScope(); |
| |
| GCRootsProviderScope(GCRootsProviderScope&) = delete; |
| GCRootsProviderScope(GCRootsProviderScope&&) = delete; |
| |
| private: |
| #if DEBUG |
| GCRootsProvider* provider_; |
| #endif |
| LocalHeap* local_heap_; |
| }; |
| |
| } // namespace v8::internal |
| |
| #endif // V8_HEAP_GC_CALLBACKS_H_ |