// 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_SAFEPOINT_H_ #define V8_HEAP_SAFEPOINT_H_ #include "src/base/platform/condition-variable.h" #include "src/base/platform/mutex.h" #include "src/common/globals.h" #include "src/handles/persistent-handles.h" #include "src/heap/local-heap.h" #include "src/objects/visitors.h" namespace v8 { namespace internal { class Heap; class LocalHeap; class PerClientSafepointData; class RootVisitor; // Used to bring all threads with heap access in an isolate to a safepoint such // that e.g. a garbage collection can be performed. class IsolateSafepoint final { public: explicit IsolateSafepoint(Heap* heap); // Iterate handles in local heaps void Iterate(RootVisitor* visitor); // Iterate local heaps template <typename Callback> void IterateLocalHeaps(Callback callback) { AssertActive(); for (LocalHeap* current = local_heaps_head_; current; current = current->next_) { callback(current); } } void AssertActive() { local_heaps_mutex_.AssertHeld(); } V8_EXPORT_PRIVATE void AssertMainThreadIsOnlyThread(); private: class Barrier { base::Mutex mutex_; base::ConditionVariable cv_resume_; base::ConditionVariable cv_stopped_; bool armed_; size_t stopped_ = 0; bool IsArmed() { return armed_; } public: Barrier() : armed_(false), stopped_(0) {} void Arm(); void Disarm(); void WaitUntilRunningThreadsInSafepoint(size_t running); void WaitInSafepoint(); void WaitInUnpark(); void NotifyPark(); }; enum class IncludeMainThread { kYes, kNo }; // Wait until unpark operation is safe again. void WaitInUnpark(); // Enter the safepoint from a running thread. void WaitInSafepoint(); // Running thread reached a safepoint by parking itself. void NotifyPark(); // Methods for entering/leaving local safepoint scopes. void EnterLocalSafepointScope(); void LeaveLocalSafepointScope(); // Methods for entering/leaving global safepoint scopes. void TryInitiateGlobalSafepointScope(Isolate* initiator, PerClientSafepointData* client_data); void InitiateGlobalSafepointScope(Isolate* initiator, PerClientSafepointData* client_data); void InitiateGlobalSafepointScopeRaw(Isolate* initiator, PerClientSafepointData* client_data); void LeaveGlobalSafepointScope(Isolate* initiator); // Blocks until all running threads reached a safepoint. void WaitUntilRunningThreadsInSafepoint( const PerClientSafepointData* client_data); IncludeMainThread IncludeMainThreadUnlessInitiator(Isolate* initiator); void LockMutex(LocalHeap* local_heap); size_t SetSafepointRequestedFlags(IncludeMainThread include_main_thread); void ClearSafepointRequestedFlags(IncludeMainThread include_main_thread); template <typename Callback> void AddLocalHeap(LocalHeap* local_heap, Callback callback) { // Safepoint holds this lock in order to stop threads from starting or // stopping. base::RecursiveMutexGuard guard(&local_heaps_mutex_); // Additional code protected from safepoint callback(); // Add list to doubly-linked list if (local_heaps_head_) local_heaps_head_->prev_ = local_heap; local_heap->prev_ = nullptr; local_heap->next_ = local_heaps_head_; local_heaps_head_ = local_heap; } template <typename Callback> void RemoveLocalHeap(LocalHeap* local_heap, Callback callback) { base::RecursiveMutexGuard guard(&local_heaps_mutex_); // Additional code protected from safepoint callback(); // Remove list from doubly-linked list if (local_heap->next_) local_heap->next_->prev_ = local_heap->prev_; if (local_heap->prev_) local_heap->prev_->next_ = local_heap->next_; else local_heaps_head_ = local_heap->next_; } Isolate* isolate() const; Isolate* shared_isolate() const; Barrier barrier_; Heap* heap_; // Mutex is used both for safepointing and adding/removing threads. A // RecursiveMutex is needed since we need to support nested SafepointScopes. base::RecursiveMutex local_heaps_mutex_; LocalHeap* local_heaps_head_; int active_safepoint_scopes_; friend class GlobalSafepoint; friend class GlobalSafepointScope; friend class LocalHeap; friend class SafepointScope; }; class V8_NODISCARD SafepointScope { public: V8_EXPORT_PRIVATE explicit SafepointScope(Heap* heap); V8_EXPORT_PRIVATE ~SafepointScope(); private: IsolateSafepoint* safepoint_; }; // Used for reaching a global safepoint, a safepoint across all client isolates // of the shared isolate. class GlobalSafepoint final { public: explicit GlobalSafepoint(Isolate* isolate); void AppendClient(Isolate* client); void RemoveClient(Isolate* client); template <typename Callback> void IterateClientIsolates(Callback callback) { for (Isolate* current = clients_head_; current; current = current->global_safepoint_next_client_isolate_) { callback(current); } } void AssertNoClients(); void AssertActive() { clients_mutex_.AssertHeld(); } private: void EnterGlobalSafepointScope(Isolate* initiator); void LeaveGlobalSafepointScope(Isolate* initiator); Isolate* const shared_isolate_; Heap* const shared_heap_; base::Mutex clients_mutex_; Isolate* clients_head_ = nullptr; friend class GlobalSafepointScope; friend class Isolate; }; class V8_NODISCARD GlobalSafepointScope { public: V8_EXPORT_PRIVATE explicit GlobalSafepointScope(Isolate* initiator); V8_EXPORT_PRIVATE ~GlobalSafepointScope(); private: Isolate* const initiator_; Isolate* const shared_isolate_; }; } // namespace internal } // namespace v8 #endif // V8_HEAP_SAFEPOINT_H_