[heap] Introduce LocalHandleScope for background threads

Add LocalHandleScope to allow for local handles in LocalHeaps
(background threads). This class is similar to HandleScope which still
needs to be used on the main thread. When performing a GC, the main
thread halts all background threads at a safepoint such that it can
safely iterate their roots.

Bug: v8:10315
Change-Id: Id8f5d54cc2535e004081ccdef15dc03a39b2d0f0
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2111218
Commit-Queue: Dominik Inführ <dinfuehr@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66853}
This commit is contained in:
Dominik Inführ 2020-03-25 11:09:07 +01:00 committed by Commit Bot
parent 04774ffaaa
commit 12597a8ae9
16 changed files with 355 additions and 17 deletions

View File

@ -2325,6 +2325,9 @@ v8_source_set("v8_base_without_compiler") {
"src/handles/handles-inl.h",
"src/handles/handles.cc",
"src/handles/handles.h",
"src/handles/local-handles-inl.h",
"src/handles/local-handles.cc",
"src/handles/local-handles.h",
"src/handles/maybe-handles-inl.h",
"src/handles/maybe-handles.h",
"src/heap/array-buffer-collector.cc",

View File

@ -16,6 +16,7 @@ include_rules = [
"+src/heap/heap-inl.h",
"+src/heap/heap-write-barrier-inl.h",
"+src/heap/heap-write-barrier.h",
"+src/heap/local-heap.h",
"+src/heap/off-thread-factory-inl.h",
"+src/heap/off-thread-factory.h",
"+src/heap/read-only-heap-inl.h",

View File

@ -909,6 +909,7 @@ DEFINE_BOOL_READONLY(array_buffer_extension, V8_ARRAY_BUFFER_EXTENSION_BOOL,
DEFINE_IMPLICATION(array_buffer_extension, always_promote_young_mc)
DEFINE_BOOL(concurrent_array_buffer_sweeping, true,
"concurrently sweep array buffers")
DEFINE_BOOL(local_heaps, false, "allow heap access from background tasks")
DEFINE_BOOL(parallel_marking, true, "use parallel marking in atomic pause")
DEFINE_INT(ephemeron_fixpoint_iterations, 10,
"number of fixpoint iterations it takes to switch to linear "

View File

@ -8,17 +8,23 @@
#include "src/execution/isolate.h"
#include "src/execution/off-thread-isolate.h"
#include "src/handles/handles.h"
#include "src/handles/local-handles-inl.h"
#include "src/sanitizer/msan.h"
namespace v8 {
namespace internal {
class LocalHeap;
HandleBase::HandleBase(Address object, Isolate* isolate)
: location_(HandleScope::GetHandle(isolate, object)) {}
HandleBase::HandleBase(Address object, OffThreadIsolate* isolate)
: location_(isolate->NewHandle(object)) {}
HandleBase::HandleBase(Address object, LocalHeap* local_heap)
: location_(LocalHandleScope::GetHandle(local_heap, object)) {}
// Allocate a new handle for the object, do not canonicalize.
template <typename T>
@ -41,6 +47,10 @@ template <typename T>
Handle<T>::Handle(T object, OffThreadIsolate* isolate)
: HandleBase(object.ptr(), isolate) {}
template <typename T>
Handle<T>::Handle(T object, LocalHeap* local_heap)
: HandleBase(object.ptr(), local_heap) {}
template <typename T>
V8_INLINE Handle<T> handle(T object, Isolate* isolate) {
return Handle<T>(object, isolate);
@ -51,6 +61,11 @@ V8_INLINE Handle<T> handle(T object, OffThreadIsolate* isolate) {
return Handle<T>(object, isolate);
}
template <typename T>
V8_INLINE Handle<T> handle(T object, LocalHeap* local_heap) {
return Handle<T>(object, local_heap);
}
// Convenience overloads for when we already have a Handle, but want
// either a Handle or an Handle.
template <typename T>
@ -61,6 +76,10 @@ template <typename T>
V8_INLINE Handle<T> handle(Handle<T> handle, OffThreadIsolate* isolate) {
return Handle<T>(*handle);
}
template <typename T>
V8_INLINE Handle<T> handle(Handle<T> handle, LocalHeap* local_heap) {
return Handle<T>(*handle, local_heap);
}
template <typename T>
inline std::ostream& operator<<(std::ostream& os, Handle<T> handle) {

View File

@ -21,6 +21,7 @@ namespace internal {
class DeferredHandles;
class HandleScopeImplementer;
class Isolate;
class LocalHeap;
class OffThreadIsolate;
template <typename T>
class MaybeHandle;
@ -28,6 +29,7 @@ class Object;
class OrderedHashMap;
class OrderedHashSet;
class OrderedNameDictionary;
class RootVisitor;
class SmallOrderedHashMap;
class SmallOrderedHashSet;
class SmallOrderedNameDictionary;
@ -40,6 +42,7 @@ class HandleBase {
V8_INLINE explicit HandleBase(Address* location) : location_(location) {}
V8_INLINE explicit HandleBase(Address object, Isolate* isolate);
V8_INLINE explicit HandleBase(Address object, OffThreadIsolate* isolate);
V8_INLINE explicit HandleBase(Address object, LocalHeap* local_heap);
// Check if this handle refers to the exact same object as the other handle.
V8_INLINE bool is_identical_to(const HandleBase that) const {
@ -121,6 +124,7 @@ class Handle final : public HandleBase {
V8_INLINE Handle(T object, Isolate* isolate);
V8_INLINE Handle(T object, OffThreadIsolate* isolate);
V8_INLINE Handle(T object, LocalHeap* local_heap);
// Allocate a new handle for the object, do not canonicalize.
V8_INLINE static Handle<T> New(T object, Isolate* isolate);

View File

@ -0,0 +1,61 @@
// 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_LOCAL_HANDLES_INL_H_
#define V8_HANDLES_LOCAL_HANDLES_INL_H_
#include "src/handles/local-handles.h"
#include "src/sanitizer/msan.h"
namespace v8 {
namespace internal {
// static
V8_INLINE Address* LocalHandleScope::GetHandle(LocalHeap* local_heap,
Address value) {
LocalHandles* handles = local_heap->handles();
Address* result = handles->scope_.next;
if (result == handles->scope_.limit) {
result = handles->AddBlock();
}
DCHECK_LT(result, handles->scope_.limit);
handles->scope_.next++;
*result = value;
return result;
}
LocalHandleScope::LocalHandleScope(LocalHeap* local_heap) {
LocalHandles* handles = local_heap->handles();
local_heap_ = local_heap;
prev_next_ = handles->scope_.next;
prev_limit_ = handles->scope_.limit;
handles->scope_.level++;
}
LocalHandleScope::~LocalHandleScope() {
LocalHandles* handles = local_heap_->handles();
Address* old_limit = handles->scope_.limit;
handles->scope_.next = prev_next_;
handles->scope_.limit = prev_limit_;
handles->scope_.level--;
if (old_limit != handles->scope_.limit) {
handles->RemoveBlocks();
old_limit = handles->scope_.limit;
}
// TODO(dinfuehr): Zap handles
MSAN_ALLOCATED_UNINITIALIZED_MEMORY(
handles->scope_.next,
static_cast<size_t>(reinterpret_cast<Address>(old_limit) -
reinterpret_cast<Address>(handles->scope_.next)));
}
} // namespace internal
} // namespace v8
#endif // V8_HANDLES_LOCAL_HANDLES_INL_H_

View File

@ -0,0 +1,58 @@
// Copyright 2012 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/local-handles.h"
#include "src/api/api.h"
#include "src/handles/handles.h"
namespace v8 {
namespace internal {
LocalHandles::LocalHandles() { scope_.Initialize(); }
void LocalHandles::Iterate(RootVisitor* visitor) {
for (int i = 0; i < static_cast<int>(blocks_.size()) - 1; i++) {
Address* block = blocks_[i];
visitor->VisitRootPointers(Root::kHandleScope, nullptr,
FullObjectSlot(block),
FullObjectSlot(&block[kHandleBlockSize]));
}
if (!blocks_.empty()) {
Address* block = blocks_.back();
visitor->VisitRootPointers(Root::kHandleScope, nullptr,
FullObjectSlot(block),
FullObjectSlot(scope_.next));
}
}
Address* LocalHandles::AddBlock() {
DCHECK_EQ(scope_.next, scope_.limit);
Address* block = NewArray<Address>(kHandleBlockSize);
blocks_.push_back(block);
scope_.next = block;
scope_.limit = block + kHandleBlockSize;
return block;
}
void LocalHandles::RemoveBlocks() {
while (!blocks_.empty()) {
Address* block_start = blocks_.back();
Address* block_limit = block_start + kHandleBlockSize;
if (block_limit == scope_.limit) {
break;
}
blocks_.pop_back();
// TODO(dinfuehr): Zap handles in block
DeleteArray(block_start);
}
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,57 @@
// Copyright 2011 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_LOCAL_HANDLES_H_
#define V8_HANDLES_LOCAL_HANDLES_H_
#include "include/v8-internal.h"
#include "src/base/functional.h"
#include "src/base/macros.h"
#include "src/handles/handles.h"
#include "src/heap/local-heap.h"
namespace v8 {
namespace internal {
class RootVisitor;
class LocalHandles {
public:
LocalHandles();
void Iterate(RootVisitor* visitor);
private:
HandleScopeData scope_;
std::vector<Address*> blocks_;
V8_EXPORT_PRIVATE Address* AddBlock();
V8_EXPORT_PRIVATE void RemoveBlocks();
friend class LocalHandleScope;
};
class LocalHandleScope {
public:
explicit inline LocalHandleScope(LocalHeap* local_heap);
inline ~LocalHandleScope();
V8_INLINE static Address* GetHandle(LocalHeap* local_heap, Address value);
private:
// Prevent heap allocation or illegal handle scopes.
void* operator new(size_t size);
void operator delete(void* size_t);
LocalHeap* local_heap_;
Address* prev_limit_;
Address* prev_next_;
DISALLOW_COPY_AND_ASSIGN(LocalHandleScope);
};
} // namespace internal
} // namespace v8
#endif // V8_HANDLES_LOCAL_HANDLES_H_

View File

@ -822,12 +822,6 @@ void Heap::GarbageCollectionPrologue() {
{
AllowHeapAllocation for_the_first_part_of_prologue;
gc_count_++;
#ifdef VERIFY_HEAP
if (FLAG_verify_heap) {
Verify();
}
#endif
}
// Reset GC statistics.
@ -1121,12 +1115,6 @@ void Heap::GarbageCollectionEpilogue() {
ZapFromSpace();
}
#ifdef VERIFY_HEAP
if (FLAG_verify_heap) {
Verify();
}
#endif
AllowHeapAllocation for_the_rest_of_the_epilogue;
#ifdef DEBUG
@ -2022,6 +2010,13 @@ bool Heap::PerformGarbageCollection(
}
}
if (FLAG_local_heaps) safepoint()->Start();
#ifdef VERIFY_HEAP
if (FLAG_verify_heap) {
Verify();
}
#endif
EnsureFromSpaceIsCommitted();
size_t start_young_generation_size =
@ -2095,6 +2090,13 @@ bool Heap::PerformGarbageCollection(
local_embedder_heap_tracer()->TraceEpilogue();
}
#ifdef VERIFY_HEAP
if (FLAG_verify_heap) {
Verify();
}
#endif
if (FLAG_local_heaps) safepoint()->End();
{
TRACE_GC(tracer(), GCTracer::Scope::HEAP_EXTERNAL_WEAK_GLOBAL_HANDLES);
gc_post_processing_depth_++;
@ -4467,6 +4469,12 @@ void Heap::IterateStrongRoots(RootVisitor* v, VisitMode mode) {
FixStaleLeftTrimmedHandlesVisitor left_trim_visitor(this);
isolate_->handle_scope_implementer()->Iterate(&left_trim_visitor);
isolate_->handle_scope_implementer()->Iterate(v);
if (FLAG_local_heaps) {
safepoint_->Iterate(&left_trim_visitor);
safepoint_->Iterate(v);
}
isolate_->IterateDeferredHandles(&left_trim_visitor);
isolate_->IterateDeferredHandles(v);
v->Synchronize(VisitorSynchronization::kHandleScope);

View File

@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "src/heap/local-heap.h"
#include "src/handles/local-handles.h"
#include "src/heap/heap.h"
#include "src/heap/safepoint.h"
@ -14,7 +15,8 @@ LocalHeap::LocalHeap(Heap* heap)
state_(ThreadState::Running),
safepoint_requested_(false),
prev_(nullptr),
next_(nullptr) {
next_(nullptr),
handles_(new LocalHandles) {
heap_->safepoint()->AddLocalHeap(this);
}

View File

@ -6,6 +6,7 @@
#define V8_HEAP_LOCAL_HEAP_H_
#include <atomic>
#include <memory>
#include "src/base/platform/condition-variable.h"
#include "src/base/platform/mutex.h"
@ -15,6 +16,7 @@ namespace internal {
class Heap;
class Safepoint;
class LocalHandles;
class LocalHeap {
public:
@ -29,6 +31,8 @@ class LocalHeap {
// from the main thread.
V8_EXPORT_PRIVATE void Safepoint();
LocalHandles* handles() { return handles_.get(); }
private:
enum class ThreadState {
// Threads in this state need to be stopped in a safepoint.
@ -60,6 +64,8 @@ class LocalHeap {
LocalHeap* prev_;
LocalHeap* next_;
std::unique_ptr<LocalHandles> handles_;
friend class Heap;
friend class Safepoint;
friend class ParkedScope;

View File

@ -3,6 +3,8 @@
// found in the LICENSE file.
#include "src/heap/safepoint.h"
#include "src/handles/local-handles.h"
#include "src/heap/heap.h"
#include "src/heap/local-heap.h"
@ -11,6 +13,10 @@ namespace internal {
Safepoint::Safepoint(Heap* heap) : heap_(heap), local_heaps_head_(nullptr) {}
void Safepoint::Start() { StopThreads(); }
void Safepoint::End() { ResumeThreads(); }
void Safepoint::StopThreads() {
local_heaps_mutex_.Lock();
@ -117,5 +123,12 @@ bool Safepoint::ContainsAnyLocalHeap() {
return local_heaps_head_ != nullptr;
}
void Safepoint::Iterate(RootVisitor* visitor) {
for (LocalHeap* current = local_heaps_head_; current;
current = current->next_) {
current->handles()->Iterate(visitor);
}
}
} // namespace internal
} // namespace v8

View File

@ -13,6 +13,7 @@ namespace internal {
class Heap;
class LocalHeap;
class RootVisitor;
class Safepoint {
public:
@ -24,6 +25,13 @@ class Safepoint {
V8_EXPORT_PRIVATE bool ContainsLocalHeap(LocalHeap* local_heap);
V8_EXPORT_PRIVATE bool ContainsAnyLocalHeap();
// Iterate handles in local heaps
void Iterate(RootVisitor* visitor);
// Use these methods now instead of the more intrusive SafepointScope
void Start();
void End();
private:
class Barrier {
base::Mutex mutex_;

View File

@ -219,6 +219,7 @@ v8_source_set("cctest_sources") {
"test-intl.cc",
"test-js-weak-refs.cc",
"test-liveedit.cc",
"test-local-handles.cc",
"test-lockers.cc",
"test-log.cc",
"test-managed.cc",

View File

@ -0,0 +1,96 @@
// 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/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 {
class LocalHandlesThread final : public v8::base::Thread {
public:
LocalHandlesThread(Heap* heap, Address object, base::Semaphore* sema_started,
base::Semaphore* sema_gc_finished)
: v8::base::Thread(base::Thread::Options("ThreadWithLocalHeap")),
heap_(heap),
object_(object),
sema_started_(sema_started),
sema_gc_finished_(sema_gc_finished) {}
void Run() override {
LocalHeap local_heap(heap_);
LocalHandleScope scope(&local_heap);
static constexpr int kNumHandles =
kHandleBlockSize * 2 + kHandleBlockSize / 2;
std::vector<Handle<HeapNumber>> handles;
handles.reserve(kNumHandles);
for (int i = 0; i < kNumHandles; i++) {
Handle<HeapNumber> number = handle(
HeapNumber::cast(HeapObject::FromAddress(object_)), &local_heap);
handles.push_back(number);
}
sema_started_->Signal();
{
ParkedScope scope(&local_heap);
sema_gc_finished_->Wait();
}
for (Handle<HeapNumber> handle : handles) {
CHECK_EQ(42.0, handle->value());
}
}
Heap* heap_;
Address object_;
base::Semaphore* sema_started_;
base::Semaphore* sema_gc_finished_;
};
TEST(CreateLocalHandles) {
CcTest::InitializeVM();
FLAG_local_heaps = true;
Isolate* isolate = CcTest::i_isolate();
Address object = kNullAddress;
{
HandleScope handle_scope(isolate);
Handle<HeapNumber> number = isolate->factory()->NewHeapNumber(42.0);
object = (*number).address();
}
base::Semaphore sema_started(0);
base::Semaphore sema_gc_finished(0);
std::unique_ptr<LocalHandlesThread> thread(new LocalHandlesThread(
isolate->heap(), object, &sema_started, &sema_gc_finished));
CHECK(thread->Start());
sema_started.Wait();
CcTest::CollectAllGarbage();
sema_gc_finished.Signal();
thread->Join();
}
} // namespace internal
} // namespace v8

View File

@ -13,9 +13,9 @@
namespace v8 {
namespace internal {
using SafepoinTest = TestWithIsolate;
using SafepointTest = TestWithIsolate;
TEST_F(SafepoinTest, ReachSafepointWithoutLocalHeaps) {
TEST_F(SafepointTest, ReachSafepointWithoutLocalHeaps) {
Heap* heap = i_isolate()->heap();
bool run = false;
{
@ -45,7 +45,7 @@ class ParkedThread final : public v8::base::Thread {
base::Mutex* mutex_;
};
TEST_F(SafepoinTest, StopParkedThreads) {
TEST_F(SafepointTest, StopParkedThreads) {
Heap* heap = i_isolate()->heap();
int safepoints = 0;
@ -103,7 +103,7 @@ class RunningThread final : public v8::base::Thread {
std::atomic<int>* counter_;
};
TEST_F(SafepoinTest, StopRunningThreads) {
TEST_F(SafepointTest, StopRunningThreads) {
Heap* heap = i_isolate()->heap();
const int kThreads = 10;