[handles] Add PersistentHandlesScope
PersistentHandlesScope works similar to the DeferredHandleScope, but returns PersistentHandles instead of DeferredHandles on Detach(). Since PersistentHandlesScope takes over filled blocks from the main thread local handle, remove the block_size_ field and use kHandleBlockSize instead. This way all blocks have exactly the same size. Bug: v8:10315 Change-Id: I295cad6f84852f87c55d95572905069443f5698c Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2324254 Commit-Queue: Dominik Inführ <dinfuehr@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Cr-Commit-Position: refs/heads/master@{#69138}
This commit is contained in:
parent
7bf85afc02
commit
3c6d9aac45
@ -49,6 +49,7 @@
|
||||
#include "src/execution/v8threads.h"
|
||||
#include "src/execution/vm-state-inl.h"
|
||||
#include "src/handles/global-handles.h"
|
||||
#include "src/handles/persistent-handles.h"
|
||||
#include "src/heap/embedder-tracing.h"
|
||||
#include "src/heap/heap-inl.h"
|
||||
#include "src/init/bootstrapper.h"
|
||||
@ -11151,13 +11152,14 @@ std::unique_ptr<DeferredHandles> HandleScopeImplementer::Detach(
|
||||
Address* prev_limit) {
|
||||
std::unique_ptr<DeferredHandles> deferred(
|
||||
new DeferredHandles(isolate()->handle_scope_data()->next, isolate()));
|
||||
DCHECK_NOT_NULL(prev_limit);
|
||||
|
||||
while (!blocks_.empty()) {
|
||||
Address* block_start = blocks_.back();
|
||||
Address* block_limit = &block_start[kHandleBlockSize];
|
||||
// We should not need to check for SealHandleScope here. Assert this.
|
||||
DCHECK(prev_limit == block_limit ||
|
||||
!(block_start <= prev_limit && prev_limit <= block_limit));
|
||||
DCHECK_IMPLIES(block_start <= prev_limit && prev_limit <= block_limit,
|
||||
prev_limit == block_limit);
|
||||
if (prev_limit == block_limit) break;
|
||||
deferred->blocks_.push_back(blocks_.back());
|
||||
blocks_.pop_back();
|
||||
@ -11166,15 +11168,47 @@ std::unique_ptr<DeferredHandles> HandleScopeImplementer::Detach(
|
||||
// deferred->blocks_ now contains the blocks installed on the
|
||||
// HandleScope stack since BeginDeferredScope was called, but in
|
||||
// reverse order.
|
||||
DCHECK(!blocks_.empty() && !deferred->blocks_.empty());
|
||||
|
||||
DCHECK(prev_limit == nullptr || !blocks_.empty());
|
||||
|
||||
DCHECK(!blocks_.empty() && prev_limit != nullptr);
|
||||
DCHECK_NOT_NULL(last_handle_before_deferred_block_);
|
||||
last_handle_before_deferred_block_ = nullptr;
|
||||
return deferred;
|
||||
}
|
||||
|
||||
std::unique_ptr<PersistentHandles> HandleScopeImplementer::DetachPersistent(
|
||||
Address* prev_limit) {
|
||||
std::unique_ptr<PersistentHandles> ph(new PersistentHandles(isolate()));
|
||||
DCHECK_NOT_NULL(prev_limit);
|
||||
|
||||
while (!blocks_.empty()) {
|
||||
Address* block_start = blocks_.back();
|
||||
Address* block_limit = &block_start[kHandleBlockSize];
|
||||
// We should not need to check for SealHandleScope here. Assert this.
|
||||
DCHECK_IMPLIES(block_start <= prev_limit && prev_limit <= block_limit,
|
||||
prev_limit == block_limit);
|
||||
if (prev_limit == block_limit) break;
|
||||
ph->blocks_.push_back(blocks_.back());
|
||||
blocks_.pop_back();
|
||||
}
|
||||
|
||||
// ph->blocks_ now contains the blocks installed on the
|
||||
// HandleScope stack since BeginDeferredScope was called, but in
|
||||
// reverse order.
|
||||
|
||||
// Switch first and last blocks, such that the last block is the one
|
||||
// that is potentially half full.
|
||||
DCHECK(!blocks_.empty() && !ph->blocks_.empty());
|
||||
std::swap(ph->blocks_.front(), ph->blocks_.back());
|
||||
|
||||
ph->block_next_ = isolate()->handle_scope_data()->next;
|
||||
Address* block_start = ph->blocks_.back();
|
||||
ph->block_limit_ = block_start + kHandleBlockSize;
|
||||
|
||||
DCHECK_NOT_NULL(last_handle_before_deferred_block_);
|
||||
last_handle_before_deferred_block_ = nullptr;
|
||||
return ph;
|
||||
}
|
||||
|
||||
void HandleScopeImplementer::BeginDeferredScope() {
|
||||
DCHECK_NULL(last_handle_before_deferred_block_);
|
||||
last_handle_before_deferred_block_ = isolate()->handle_scope_data()->next;
|
||||
|
@ -302,6 +302,8 @@ inline bool ToLocal(v8::internal::MaybeHandle<v8::internal::Object> maybe,
|
||||
|
||||
namespace internal {
|
||||
|
||||
class PersistentHandles;
|
||||
|
||||
class V8_EXPORT_PRIVATE DeferredHandles {
|
||||
public:
|
||||
~DeferredHandles();
|
||||
@ -432,6 +434,7 @@ class HandleScopeImplementer {
|
||||
|
||||
void BeginDeferredScope();
|
||||
std::unique_ptr<DeferredHandles> Detach(Address* prev_limit);
|
||||
std::unique_ptr<PersistentHandles> DetachPersistent(Address* prev_limit);
|
||||
|
||||
Isolate* isolate_;
|
||||
DetachableVector<Address*> blocks_;
|
||||
@ -458,6 +461,7 @@ class HandleScopeImplementer {
|
||||
friend class DeferredHandles;
|
||||
friend class DeferredHandleScope;
|
||||
friend class HandleScopeImplementerOffsets;
|
||||
friend class PersistentHandlesScope;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(HandleScopeImplementer);
|
||||
};
|
||||
|
@ -178,7 +178,7 @@ DeferredHandleScope::DeferredHandleScope(Isolate* isolate)
|
||||
// Check that at least one HandleScope with at least one Handle in it exists,
|
||||
// see the class description.
|
||||
DCHECK(!impl_->blocks()->empty());
|
||||
// Check that we are not in a SealedHandleScope.
|
||||
// Check that we are not in a SealHandleScope.
|
||||
DCHECK(data->limit == &impl_->blocks()->back()[kHandleBlockSize]);
|
||||
impl_->blocks()->push_back(new_next);
|
||||
|
||||
|
@ -12,9 +12,8 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
PersistentHandles::PersistentHandles(Isolate* isolate, size_t block_size)
|
||||
PersistentHandles::PersistentHandles(Isolate* isolate)
|
||||
: isolate_(isolate),
|
||||
block_size_(block_size),
|
||||
block_next_(nullptr),
|
||||
block_limit_(nullptr),
|
||||
prev_(nullptr),
|
||||
@ -27,7 +26,7 @@ PersistentHandles::~PersistentHandles() {
|
||||
|
||||
for (Address* block_start : blocks_) {
|
||||
#if ENABLE_HANDLE_ZAPPING
|
||||
HandleScope::ZapRange(block_start, block_start + block_size_);
|
||||
HandleScope::ZapRange(block_start, block_start + kHandleBlockSize);
|
||||
#endif
|
||||
DeleteArray(block_start);
|
||||
}
|
||||
@ -58,18 +57,18 @@ bool PersistentHandles::Contains(Address* location) {
|
||||
// less than block_size_ handles.
|
||||
return location < block_next_;
|
||||
}
|
||||
return location < *it + block_size_;
|
||||
return location < *it + kHandleBlockSize;
|
||||
}
|
||||
#endif
|
||||
|
||||
void PersistentHandles::AddBlock() {
|
||||
DCHECK_EQ(block_next_, block_limit_);
|
||||
|
||||
Address* block_start = NewArray<Address>(block_size_);
|
||||
Address* block_start = NewArray<Address>(kHandleBlockSize);
|
||||
blocks_.push_back(block_start);
|
||||
|
||||
block_next_ = block_start;
|
||||
block_limit_ = block_start + block_size_;
|
||||
block_limit_ = block_start + kHandleBlockSize;
|
||||
|
||||
#ifdef DEBUG
|
||||
ordered_blocks_.insert(block_start);
|
||||
@ -89,7 +88,7 @@ Address* PersistentHandles::GetHandle(Address value) {
|
||||
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_;
|
||||
Address* block_end = block_start + kHandleBlockSize;
|
||||
visitor->VisitRootPointers(Root::kHandleScope, nullptr,
|
||||
FullObjectSlot(block_start),
|
||||
FullObjectSlot(block_end));
|
||||
@ -135,5 +134,45 @@ void PersistentHandlesList::Iterate(RootVisitor* visitor) {
|
||||
}
|
||||
}
|
||||
|
||||
PersistentHandlesScope::PersistentHandlesScope(Isolate* isolate)
|
||||
: impl_(isolate->handle_scope_implementer()) {
|
||||
impl_->BeginDeferredScope();
|
||||
HandleScopeData* data = impl_->isolate()->handle_scope_data();
|
||||
Address* new_next = impl_->GetSpareOrNewBlock();
|
||||
Address* new_limit = &new_next[kHandleBlockSize];
|
||||
// Check that at least one HandleScope with at least one Handle in it exists,
|
||||
// see the class description.
|
||||
DCHECK(!impl_->blocks()->empty());
|
||||
// Check that we are not in a SealHandleScope.
|
||||
DCHECK(data->limit == &impl_->blocks()->back()[kHandleBlockSize]);
|
||||
impl_->blocks()->push_back(new_next);
|
||||
|
||||
#ifdef DEBUG
|
||||
prev_level_ = data->level;
|
||||
#endif
|
||||
data->level++;
|
||||
prev_limit_ = data->limit;
|
||||
prev_next_ = data->next;
|
||||
data->next = new_next;
|
||||
data->limit = new_limit;
|
||||
}
|
||||
|
||||
PersistentHandlesScope::~PersistentHandlesScope() {
|
||||
DCHECK(handles_detached_);
|
||||
impl_->isolate()->handle_scope_data()->level--;
|
||||
DCHECK_EQ(impl_->isolate()->handle_scope_data()->level, prev_level_);
|
||||
}
|
||||
|
||||
std::unique_ptr<PersistentHandles> PersistentHandlesScope::Detach() {
|
||||
std::unique_ptr<PersistentHandles> ph = impl_->DetachPersistent(prev_limit_);
|
||||
HandleScopeData* data = impl_->isolate()->handle_scope_data();
|
||||
data->next = prev_next_;
|
||||
data->limit = prev_limit_;
|
||||
#ifdef DEBUG
|
||||
handles_detached_ = true;
|
||||
#endif
|
||||
return ph;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "src/api/api.h"
|
||||
#include "src/base/macros.h"
|
||||
#include "src/objects/visitors.h"
|
||||
#include "testing/gtest/include/gtest/gtest_prod.h" // nogncheck
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -22,14 +23,13 @@ class Heap;
|
||||
// thread-safe and the isolate tracks all PersistentHandles containers.
|
||||
class PersistentHandles {
|
||||
public:
|
||||
V8_EXPORT_PRIVATE explicit PersistentHandles(
|
||||
Isolate* isolate, size_t block_size = kHandleBlockSize);
|
||||
V8_EXPORT_PRIVATE explicit PersistentHandles(Isolate* isolate);
|
||||
V8_EXPORT_PRIVATE ~PersistentHandles();
|
||||
|
||||
PersistentHandles(const PersistentHandles&) = delete;
|
||||
PersistentHandles& operator=(const PersistentHandles&) = delete;
|
||||
|
||||
void Iterate(RootVisitor* visitor);
|
||||
V8_EXPORT_PRIVATE void Iterate(RootVisitor* visitor);
|
||||
|
||||
template <typename T>
|
||||
Handle<T> NewHandle(T obj) {
|
||||
@ -66,7 +66,6 @@ class PersistentHandles {
|
||||
|
||||
Isolate* isolate_;
|
||||
std::vector<Address*> blocks_;
|
||||
const size_t block_size_;
|
||||
|
||||
Address* block_next_;
|
||||
Address* block_limit_;
|
||||
@ -78,8 +77,11 @@ class PersistentHandles {
|
||||
std::set<Address*> ordered_blocks_;
|
||||
#endif
|
||||
|
||||
friend class PersistentHandlesList;
|
||||
friend class HandleScopeImplementer;
|
||||
friend class LocalHeap;
|
||||
friend class PersistentHandlesList;
|
||||
|
||||
FRIEND_TEST(PersistentHandlesTest, OrderOfBlocks);
|
||||
};
|
||||
|
||||
class PersistentHandlesList {
|
||||
@ -102,6 +104,27 @@ class PersistentHandlesList {
|
||||
friend class PersistentHandles;
|
||||
};
|
||||
|
||||
// PersistentHandlesScope sets up a scope in which all created main thread
|
||||
// handles become persistent handles that can be sent to another thread.
|
||||
class PersistentHandlesScope {
|
||||
public:
|
||||
V8_EXPORT_PRIVATE explicit PersistentHandlesScope(Isolate* isolate);
|
||||
V8_EXPORT_PRIVATE ~PersistentHandlesScope();
|
||||
|
||||
// Moves all blocks of this scope into PersistentHandles and returns it.
|
||||
V8_EXPORT_PRIVATE std::unique_ptr<PersistentHandles> Detach();
|
||||
|
||||
private:
|
||||
Address* prev_limit_;
|
||||
Address* prev_next_;
|
||||
HandleScopeImplementer* const impl_;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool handles_detached_ = false;
|
||||
int prev_level_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
|
@ -251,6 +251,7 @@ v8_source_set("unittests_sources") {
|
||||
"heap/memory-reducer-unittest.cc",
|
||||
"heap/object-stats-unittest.cc",
|
||||
"heap/off-thread-factory-unittest.cc",
|
||||
"heap/persistent-handles-unittest.cc",
|
||||
"heap/safepoint-unittest.cc",
|
||||
"heap/slot-set-unittest.cc",
|
||||
"heap/spaces-unittest.cc",
|
||||
|
107
test/unittests/heap/persistent-handles-unittest.cc
Normal file
107
test/unittests/heap/persistent-handles-unittest.cc
Normal file
@ -0,0 +1,107 @@
|
||||
// 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 "test/unittests/test-utils.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
using PersistentHandlesTest = TestWithIsolate;
|
||||
|
||||
TEST_F(PersistentHandlesTest, OrderOfBlocks) {
|
||||
Isolate* isolate = i_isolate();
|
||||
Heap* heap = isolate->heap();
|
||||
HandleScope scope(isolate);
|
||||
handle(ReadOnlyRoots(heap).empty_string(), isolate);
|
||||
HandleScopeData* data = isolate->handle_scope_data();
|
||||
|
||||
Address* next;
|
||||
Address* limit;
|
||||
Handle<String> first_empty, last_empty;
|
||||
std::unique_ptr<PersistentHandles> ph;
|
||||
|
||||
{
|
||||
PersistentHandlesScope persistent_scope(isolate);
|
||||
|
||||
// fill block
|
||||
first_empty = handle(ReadOnlyRoots(heap).empty_string(), isolate);
|
||||
|
||||
while (data->next < data->limit) {
|
||||
handle(ReadOnlyRoots(heap).empty_string(), isolate);
|
||||
}
|
||||
|
||||
// add second block and two more handles on it
|
||||
handle(ReadOnlyRoots(heap).empty_string(), isolate);
|
||||
last_empty = handle(ReadOnlyRoots(heap).empty_string(), isolate);
|
||||
|
||||
// remember next and limit in second block
|
||||
next = data->next;
|
||||
limit = data->limit;
|
||||
|
||||
ph = persistent_scope.Detach();
|
||||
}
|
||||
|
||||
CHECK_EQ(ph->block_next_, next);
|
||||
CHECK_EQ(ph->block_limit_, limit);
|
||||
|
||||
CHECK_EQ(first_empty->length(), 0);
|
||||
CHECK_EQ(last_empty->length(), 0);
|
||||
CHECK_EQ(*first_empty, *last_empty);
|
||||
}
|
||||
|
||||
namespace {
|
||||
class CounterDummyVisitor : public RootVisitor {
|
||||
public:
|
||||
void VisitRootPointers(Root root, const char* description,
|
||||
FullObjectSlot start, FullObjectSlot end) override {
|
||||
counter += end - start;
|
||||
}
|
||||
size_t counter = 0;
|
||||
};
|
||||
|
||||
size_t count_handles(Isolate* isolate) {
|
||||
CounterDummyVisitor visitor;
|
||||
isolate->handle_scope_implementer()->Iterate(&visitor);
|
||||
return visitor.counter;
|
||||
}
|
||||
|
||||
size_t count_handles(PersistentHandles* ph) {
|
||||
CounterDummyVisitor visitor;
|
||||
ph->Iterate(&visitor);
|
||||
return visitor.counter;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_F(PersistentHandlesTest, Iterate) {
|
||||
Isolate* isolate = i_isolate();
|
||||
Heap* heap = isolate->heap();
|
||||
v8::HandleScope scope(reinterpret_cast<v8::Isolate*>(isolate));
|
||||
HandleScopeData* data = isolate->handle_scope_data();
|
||||
|
||||
size_t handles_in_empty_scope = count_handles(isolate);
|
||||
|
||||
Handle<Object> init(ReadOnlyRoots(heap).empty_string(), isolate);
|
||||
Address* old_limit = data->limit;
|
||||
CHECK_EQ(count_handles(isolate), handles_in_empty_scope + 1);
|
||||
|
||||
std::unique_ptr<PersistentHandles> ph;
|
||||
|
||||
{
|
||||
PersistentHandlesScope persistent_scope(isolate);
|
||||
handle(ReadOnlyRoots(heap).empty_string(), isolate);
|
||||
CHECK_NE(old_limit, data->limit);
|
||||
CHECK_EQ(count_handles(isolate), handles_in_empty_scope + 2);
|
||||
ph = persistent_scope.Detach();
|
||||
}
|
||||
|
||||
ph->NewHandle(ReadOnlyRoots(heap).empty_string());
|
||||
CHECK_EQ(count_handles(ph.get()), 2);
|
||||
CHECK_EQ(count_handles(isolate), handles_in_empty_scope + 1);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
Loading…
Reference in New Issue
Block a user