| // 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. |
| |
| #include "src/heap/safepoint.h" |
| |
| #include "src/handles/local-handles.h" |
| #include "src/handles/persistent-handles.h" |
| #include "src/heap/gc-tracer.h" |
| #include "src/heap/heap-inl.h" |
| #include "src/heap/local-heap.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| GlobalSafepoint::GlobalSafepoint(Heap* heap) |
| : heap_(heap), local_heaps_head_(nullptr), active_safepoint_scopes_(0) {} |
| |
| void GlobalSafepoint::EnterSafepointScope() { |
| if (!FLAG_local_heaps) return; |
| |
| if (++active_safepoint_scopes_ > 1) return; |
| |
| TimedHistogramScope timer(heap_->isolate()->counters()->stop_the_world()); |
| TRACE_GC(heap_->tracer(), GCTracer::Scope::STOP_THE_WORLD); |
| |
| local_heaps_mutex_.Lock(); |
| local_heap_of_this_thread_ = LocalHeap::Current(); |
| |
| barrier_.Arm(); |
| |
| for (LocalHeap* current = local_heaps_head_; current; |
| current = current->next_) { |
| if (current == local_heap_of_this_thread_) { |
| continue; |
| } |
| current->RequestSafepoint(); |
| } |
| |
| for (LocalHeap* current = local_heaps_head_; current; |
| current = current->next_) { |
| if (current == local_heap_of_this_thread_) { |
| DCHECK(current->is_main_thread()); |
| continue; |
| } |
| DCHECK(!current->is_main_thread()); |
| current->state_mutex_.Lock(); |
| |
| while (current->state_ == LocalHeap::ThreadState::Running) { |
| current->state_change_.Wait(¤t->state_mutex_); |
| } |
| } |
| } |
| |
| void GlobalSafepoint::LeaveSafepointScope() { |
| if (!FLAG_local_heaps) return; |
| |
| DCHECK_GT(active_safepoint_scopes_, 0); |
| if (--active_safepoint_scopes_ > 0) return; |
| |
| DCHECK_EQ(local_heap_of_this_thread_, LocalHeap::Current()); |
| |
| for (LocalHeap* current = local_heaps_head_; current; |
| current = current->next_) { |
| if (current == local_heap_of_this_thread_) { |
| continue; |
| } |
| current->state_mutex_.Unlock(); |
| } |
| |
| barrier_.Disarm(); |
| |
| local_heap_of_this_thread_ = nullptr; |
| local_heaps_mutex_.Unlock(); |
| } |
| |
| void GlobalSafepoint::EnterFromThread(LocalHeap* local_heap) { |
| { |
| base::MutexGuard guard(&local_heap->state_mutex_); |
| DCHECK_EQ(local_heap->state_, LocalHeap::ThreadState::Running); |
| local_heap->state_ = LocalHeap::ThreadState::Safepoint; |
| local_heap->state_change_.NotifyAll(); |
| } |
| |
| barrier_.Wait(); |
| |
| { |
| base::MutexGuard guard(&local_heap->state_mutex_); |
| local_heap->state_ = LocalHeap::ThreadState::Running; |
| } |
| } |
| |
| void GlobalSafepoint::Barrier::Arm() { |
| base::MutexGuard guard(&mutex_); |
| CHECK(!armed_); |
| armed_ = true; |
| } |
| |
| void GlobalSafepoint::Barrier::Disarm() { |
| base::MutexGuard guard(&mutex_); |
| CHECK(armed_); |
| armed_ = false; |
| cond_.NotifyAll(); |
| } |
| |
| void GlobalSafepoint::Barrier::Wait() { |
| base::MutexGuard guard(&mutex_); |
| while (armed_) { |
| cond_.Wait(&mutex_); |
| } |
| } |
| |
| SafepointScope::SafepointScope(Heap* heap) : safepoint_(heap->safepoint()) { |
| safepoint_->EnterSafepointScope(); |
| } |
| |
| SafepointScope::~SafepointScope() { safepoint_->LeaveSafepointScope(); } |
| |
| bool GlobalSafepoint::ContainsLocalHeap(LocalHeap* local_heap) { |
| base::MutexGuard guard(&local_heaps_mutex_); |
| LocalHeap* current = local_heaps_head_; |
| |
| while (current) { |
| if (current == local_heap) return true; |
| current = current->next_; |
| } |
| |
| return false; |
| } |
| |
| bool GlobalSafepoint::ContainsAnyLocalHeap() { |
| base::MutexGuard guard(&local_heaps_mutex_); |
| return local_heaps_head_ != nullptr; |
| } |
| |
| void GlobalSafepoint::Iterate(RootVisitor* visitor) { |
| DCHECK(IsActive()); |
| for (LocalHeap* current = local_heaps_head_; current; |
| current = current->next_) { |
| current->handles()->Iterate(visitor); |
| } |
| } |
| |
| } // namespace internal |
| } // namespace v8 |