diff --git a/BUILD.gn b/BUILD.gn index d53368324a..7246e43c7b 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -2339,6 +2339,8 @@ v8_source_set("v8_base_without_compiler") { "src/handles/local-handles.h", "src/handles/maybe-handles-inl.h", "src/handles/maybe-handles.h", + "src/handles/persistent-handles.cc", + "src/handles/persistent-handles.h", "src/heap/array-buffer-collector.cc", "src/heap/array-buffer-collector.h", "src/heap/array-buffer-sweeper.cc", diff --git a/src/DEPS b/src/DEPS index 772ad53b32..81c687d692 100644 --- a/src/DEPS +++ b/src/DEPS @@ -21,6 +21,7 @@ include_rules = [ "+src/heap/off-thread-factory.h", "+src/heap/read-only-heap-inl.h", "+src/heap/read-only-heap.h", + "+src/heap/safepoint.h", "-src/inspector", "-src/interpreter", "+src/interpreter/bytecode-array-accessor.h", diff --git a/src/execution/isolate.cc b/src/execution/isolate.cc index a27173eef7..e666a8633f 100644 --- a/src/execution/isolate.cc +++ b/src/execution/isolate.cc @@ -42,6 +42,7 @@ #include "src/execution/simulator.h" #include "src/execution/v8threads.h" #include "src/execution/vm-state-inl.h" +#include "src/handles/persistent-handles.h" #include "src/heap/heap-inl.h" #include "src/heap/read-only-heap.h" #include "src/ic/stub-cache.h" @@ -2857,6 +2858,7 @@ Isolate::Isolate(std::unique_ptr isolate_allocator) builtins_(this), rail_mode_(PERFORMANCE_ANIMATION), code_event_dispatcher_(new CodeEventDispatcher()), + persistent_handles_list_(new PersistentHandlesList(this)), jitless_(FLAG_jitless), #if V8_SFI_HAS_UNIQUE_ID next_unique_sfi_id_(0), @@ -3654,6 +3656,10 @@ void Isolate::UnlinkDeferredHandles(DeferredHandles* deferred) { } } +std::unique_ptr Isolate::NewPersistentHandles() { + return std::make_unique(this); +} + void Isolate::DumpAndResetStats() { if (turbo_statistics() != nullptr) { DCHECK(FLAG_turbo_stats || FLAG_turbo_stats_nvp); diff --git a/src/execution/isolate.h b/src/execution/isolate.h index 037e6ea7f5..b0643244e5 100644 --- a/src/execution/isolate.h +++ b/src/execution/isolate.h @@ -85,6 +85,8 @@ class MaterializedObjectStore; class Microtask; class MicrotaskQueue; class OptimizingCompileDispatcher; +class PersistentHandles; +class PersistentHandlesList; class ReadOnlyDeserializer; class RegExpStack; class RootVisitor; @@ -1200,6 +1202,12 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { void LinkDeferredHandles(DeferredHandles* deferred_handles); void UnlinkDeferredHandles(DeferredHandles* deferred_handles); + std::unique_ptr NewPersistentHandles(); + + PersistentHandlesList* persistent_handles_list() { + return persistent_handles_list_.get(); + } + #ifdef DEBUG bool IsDeferredHandle(Address* location); #endif // DEBUG @@ -1764,6 +1772,8 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { DeferredHandles* deferred_handles_head_ = nullptr; OptimizingCompileDispatcher* optimizing_compile_dispatcher_ = nullptr; + std::unique_ptr persistent_handles_list_; + // Counts deopt points if deopt_every_n_times is enabled. unsigned int stress_deopt_count_ = 0; diff --git a/src/handles/persistent-handles.cc b/src/handles/persistent-handles.cc new file mode 100644 index 0000000000..3ef2dee6f1 --- /dev/null +++ b/src/handles/persistent-handles.cc @@ -0,0 +1,122 @@ +// 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/handles/persistent-handles.h" + +#include "src/api/api.h" +#include "src/heap/heap-inl.h" +#include "src/heap/safepoint.h" +#include "src/utils/allocation.h" + +namespace v8 { +namespace internal { + +PersistentHandles::PersistentHandles(Isolate* isolate, size_t block_size) + : isolate_(isolate), + block_size_(block_size), + block_next_(nullptr), + block_limit_(nullptr), + prev_(nullptr), + next_(nullptr) { + isolate->persistent_handles_list()->Add(this); +} + +PersistentHandles::~PersistentHandles() { + isolate_->persistent_handles_list()->Remove(this); + + for (Address* block_start : blocks_) { + DeleteArray(block_start); + } +} + +#ifdef DEBUG +void PersistentHandles::Attach(LocalHeap* local_heap) { + DCHECK_NULL(owner_); + owner_ = local_heap; +} + +void PersistentHandles::Detach() { + DCHECK_NOT_NULL(owner_); + owner_ = nullptr; +} +#endif + +void PersistentHandles::AddBlock() { + DCHECK_EQ(block_next_, block_limit_); + + Address* block_start = NewArray
(block_size_); + blocks_.push_back(block_start); + + block_next_ = block_start; + block_limit_ = block_start + block_size_; +} + +Handle PersistentHandles::NewHandle(Address value) { +#ifdef DEBUG + if (owner_) DCHECK(!owner_->IsParked()); +#endif + return Handle(GetHandle(value)); +} + +Address* PersistentHandles::GetHandle(Address value) { + if (block_next_ == block_limit_) { + AddBlock(); + } + + DCHECK_LT(block_next_, block_limit_); + *block_next_ = value; + return block_next_++; +} + +void PersistentHandles::Iterate(RootVisitor* visitor) { + for (int i = 0; i < static_cast(blocks_.size()) - 1; i++) { + Address* block_start = blocks_[i]; + Address* block_end = block_start + block_size_; + visitor->VisitRootPointers(Root::kHandleScope, nullptr, + FullObjectSlot(block_start), + FullObjectSlot(block_end)); + } + + if (!blocks_.empty()) { + Address* block_start = blocks_.back(); + visitor->VisitRootPointers(Root::kHandleScope, nullptr, + FullObjectSlot(block_start), + FullObjectSlot(block_next_)); + } +} + +void PersistentHandlesList::Add(PersistentHandles* persistent_handles) { + base::MutexGuard guard(&persistent_handles_mutex_); + if (persistent_handles_head_) + persistent_handles_head_->prev_ = persistent_handles; + persistent_handles->prev_ = nullptr; + persistent_handles->next_ = persistent_handles_head_; + persistent_handles_head_ = persistent_handles; +} + +void PersistentHandlesList::Remove(PersistentHandles* persistent_handles) { + base::MutexGuard guard(&persistent_handles_mutex_); + if (persistent_handles->next_) + persistent_handles->next_->prev_ = persistent_handles->prev_; + if (persistent_handles->prev_) + persistent_handles->prev_->next_ = persistent_handles->next_; + else + persistent_handles_head_ = persistent_handles->next_; +} + +void PersistentHandlesList::Iterate(RootVisitor* visitor) { +#if DEBUG + DCHECK(isolate_->heap()->safepoint()->IsActive()); +#else + USE(isolate_); +#endif + base::MutexGuard guard(&persistent_handles_mutex_); + for (PersistentHandles* current = persistent_handles_head_; current; + current = current->next_) { + current->Iterate(visitor); + } +} + +} // namespace internal +} // namespace v8 diff --git a/src/handles/persistent-handles.h b/src/handles/persistent-handles.h new file mode 100644 index 0000000000..1e27bb2d01 --- /dev/null +++ b/src/handles/persistent-handles.h @@ -0,0 +1,85 @@ +// 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_HANDLES_PERSISTENT_HANDLES_H_ +#define V8_HANDLES_PERSISTENT_HANDLES_H_ + +#include + +#include "include/v8-internal.h" +#include "src/api/api.h" +#include "src/base/macros.h" +#include "src/objects/visitors.h" + +namespace v8 { +namespace internal { + +class Heap; + +class PersistentHandles { + public: + V8_EXPORT_PRIVATE explicit PersistentHandles( + Isolate* isolate, size_t block_size = kHandleBlockSize); + V8_EXPORT_PRIVATE ~PersistentHandles(); + + PersistentHandles(const PersistentHandles&) = delete; + PersistentHandles& operator=(const PersistentHandles&) = delete; + + void Iterate(RootVisitor* visitor); + + V8_EXPORT_PRIVATE Handle NewHandle(Address value); + + private: + void AddBlock(); + Address* GetHandle(Address value); + +#ifdef DEBUG + void Attach(LocalHeap* local_heap); + void Detach(); + + LocalHeap* owner_ = nullptr; + +#else + void Attach(LocalHeap*) {} + void Detach() {} +#endif + + Isolate* isolate_; + std::vector blocks_; + size_t block_size_; + + Address* block_next_; + Address* block_limit_; + + PersistentHandles* prev_; + PersistentHandles* next_; + + friend class PersistentHandlesList; + friend class LocalHeap; +}; + +class PersistentHandlesList { + public: + explicit PersistentHandlesList(Isolate* isolate) + : isolate_(isolate), persistent_handles_head_(nullptr) {} + + // Iteration is only safe during a safepoint + void Iterate(RootVisitor* visitor); + + private: + void Add(PersistentHandles* persistent_handles); + void Remove(PersistentHandles* persistent_handles); + + Isolate* isolate_; + + base::Mutex persistent_handles_mutex_; + PersistentHandles* persistent_handles_head_ = nullptr; + + friend class PersistentHandles; +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_HANDLES_PERSISTENT_HANDLES_H_ diff --git a/src/heap/heap.cc b/src/heap/heap.cc index 518bbcf162..b00ee0da4b 100644 --- a/src/heap/heap.cc +++ b/src/heap/heap.cc @@ -4473,6 +4473,8 @@ void Heap::IterateStrongRoots(RootVisitor* v, VisitMode mode) { if (FLAG_local_heaps) { safepoint_->Iterate(&left_trim_visitor); safepoint_->Iterate(v); + isolate_->persistent_handles_list()->Iterate(&left_trim_visitor); + isolate_->persistent_handles_list()->Iterate(v); } isolate_->IterateDeferredHandles(&left_trim_visitor); @@ -5376,7 +5378,12 @@ void Heap::StartTearDown() { // a good time to run heap verification (if requested), before starting to // tear down parts of the Isolate. if (FLAG_verify_heap) { - Verify(); + if (FLAG_local_heaps) { + SafepointScope scope(this); + Verify(); + } else { + Verify(); + } } #endif } diff --git a/src/heap/incremental-marking.cc b/src/heap/incremental-marking.cc index 76fdbc80c8..0d81ffdb63 100644 --- a/src/heap/incremental-marking.cc +++ b/src/heap/incremental-marking.cc @@ -19,6 +19,7 @@ #include "src/heap/object-stats.h" #include "src/heap/objects-visiting-inl.h" #include "src/heap/objects-visiting.h" +#include "src/heap/safepoint.h" #include "src/heap/sweeper.h" #include "src/init/v8.h" #include "src/numbers/conversions.h" @@ -406,8 +407,10 @@ void IncrementalMarking::MarkRoots() { DCHECK(!finalize_marking_completed_); DCHECK(IsMarking()); + if (FLAG_local_heaps) heap_->safepoint()->Start(); IncrementalMarkingRootMarkingVisitor visitor(this); heap_->IterateStrongRoots(&visitor, VISIT_ONLY_STRONG_IGNORE_STACK); + if (FLAG_local_heaps) heap_->safepoint()->End(); } bool IncrementalMarking::ShouldRetainMap(Map map, int age) { diff --git a/src/heap/local-heap.cc b/src/heap/local-heap.cc index 392b343236..575019f87e 100644 --- a/src/heap/local-heap.cc +++ b/src/heap/local-heap.cc @@ -3,21 +3,30 @@ // found in the LICENSE file. #include "src/heap/local-heap.h" + +#include + +#include "src/base/platform/mutex.h" #include "src/handles/local-handles.h" -#include "src/heap/heap.h" +#include "src/heap/heap-inl.h" #include "src/heap/safepoint.h" namespace v8 { namespace internal { -LocalHeap::LocalHeap(Heap* heap) +LocalHeap::LocalHeap(Heap* heap, + std::unique_ptr persistent_handles) : heap_(heap), state_(ThreadState::Running), safepoint_requested_(false), prev_(nullptr), next_(nullptr), - handles_(new LocalHandles) { + handles_(new LocalHandles), + persistent_handles_(std::move(persistent_handles)) { heap_->safepoint()->AddLocalHeap(this); + if (persistent_handles_) { + persistent_handles_->Attach(this); + } } LocalHeap::~LocalHeap() { @@ -27,6 +36,24 @@ LocalHeap::~LocalHeap() { heap_->safepoint()->RemoveLocalHeap(this); } +Handle LocalHeap::NewPersistentHandle(Address value) { + if (!persistent_handles_) { + persistent_handles_.reset( + heap_->isolate()->NewPersistentHandles().release()); + } + return persistent_handles_->NewHandle(value); +} + +std::unique_ptr LocalHeap::DetachPersistentHandles() { + if (persistent_handles_) persistent_handles_->Detach(); + return std::move(persistent_handles_); +} + +bool LocalHeap::IsParked() { + base::MutexGuard guard(&state_mutex_); + return state_ == ThreadState::Parked; +} + void LocalHeap::Park() { base::MutexGuard guard(&state_mutex_); CHECK(state_ == ThreadState::Running); diff --git a/src/heap/local-heap.h b/src/heap/local-heap.h index a6eed1d928..8255eeb325 100644 --- a/src/heap/local-heap.h +++ b/src/heap/local-heap.h @@ -10,6 +10,7 @@ #include "src/base/platform/condition-variable.h" #include "src/base/platform/mutex.h" +#include "src/execution/isolate.h" namespace v8 { namespace internal { @@ -17,10 +18,13 @@ namespace internal { class Heap; class Safepoint; class LocalHandles; +class PersistentHandles; class LocalHeap { public: - V8_EXPORT_PRIVATE explicit LocalHeap(Heap* heap); + V8_EXPORT_PRIVATE explicit LocalHeap( + Heap* heap, + std::unique_ptr persistent_handles = nullptr); V8_EXPORT_PRIVATE ~LocalHeap(); // Invoked by main thread to signal this thread that it needs to halt in a @@ -33,6 +37,12 @@ class LocalHeap { LocalHandles* handles() { return handles_.get(); } + V8_EXPORT_PRIVATE Handle NewPersistentHandle(Address value); + V8_EXPORT_PRIVATE std::unique_ptr + DetachPersistentHandles(); + + bool IsParked(); + private: enum class ThreadState { // Threads in this state need to be stopped in a safepoint. @@ -65,6 +75,7 @@ class LocalHeap { LocalHeap* next_; std::unique_ptr handles_; + std::unique_ptr persistent_handles_; friend class Heap; friend class Safepoint; diff --git a/src/heap/safepoint.cc b/src/heap/safepoint.cc index f524b30e74..a37eb36491 100644 --- a/src/heap/safepoint.cc +++ b/src/heap/safepoint.cc @@ -5,13 +5,15 @@ #include "src/heap/safepoint.h" #include "src/handles/local-handles.h" +#include "src/handles/persistent-handles.h" #include "src/heap/heap.h" #include "src/heap/local-heap.h" namespace v8 { namespace internal { -Safepoint::Safepoint(Heap* heap) : heap_(heap), local_heaps_head_(nullptr) {} +Safepoint::Safepoint(Heap* heap) + : heap_(heap), local_heaps_head_(nullptr), is_active_(false) {} void Safepoint::Start() { StopThreads(); } @@ -35,9 +37,13 @@ void Safepoint::StopThreads() { current->state_change_.Wait(¤t->state_mutex_); } } + + is_active_ = true; } void Safepoint::ResumeThreads() { + is_active_ = false; + for (LocalHeap* current = local_heaps_head_; current; current = current->next_) { current->state_mutex_.Unlock(); @@ -124,6 +130,7 @@ bool Safepoint::ContainsAnyLocalHeap() { } void Safepoint::Iterate(RootVisitor* visitor) { + DCHECK(IsActive()); for (LocalHeap* current = local_heaps_head_; current; current = current->next_) { current->handles()->Iterate(visitor); diff --git a/src/heap/safepoint.h b/src/heap/safepoint.h index 4b0036c047..06f9c75044 100644 --- a/src/heap/safepoint.h +++ b/src/heap/safepoint.h @@ -7,6 +7,8 @@ #include "src/base/platform/condition-variable.h" #include "src/base/platform/mutex.h" +#include "src/handles/persistent-handles.h" +#include "src/objects/visitors.h" namespace v8 { namespace internal { @@ -32,6 +34,8 @@ class Safepoint { void Start(); void End(); + bool IsActive() { return is_active_; } + private: class Barrier { base::Mutex mutex_; @@ -58,8 +62,11 @@ class Safepoint { base::Mutex local_heaps_mutex_; LocalHeap* local_heaps_head_; + bool is_active_; + friend class SafepointScope; friend class LocalHeap; + friend class PersistentHandles; }; class SafepointScope { diff --git a/test/cctest/BUILD.gn b/test/cctest/BUILD.gn index 89fe36f65b..3f5475b2a5 100644 --- a/test/cctest/BUILD.gn +++ b/test/cctest/BUILD.gn @@ -228,6 +228,7 @@ v8_source_set("cctest_sources") { "test-object.cc", "test-orderedhashtable.cc", "test-parsing.cc", + "test-persistent-handles.cc", "test-platform.cc", "test-profile-generator.cc", "test-random-number-generator.cc", diff --git a/test/cctest/test-persistent-handles.cc b/test/cctest/test-persistent-handles.cc new file mode 100644 index 0000000000..0bb2990d17 --- /dev/null +++ b/test/cctest/test-persistent-handles.cc @@ -0,0 +1,114 @@ +// 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 + +#include "src/api/api.h" +#include "src/base/platform/condition-variable.h" +#include "src/base/platform/mutex.h" +#include "src/base/platform/semaphore.h" +#include "src/handles/handles-inl.h" +#include "src/handles/local-handles-inl.h" +#include "src/handles/persistent-handles.h" +#include "src/heap/heap.h" +#include "src/heap/local-heap.h" +#include "src/heap/safepoint.h" +#include "src/objects/heap-number.h" +#include "test/cctest/cctest.h" +#include "test/cctest/heap/heap-utils.h" + +namespace v8 { +namespace internal { + +static constexpr int kNumHandles = kHandleBlockSize * 2 + kHandleBlockSize / 2; + +class PersistentHandlesThread final : public v8::base::Thread { + public: + PersistentHandlesThread(Heap* heap, std::vector> handles, + std::unique_ptr ph, Address object, + base::Semaphore* sema_started, + base::Semaphore* sema_gc_finished) + : v8::base::Thread(base::Thread::Options("ThreadWithLocalHeap")), + heap_(heap), + handles_(std::move(handles)), + ph_(std::move(ph)), + object_(object), + sema_started_(sema_started), + sema_gc_finished_(sema_gc_finished) {} + + void Run() override { + LocalHeap local_heap(heap_, std::move(ph_)); + LocalHandleScope scope(&local_heap); + + for (int i = 0; i < kNumHandles; i++) { + handles_.push_back( + Handle::cast(local_heap.NewPersistentHandle(object_))); + } + + sema_started_->Signal(); + + { + ParkedScope scope(&local_heap); + sema_gc_finished_->Wait(); + } + + for (Handle handle : handles_) { + CHECK_EQ(42.0, handle->value()); + } + + CHECK_EQ(handles_.size(), kNumHandles * 2); + + CHECK(!ph_); + ph_ = local_heap.DetachPersistentHandles(); + } + + Heap* heap_; + std::vector> handles_; + std::unique_ptr ph_; + Address object_; + base::Semaphore* sema_started_; + base::Semaphore* sema_gc_finished_; +}; + +TEST(CreatePersistentHandles) { + CcTest::InitializeVM(); + FLAG_local_heaps = true; + Isolate* isolate = CcTest::i_isolate(); + + Address object = kNullAddress; + std::unique_ptr ph = isolate->NewPersistentHandles(); + std::vector> handles; + + HandleScope handle_scope(isolate); + Handle number = isolate->factory()->NewHeapNumber(42.0); + + object = number->ptr(); + + for (int i = 0; i < kNumHandles; i++) { + handles.push_back(Handle::cast(ph->NewHandle(object))); + } + + base::Semaphore sema_started(0); + base::Semaphore sema_gc_finished(0); + + // pass persistent handles to background thread + std::unique_ptr thread(new PersistentHandlesThread( + isolate->heap(), std::move(handles), std::move(ph), object, &sema_started, + &sema_gc_finished)); + CHECK(thread->Start()); + + sema_started.Wait(); + + CcTest::CollectAllGarbage(); + sema_gc_finished.Signal(); + + thread->Join(); + + // get persistent handles back to main thread + ph = std::move(thread->ph_); + ph->NewHandle(number->ptr()); +} + +} // namespace internal +} // namespace v8