[heap] Add PersistentHandles container

Adds the PersistentHandles class, which serves as a container for
handles that can be passed back and forth between threads. Allocation
and deallocation of this class is thread-safe and the isolate tracks
all PersistentHandles containers.

Design doc: https://docs.google.com/document/d/17yKs-6apE2rGEag7tDsoyeRxg99c1dXyXQ2MfHe65tY/edit?usp=sharing

Bug: v8:10315
Change-Id: I4b9c958c9a57d755ca68862197501f75274670fb
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2128058
Commit-Queue: Dominik Inführ <dinfuehr@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67004}
This commit is contained in:
Dominik Inführ 2020-04-06 08:23:44 +02:00 committed by Commit Bot
parent dd1dbd99a6
commit 744a1d23b2
14 changed files with 409 additions and 6 deletions

View File

@ -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",

View File

@ -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",

View File

@ -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<i::IsolateAllocator> 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<PersistentHandles> Isolate::NewPersistentHandles() {
return std::make_unique<PersistentHandles>(this);
}
void Isolate::DumpAndResetStats() {
if (turbo_statistics() != nullptr) {
DCHECK(FLAG_turbo_stats || FLAG_turbo_stats_nvp);

View File

@ -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<PersistentHandles> 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<PersistentHandlesList> persistent_handles_list_;
// Counts deopt points if deopt_every_n_times is enabled.
unsigned int stress_deopt_count_ = 0;

View File

@ -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<Address>(block_size_);
blocks_.push_back(block_start);
block_next_ = block_start;
block_limit_ = block_start + block_size_;
}
Handle<Object> PersistentHandles::NewHandle(Address value) {
#ifdef DEBUG
if (owner_) DCHECK(!owner_->IsParked());
#endif
return Handle<Object>(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<int>(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

View File

@ -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 <vector>
#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<Object> 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<Address*> 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_

View File

@ -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
}

View File

@ -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) {

View File

@ -3,21 +3,30 @@
// found in the LICENSE file.
#include "src/heap/local-heap.h"
#include <memory>
#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<PersistentHandles> 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<Object> LocalHeap::NewPersistentHandle(Address value) {
if (!persistent_handles_) {
persistent_handles_.reset(
heap_->isolate()->NewPersistentHandles().release());
}
return persistent_handles_->NewHandle(value);
}
std::unique_ptr<PersistentHandles> 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);

View File

@ -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<PersistentHandles> 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<Object> NewPersistentHandle(Address value);
V8_EXPORT_PRIVATE std::unique_ptr<PersistentHandles>
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<LocalHandles> handles_;
std::unique_ptr<PersistentHandles> persistent_handles_;
friend class Heap;
friend class Safepoint;

View File

@ -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(&current->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);

View File

@ -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 {

View File

@ -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",

View File

@ -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 <memory>
#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<Handle<HeapNumber>> handles,
std::unique_ptr<PersistentHandles> 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<HeapNumber>::cast(local_heap.NewPersistentHandle(object_)));
}
sema_started_->Signal();
{
ParkedScope scope(&local_heap);
sema_gc_finished_->Wait();
}
for (Handle<HeapNumber> handle : handles_) {
CHECK_EQ(42.0, handle->value());
}
CHECK_EQ(handles_.size(), kNumHandles * 2);
CHECK(!ph_);
ph_ = local_heap.DetachPersistentHandles();
}
Heap* heap_;
std::vector<Handle<HeapNumber>> handles_;
std::unique_ptr<PersistentHandles> 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<PersistentHandles> ph = isolate->NewPersistentHandles();
std::vector<Handle<HeapNumber>> handles;
HandleScope handle_scope(isolate);
Handle<HeapNumber> number = isolate->factory()->NewHeapNumber(42.0);
object = number->ptr();
for (int i = 0; i < kNumHandles; i++) {
handles.push_back(Handle<HeapNumber>::cast(ph->NewHandle(object)));
}
base::Semaphore sema_started(0);
base::Semaphore sema_gc_finished(0);
// pass persistent handles to background thread
std::unique_ptr<PersistentHandlesThread> 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