%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/heap/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/heap/local-heap.cc |
// 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/local-heap.h" #include <atomic> #include <memory> #include "src/base/logging.h" #include "src/base/optional.h" #include "src/base/platform/mutex.h" #include "src/common/globals.h" #include "src/execution/isolate.h" #include "src/handles/local-handles.h" #include "src/heap/collection-barrier.h" #include "src/heap/concurrent-allocator.h" #include "src/heap/gc-tracer-inl.h" #include "src/heap/gc-tracer.h" #include "src/heap/heap-inl.h" #include "src/heap/heap-write-barrier.h" #include "src/heap/heap.h" #include "src/heap/local-heap-inl.h" #include "src/heap/marking-barrier.h" #include "src/heap/parked-scope.h" #include "src/heap/safepoint.h" namespace v8 { namespace internal { namespace { thread_local LocalHeap* current_local_heap = nullptr; } // namespace LocalHeap* LocalHeap::Current() { return current_local_heap; } #ifdef DEBUG void LocalHeap::VerifyCurrent() const { LocalHeap* current = LocalHeap::Current(); if (is_main_thread()) DCHECK_NULL(current); else DCHECK_EQ(current, this); } #endif LocalHeap::LocalHeap(Heap* heap, ThreadKind kind, std::unique_ptr<PersistentHandles> persistent_handles) : heap_(heap), ptr_compr_cage_access_scope_(heap->isolate()), is_main_thread_(kind == ThreadKind::kMain), state_(ThreadState::Parked()), allocation_failed_(false), main_thread_parked_(false), prev_(nullptr), next_(nullptr), handles_(new LocalHandles), persistent_handles_(std::move(persistent_handles)) { DCHECK_IMPLIES(!is_main_thread(), heap_->deserialization_complete()); if (!is_main_thread()) SetUp(); heap_->safepoint()->AddLocalHeap(this, [this] { if (!is_main_thread()) { saved_marking_barrier_ = WriteBarrier::SetForThread(marking_barrier_.get()); if (heap_->incremental_marking()->IsMarking()) { marking_barrier_->Activate( heap_->incremental_marking()->IsCompacting(), heap_->incremental_marking()->marking_mode()); } SetUpSharedMarking(); } }); if (persistent_handles_) { persistent_handles_->Attach(this); } DCHECK_NULL(current_local_heap); if (!is_main_thread()) current_local_heap = this; } LocalHeap::~LocalHeap() { // Park thread since removing the local heap could block. EnsureParkedBeforeDestruction(); heap_->safepoint()->RemoveLocalHeap(this, [this] { FreeLinearAllocationArea(); FreeSharedLinearAllocationArea(); if (!is_main_thread()) { marking_barrier_->PublishIfNeeded(); marking_barrier_->PublishSharedIfNeeded(); MarkingBarrier* overwritten = WriteBarrier::SetForThread(saved_marking_barrier_); DCHECK_EQ(overwritten, marking_barrier_.get()); USE(overwritten); } }); if (!is_main_thread()) { DCHECK_EQ(current_local_heap, this); current_local_heap = nullptr; } DCHECK(gc_epilogue_callbacks_.IsEmpty()); } void LocalHeap::SetUpMainThreadForTesting() { Unpark(); SetUpMainThread(); } void LocalHeap::SetUpMainThread() { DCHECK(is_main_thread()); DCHECK(IsRunning()); SetUp(); SetUpSharedMarking(); } void LocalHeap::SetUp() { DCHECK_NULL(old_space_allocator_); old_space_allocator_ = std::make_unique<ConcurrentAllocator>( this, heap_->old_space(), ConcurrentAllocator::Context::kNotGC); DCHECK_NULL(code_space_allocator_); code_space_allocator_ = std::make_unique<ConcurrentAllocator>( this, heap_->code_space(), ConcurrentAllocator::Context::kNotGC); DCHECK_NULL(shared_old_space_allocator_); if (heap_->isolate()->has_shared_space()) { shared_old_space_allocator_ = std::make_unique<ConcurrentAllocator>( this, heap_->shared_allocation_space(), ConcurrentAllocator::Context::kNotGC); } trusted_space_allocator_ = std::make_unique<ConcurrentAllocator>( this, heap_->trusted_space(), ConcurrentAllocator::Context::kNotGC); DCHECK_NULL(marking_barrier_); marking_barrier_ = std::make_unique<MarkingBarrier>(this); } void LocalHeap::SetUpSharedMarking() { #if DEBUG // Ensure the thread is either in the running state or holds the safepoint // lock. This guarantees that the state of incremental marking can't change // concurrently (this requires a safepoint). if (is_main_thread()) { DCHECK(IsRunning()); } else { heap()->safepoint()->AssertActive(); } #endif // DEBUG Isolate* isolate = heap_->isolate(); if (isolate->has_shared_space() && !isolate->is_shared_space_isolate()) { if (isolate->shared_space_isolate() ->heap() ->incremental_marking() ->IsMajorMarking()) { marking_barrier_->ActivateShared(); } } } void LocalHeap::EnsurePersistentHandles() { if (!persistent_handles_) { persistent_handles_.reset( heap_->isolate()->NewPersistentHandles().release()); persistent_handles_->Attach(this); } } void LocalHeap::AttachPersistentHandles( std::unique_ptr<PersistentHandles> persistent_handles) { DCHECK_NULL(persistent_handles_); persistent_handles_ = std::move(persistent_handles); persistent_handles_->Attach(this); } std::unique_ptr<PersistentHandles> LocalHeap::DetachPersistentHandles() { if (persistent_handles_) persistent_handles_->Detach(); return std::move(persistent_handles_); } #ifdef DEBUG bool LocalHeap::ContainsPersistentHandle(Address* location) { return persistent_handles_ ? persistent_handles_->Contains(location) : false; } bool LocalHeap::ContainsLocalHandle(Address* location) { return handles_ ? handles_->Contains(location) : false; } bool LocalHeap::IsHandleDereferenceAllowed() { VerifyCurrent(); return IsRunning(); } #endif bool LocalHeap::IsParked() const { #ifdef DEBUG VerifyCurrent(); #endif return state_.load_relaxed().IsParked(); } bool LocalHeap::IsRunning() const { #ifdef DEBUG VerifyCurrent(); #endif return state_.load_relaxed().IsRunning(); } void LocalHeap::ParkSlowPath() { while (true) { ThreadState current_state = ThreadState::Running(); if (state_.CompareExchangeStrong(current_state, ThreadState::Parked())) return; // CAS above failed, so state is Running with some additional flag. DCHECK(current_state.IsRunning()); if (is_main_thread()) { DCHECK(current_state.IsSafepointRequested() || current_state.IsCollectionRequested()); if (current_state.IsSafepointRequested()) { ThreadState old_state = state_.SetParked(); heap_->safepoint()->NotifyPark(); if (old_state.IsCollectionRequested()) heap_->collection_barrier_->CancelCollectionAndResumeThreads(); return; } if (current_state.IsCollectionRequested()) { if (!heap()->ignore_local_gc_requests()) { heap_->CollectGarbageForBackground(this); continue; } DCHECK(!current_state.IsSafepointRequested()); if (state_.CompareExchangeStrong(current_state, current_state.SetParked())) { heap_->collection_barrier_->CancelCollectionAndResumeThreads(); return; } else { continue; } } } else { DCHECK(current_state.IsSafepointRequested()); DCHECK(!current_state.IsCollectionRequested()); ThreadState old_state = state_.SetParked(); CHECK(old_state.IsRunning()); CHECK(old_state.IsSafepointRequested()); CHECK(!old_state.IsCollectionRequested()); heap_->safepoint()->NotifyPark(); return; } } } void LocalHeap::UnparkSlowPath() { while (true) { ThreadState current_state = ThreadState::Parked(); if (state_.CompareExchangeStrong(current_state, ThreadState::Running())) return; // CAS above failed, so state is Parked with some additional flag. DCHECK(current_state.IsParked()); if (is_main_thread()) { DCHECK(current_state.IsSafepointRequested() || current_state.IsCollectionRequested()); if (current_state.IsSafepointRequested()) { SleepInUnpark(); continue; } if (current_state.IsCollectionRequested()) { DCHECK(!current_state.IsSafepointRequested()); if (!state_.CompareExchangeStrong(current_state, current_state.SetRunning())) continue; if (!heap()->ignore_local_gc_requests()) { heap_->CollectGarbageForBackground(this); } return; } } else { DCHECK(current_state.IsSafepointRequested()); DCHECK(!current_state.IsCollectionRequested()); SleepInUnpark(); } } } void LocalHeap::SleepInUnpark() { GCTracer::Scope::ScopeId scope_id; ThreadKind thread_kind; if (is_main_thread()) { scope_id = GCTracer::Scope::UNPARK; thread_kind = ThreadKind::kMain; } else { scope_id = GCTracer::Scope::BACKGROUND_UNPARK; thread_kind = ThreadKind::kBackground; } TRACE_GC1(heap_->tracer(), scope_id, thread_kind); heap_->safepoint()->WaitInUnpark(); } void LocalHeap::EnsureParkedBeforeDestruction() { DCHECK_IMPLIES(!is_main_thread(), IsParked()); } void LocalHeap::SafepointSlowPath() { ThreadState current_state = state_.load_relaxed(); DCHECK(current_state.IsRunning()); if (is_main_thread()) { DCHECK(current_state.IsSafepointRequested() || current_state.IsCollectionRequested()); if (current_state.IsSafepointRequested()) { SleepInSafepoint(); } if (current_state.IsCollectionRequested()) { heap_->CollectGarbageForBackground(this); } } else { DCHECK(current_state.IsSafepointRequested()); DCHECK(!current_state.IsCollectionRequested()); SleepInSafepoint(); } } void LocalHeap::SleepInSafepoint() { GCTracer::Scope::ScopeId scope_id; ThreadKind thread_kind; if (is_main_thread()) { scope_id = GCTracer::Scope::SAFEPOINT; thread_kind = ThreadKind::kMain; } else { scope_id = GCTracer::Scope::BACKGROUND_SAFEPOINT; thread_kind = ThreadKind::kBackground; } TRACE_GC1(heap_->tracer(), scope_id, thread_kind); ExecuteWithStackMarkerIfNeeded([this]() { // Parking the running thread here is an optimization. We do not need to // wake this thread up to reach the next safepoint. ThreadState old_state = state_.SetParked(); CHECK(old_state.IsRunning()); CHECK(old_state.IsSafepointRequested()); CHECK_IMPLIES(old_state.IsCollectionRequested(), is_main_thread()); heap_->safepoint()->WaitInSafepoint(); base::Optional<IgnoreLocalGCRequests> ignore_gc_requests; if (is_main_thread()) ignore_gc_requests.emplace(heap()); Unpark(); }); } bool LocalHeap::IsMainThreadOfClientIsolate() const { return is_main_thread() && heap()->isolate()->has_shared_space(); } void LocalHeap::FreeLinearAllocationArea() { old_space_allocator_->FreeLinearAllocationArea(); code_space_allocator_->FreeLinearAllocationArea(); trusted_space_allocator_->FreeLinearAllocationArea(); } void LocalHeap::FreeSharedLinearAllocationArea() { if (shared_old_space_allocator_) { shared_old_space_allocator_->FreeLinearAllocationArea(); } } void LocalHeap::MakeLinearAllocationAreaIterable() { old_space_allocator_->MakeLinearAllocationAreaIterable(); code_space_allocator_->MakeLinearAllocationAreaIterable(); trusted_space_allocator_->MakeLinearAllocationAreaIterable(); } void LocalHeap::MakeSharedLinearAllocationAreaIterable() { if (shared_old_space_allocator_) { shared_old_space_allocator_->MakeLinearAllocationAreaIterable(); } } void LocalHeap::MarkLinearAllocationAreaBlack() { old_space_allocator_->MarkLinearAllocationAreaBlack(); code_space_allocator_->MarkLinearAllocationAreaBlack(); trusted_space_allocator_->MarkLinearAllocationAreaBlack(); } void LocalHeap::UnmarkLinearAllocationArea() { old_space_allocator_->UnmarkLinearAllocationArea(); code_space_allocator_->UnmarkLinearAllocationArea(); trusted_space_allocator_->UnmarkLinearAllocationArea(); } void LocalHeap::MarkSharedLinearAllocationAreaBlack() { if (shared_old_space_allocator_) { shared_old_space_allocator_->MarkLinearAllocationAreaBlack(); } } void LocalHeap::UnmarkSharedLinearAllocationArea() { if (shared_old_space_allocator_) { shared_old_space_allocator_->UnmarkLinearAllocationArea(); } } AllocationResult LocalHeap::PerformCollectionAndAllocateAgain( int object_size, AllocationType type, AllocationOrigin origin, AllocationAlignment alignment) { // All allocation tries in this method should have this flag enabled. CHECK(!allocation_failed_); allocation_failed_ = true; static const int kMaxNumberOfRetries = 3; int failed_allocations = 0; int parked_allocations = 0; for (int i = 0; i < kMaxNumberOfRetries; i++) { // This flag needs to be reset for each iteration. CHECK(!main_thread_parked_); if (!heap_->CollectGarbageFromAnyThread(this)) { main_thread_parked_ = true; parked_allocations++; } AllocationResult result = AllocateRaw(object_size, type, origin, alignment); main_thread_parked_ = false; if (!result.IsFailure()) { CHECK(allocation_failed_); allocation_failed_ = false; CHECK(!main_thread_parked_); return result; } failed_allocations++; } if (v8_flags.trace_gc) { heap_->isolate()->PrintWithTimestamp( "Background allocation failure: " "allocations=%d" "allocations.parked=%d", failed_allocations, parked_allocations); } CHECK(allocation_failed_); allocation_failed_ = false; CHECK(!main_thread_parked_); return AllocationResult::Failure(); } void LocalHeap::AddGCEpilogueCallback(GCEpilogueCallback* callback, void* data, GCCallbacksInSafepoint::GCType gc_type) { DCHECK(IsRunning()); gc_epilogue_callbacks_.Add(callback, data, gc_type); } void LocalHeap::RemoveGCEpilogueCallback(GCEpilogueCallback* callback, void* data) { DCHECK(IsRunning()); gc_epilogue_callbacks_.Remove(callback, data); } void LocalHeap::InvokeGCEpilogueCallbacksInSafepoint( GCCallbacksInSafepoint::GCType gc_type) { gc_epilogue_callbacks_.Invoke(gc_type); } void LocalHeap::NotifyObjectSizeChange( Tagged<HeapObject> object, int old_size, int new_size, ClearRecordedSlots clear_recorded_slots) { heap()->NotifyObjectSizeChange(object, old_size, new_size, clear_recorded_slots); } void LocalHeap::WeakenDescriptorArrays( GlobalHandleVector<DescriptorArray> strong_descriptor_arrays) { AsHeap()->WeakenDescriptorArrays(std::move(strong_descriptor_arrays)); } } // namespace internal } // namespace v8