Commit 3c6d9aac authored by Dominik Inführ's avatar Dominik Inführ Committed by Commit Bot

[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: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69138}
parent 7bf85afc
......@@ -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",
......
// 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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment