1 // Copyright 2020 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_HEAP_SAFEPOINT_H_ 6 #define V8_HEAP_SAFEPOINT_H_ 7 8 #include "src/base/platform/condition-variable.h" 9 #include "src/base/platform/mutex.h" 10 #include "src/common/globals.h" 11 #include "src/handles/persistent-handles.h" 12 #include "src/heap/local-heap.h" 13 #include "src/objects/visitors.h" 14 15 namespace v8 { 16 namespace internal { 17 18 class Heap; 19 class LocalHeap; 20 class PerClientSafepointData; 21 class RootVisitor; 22 23 // Used to bring all threads with heap access in an isolate to a safepoint such 24 // that e.g. a garbage collection can be performed. 25 class IsolateSafepoint final { 26 public: 27 explicit IsolateSafepoint(Heap* heap); 28 29 // Iterate handles in local heaps 30 void Iterate(RootVisitor* visitor); 31 32 // Iterate local heaps 33 template <typename Callback> IterateLocalHeaps(Callback callback)34 void IterateLocalHeaps(Callback callback) { 35 AssertActive(); 36 for (LocalHeap* current = local_heaps_head_; current; 37 current = current->next_) { 38 callback(current); 39 } 40 } 41 AssertActive()42 void AssertActive() { local_heaps_mutex_.AssertHeld(); } 43 44 V8_EXPORT_PRIVATE void AssertMainThreadIsOnlyThread(); 45 46 private: 47 class Barrier { 48 base::Mutex mutex_; 49 base::ConditionVariable cv_resume_; 50 base::ConditionVariable cv_stopped_; 51 bool armed_; 52 53 size_t stopped_ = 0; 54 IsArmed()55 bool IsArmed() { return armed_; } 56 57 public: Barrier()58 Barrier() : armed_(false), stopped_(0) {} 59 60 void Arm(); 61 void Disarm(); 62 void WaitUntilRunningThreadsInSafepoint(size_t running); 63 64 void WaitInSafepoint(); 65 void WaitInUnpark(); 66 void NotifyPark(); 67 }; 68 69 enum class IncludeMainThread { kYes, kNo }; 70 71 // Wait until unpark operation is safe again. 72 void WaitInUnpark(); 73 74 // Enter the safepoint from a running thread. 75 void WaitInSafepoint(); 76 77 // Running thread reached a safepoint by parking itself. 78 void NotifyPark(); 79 80 // Methods for entering/leaving local safepoint scopes. 81 void EnterLocalSafepointScope(); 82 void LeaveLocalSafepointScope(); 83 84 // Methods for entering/leaving global safepoint scopes. 85 void TryInitiateGlobalSafepointScope(Isolate* initiator, 86 PerClientSafepointData* client_data); 87 void InitiateGlobalSafepointScope(Isolate* initiator, 88 PerClientSafepointData* client_data); 89 void InitiateGlobalSafepointScopeRaw(Isolate* initiator, 90 PerClientSafepointData* client_data); 91 void LeaveGlobalSafepointScope(Isolate* initiator); 92 93 // Blocks until all running threads reached a safepoint. 94 void WaitUntilRunningThreadsInSafepoint( 95 const PerClientSafepointData* client_data); 96 97 IncludeMainThread ShouldIncludeMainThread(Isolate* initiator); 98 99 void LockMutex(LocalHeap* local_heap); 100 101 size_t SetSafepointRequestedFlags(IncludeMainThread include_main_thread); 102 void ClearSafepointRequestedFlags(IncludeMainThread include_main_thread); 103 104 template <typename Callback> AddLocalHeap(LocalHeap * local_heap,Callback callback)105 void AddLocalHeap(LocalHeap* local_heap, Callback callback) { 106 // Safepoint holds this lock in order to stop threads from starting or 107 // stopping. 108 base::RecursiveMutexGuard guard(&local_heaps_mutex_); 109 110 // Additional code protected from safepoint 111 callback(); 112 113 // Add list to doubly-linked list 114 if (local_heaps_head_) local_heaps_head_->prev_ = local_heap; 115 local_heap->prev_ = nullptr; 116 local_heap->next_ = local_heaps_head_; 117 local_heaps_head_ = local_heap; 118 } 119 120 template <typename Callback> RemoveLocalHeap(LocalHeap * local_heap,Callback callback)121 void RemoveLocalHeap(LocalHeap* local_heap, Callback callback) { 122 base::RecursiveMutexGuard guard(&local_heaps_mutex_); 123 124 // Additional code protected from safepoint 125 callback(); 126 127 // Remove list from doubly-linked list 128 if (local_heap->next_) local_heap->next_->prev_ = local_heap->prev_; 129 if (local_heap->prev_) 130 local_heap->prev_->next_ = local_heap->next_; 131 else 132 local_heaps_head_ = local_heap->next_; 133 } 134 135 Isolate* isolate() const; 136 Isolate* shared_isolate() const; 137 138 Barrier barrier_; 139 Heap* heap_; 140 141 // Mutex is used both for safepointing and adding/removing threads. A 142 // RecursiveMutex is needed since we need to support nested SafepointScopes. 143 base::RecursiveMutex local_heaps_mutex_; 144 LocalHeap* local_heaps_head_; 145 146 int active_safepoint_scopes_; 147 148 friend class GlobalSafepoint; 149 friend class GlobalSafepointScope; 150 friend class LocalHeap; 151 friend class SafepointScope; 152 }; 153 154 class V8_NODISCARD SafepointScope { 155 public: 156 V8_EXPORT_PRIVATE explicit SafepointScope(Heap* heap); 157 V8_EXPORT_PRIVATE ~SafepointScope(); 158 159 private: 160 IsolateSafepoint* safepoint_; 161 }; 162 163 // Used for reaching a global safepoint, a safepoint across all client isolates 164 // of the shared isolate. 165 class GlobalSafepoint final { 166 public: 167 explicit GlobalSafepoint(Isolate* isolate); 168 169 void AppendClient(Isolate* client); 170 void RemoveClient(Isolate* client); 171 172 template <typename Callback> IterateClientIsolates(Callback callback)173 void IterateClientIsolates(Callback callback) { 174 for (Isolate* current = clients_head_; current; 175 current = current->global_safepoint_next_client_isolate_) { 176 callback(current); 177 } 178 } 179 180 void AssertNoClients(); 181 AssertActive()182 void AssertActive() { clients_mutex_.AssertHeld(); } 183 184 private: 185 void EnterGlobalSafepointScope(Isolate* initiator); 186 void LeaveGlobalSafepointScope(Isolate* initiator); 187 188 Isolate* const shared_isolate_; 189 Heap* const shared_heap_; 190 base::Mutex clients_mutex_; 191 Isolate* clients_head_ = nullptr; 192 193 friend class GlobalSafepointScope; 194 friend class Isolate; 195 }; 196 197 class V8_NODISCARD GlobalSafepointScope { 198 public: 199 V8_EXPORT_PRIVATE explicit GlobalSafepointScope(Isolate* initiator); 200 V8_EXPORT_PRIVATE ~GlobalSafepointScope(); 201 202 private: 203 Isolate* const initiator_; 204 Isolate* const shared_isolate_; 205 }; 206 207 } // namespace internal 208 } // namespace v8 209 210 #endif // V8_HEAP_SAFEPOINT_H_ 211