Commit fbcaf729 authored by Michael Lippautz's avatar Michael Lippautz Committed by Commit Bot

api, cppgc-js: Allow creating a v8::CppHeap in detached state

The detached CppHeap allows for allocation without invoking garbage
collections.  Allocated bytes are reported on the first allocation
after the CppHeap has been attached to an Isolate.

States:
- Detached: Allow only allocation;
- Attached: Unified heap GCs;
- Termination GC: Require detached state;

Destruction:
- Heap::TearDown: Detach if attached;
- ~CppHeap: Detach if attached;

Bug: chromium:1056170
Change-Id: I95ce029f36a7f10392257080b6e23e13cc0fc7b8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2672940
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarOmer Katz <omerkatz@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72579}
parent 4d07f3f2
...@@ -35,6 +35,9 @@ struct V8_EXPORT CppHeapCreateParams { ...@@ -35,6 +35,9 @@ struct V8_EXPORT CppHeapCreateParams {
*/ */
class V8_EXPORT CppHeap { class V8_EXPORT CppHeap {
public: public:
static std::unique_ptr<CppHeap> Create(v8::Platform* platform,
const CppHeapCreateParams& params);
virtual ~CppHeap() = default; virtual ~CppHeap() = default;
/** /**
......
...@@ -93,7 +93,6 @@ class Utils; ...@@ -93,7 +93,6 @@ class Utils;
class Value; class Value;
class WasmMemoryObject; class WasmMemoryObject;
class WasmModuleObject; class WasmModuleObject;
struct CppHeapCreateParams;
template <class K, class V, class T> template <class K, class V, class T>
class GlobalValueMap; class GlobalValueMap;
template <class K, class V, class T> template <class K, class V, class T>
...@@ -8469,16 +8468,6 @@ class V8_EXPORT Isolate { ...@@ -8469,16 +8468,6 @@ class V8_EXPORT Isolate {
int embedder_wrapper_type_index = -1; int embedder_wrapper_type_index = -1;
int embedder_wrapper_object_index = -1; int embedder_wrapper_object_index = -1;
/**
* If parameters are set, V8 creates a managed C++ heap as extension to its
* JavaScript heap.
*
* See v8::Isolate::GetCppHeap() for working with the heap.
*
* This is an experimental feature and may still change significantly.
*/
std::shared_ptr<CppHeapCreateParams> cpp_heap_params;
V8_DEPRECATED( V8_DEPRECATED(
"Setting this has no effect. Embedders should ignore import assertions " "Setting this has no effect. Embedders should ignore import assertions "
"that they do not use.") "that they do not use.")
...@@ -9100,8 +9089,26 @@ class V8_EXPORT Isolate { ...@@ -9100,8 +9089,26 @@ class V8_EXPORT Isolate {
EmbedderHeapTracer* GetEmbedderHeapTracer(); EmbedderHeapTracer* GetEmbedderHeapTracer();
/** /**
* \returns the C++ heap managed by V8. Only available if the Isolate was * Attaches a managed C++ heap as an extension to the JavaScript heap. The
* created with proper CreatePrams::cpp_heap_params option. * embedder maintains ownership of the CppHeap. At most one C++ heap can be
* attached to V8.
*
* This is an experimental feature and may still change significantly.
*/
void AttachCppHeap(CppHeap*);
/**
* Detaches a managed C++ heap if one was attached using `AttachCppHeap()`.
*
* This is an experimental feature and may still change significantly.
*/
void DetachCppHeap();
/**
* This is an experimental feature and may still change significantly.
* \returns the C++ heap managed by V8. Only available if such a heap has been
* attached using `AttachCppHeap()`.
*/ */
CppHeap* GetCppHeap() const; CppHeap* GetCppHeap() const;
......
...@@ -8223,6 +8223,16 @@ EmbedderHeapTracer* Isolate::GetEmbedderHeapTracer() { ...@@ -8223,6 +8223,16 @@ EmbedderHeapTracer* Isolate::GetEmbedderHeapTracer() {
return isolate->heap()->GetEmbedderHeapTracer(); return isolate->heap()->GetEmbedderHeapTracer();
} }
void Isolate::AttachCppHeap(CppHeap* cpp_heap) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
isolate->heap()->AttachCppHeap(cpp_heap);
}
void Isolate::DetachCppHeap() {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
isolate->heap()->DetachCppHeap();
}
CppHeap* Isolate::GetCppHeap() const { CppHeap* Isolate::GetCppHeap() const {
const i::Isolate* isolate = reinterpret_cast<const i::Isolate*>(this); const i::Isolate* isolate = reinterpret_cast<const i::Isolate*>(this);
return isolate->heap()->cpp_heap(); return isolate->heap()->cpp_heap();
...@@ -8335,9 +8345,6 @@ void Isolate::Initialize(Isolate* isolate, ...@@ -8335,9 +8345,6 @@ void Isolate::Initialize(Isolate* isolate,
i_isolate->set_allow_atomics_wait(params.allow_atomics_wait); i_isolate->set_allow_atomics_wait(params.allow_atomics_wait);
i_isolate->heap()->ConfigureHeap(params.constraints); i_isolate->heap()->ConfigureHeap(params.constraints);
if (params.cpp_heap_params) {
i_isolate->heap()->ConfigureCppHeap(params.cpp_heap_params);
}
if (params.constraints.stack_limit() != nullptr) { if (params.constraints.stack_limit() != nullptr) {
uintptr_t limit = uintptr_t limit =
reinterpret_cast<uintptr_t>(params.constraints.stack_limit()); reinterpret_cast<uintptr_t>(params.constraints.stack_limit());
......
...@@ -35,6 +35,12 @@ ...@@ -35,6 +35,12 @@
namespace v8 { namespace v8 {
// static
std::unique_ptr<CppHeap> CppHeap::Create(v8::Platform* platform,
const CppHeapCreateParams& params) {
return std::make_unique<internal::CppHeap>(platform, params.custom_spaces);
}
cppgc::AllocationHandle& CppHeap::GetAllocationHandle() { cppgc::AllocationHandle& CppHeap::GetAllocationHandle() {
return internal::CppHeap::From(this)->object_allocator(); return internal::CppHeap::From(this)->object_allocator();
} }
...@@ -63,8 +69,7 @@ namespace { ...@@ -63,8 +69,7 @@ namespace {
class CppgcPlatformAdapter final : public cppgc::Platform { class CppgcPlatformAdapter final : public cppgc::Platform {
public: public:
explicit CppgcPlatformAdapter(v8::Isolate* isolate) explicit CppgcPlatformAdapter(v8::Platform* platform) : platform_(platform) {}
: platform_(V8::GetCurrentPlatform()), isolate_(isolate) {}
CppgcPlatformAdapter(const CppgcPlatformAdapter&) = delete; CppgcPlatformAdapter(const CppgcPlatformAdapter&) = delete;
CppgcPlatformAdapter& operator=(const CppgcPlatformAdapter&) = delete; CppgcPlatformAdapter& operator=(const CppgcPlatformAdapter&) = delete;
...@@ -90,9 +95,11 @@ class CppgcPlatformAdapter final : public cppgc::Platform { ...@@ -90,9 +95,11 @@ class CppgcPlatformAdapter final : public cppgc::Platform {
return platform_->GetTracingController(); return platform_->GetTracingController();
} }
void SetIsolate(v8::Isolate* isolate) { isolate_ = isolate; }
private: private:
v8::Platform* platform_; v8::Platform* platform_;
v8::Isolate* isolate_; v8::Isolate* isolate_ = nullptr;
}; };
class UnifiedHeapConcurrentMarker class UnifiedHeapConcurrentMarker
...@@ -169,38 +176,67 @@ void UnifiedHeapMarker::AddObject(void* object) { ...@@ -169,38 +176,67 @@ void UnifiedHeapMarker::AddObject(void* object) {
} // namespace } // namespace
CppHeap::CppHeap( CppHeap::CppHeap(
v8::Isolate* isolate, v8::Platform* platform,
const std::vector<std::unique_ptr<cppgc::CustomSpaceBase>>& custom_spaces, const std::vector<std::unique_ptr<cppgc::CustomSpaceBase>>& custom_spaces,
std::unique_ptr<cppgc::internal::MetricRecorder> metric_recorder) std::unique_ptr<cppgc::internal::MetricRecorder> metric_recorder)
: cppgc::internal::HeapBase(std::make_shared<CppgcPlatformAdapter>(isolate), : cppgc::internal::HeapBase(
custom_spaces, std::make_shared<CppgcPlatformAdapter>(platform), custom_spaces,
cppgc::internal::HeapBase::StackSupport:: cppgc::internal::HeapBase::StackSupport::
kSupportsConservativeStackScan, kSupportsConservativeStackScan,
std::move(metric_recorder)), std::move(metric_recorder)) {
isolate_(*reinterpret_cast<Isolate*>(isolate)) { // Enter no GC scope. `AttachIsolate()` removes this and allows triggering
if (isolate_.heap_profiler()) { // garbage collections.
isolate_.heap_profiler()->AddBuildEmbedderGraphCallback( no_gc_scope_++;
&CppGraphBuilder::Run, this);
}
stats_collector()->RegisterObserver(this); stats_collector()->RegisterObserver(this);
} }
CppHeap::~CppHeap() { CppHeap::~CppHeap() {
if (isolate_.heap_profiler()) { if (isolate_) {
isolate_.heap_profiler()->RemoveBuildEmbedderGraphCallback( isolate_->heap()->DetachCppHeap();
&CppGraphBuilder::Run, this);
} }
} }
void CppHeap::Terminate() { void CppHeap::Terminate() {
FinalizeIncrementalGarbageCollectionIfNeeded( // Must not be attached to a heap when invoking termination GCs.
cppgc::Heap::StackState::kNoHeapPointers); CHECK(!isolate_);
// Any future garbage collections will ignore the V8->C++ references.
isolate()->SetEmbedderHeapTracer(nullptr);
// Gracefully terminate the C++ heap invoking destructors. // Gracefully terminate the C++ heap invoking destructors.
HeapBase::Terminate(); HeapBase::Terminate();
} }
void CppHeap::AttachIsolate(Isolate* isolate) {
CHECK_NULL(isolate_);
isolate_ = isolate;
static_cast<CppgcPlatformAdapter*>(platform())
->SetIsolate(reinterpret_cast<v8::Isolate*>(isolate_));
if (isolate_->heap_profiler()) {
isolate_->heap_profiler()->AddBuildEmbedderGraphCallback(
&CppGraphBuilder::Run, this);
}
isolate_->heap()->SetEmbedderHeapTracer(this);
no_gc_scope_--;
}
void CppHeap::DetachIsolate() {
// TODO(chromium:1056170): Investigate whether this can be enforced with a
// CHECK across all relevant embedders and setups.
if (!isolate_) return;
// Delegate to existing EmbedderHeapTracer API to finish any ongoing garbage
// collection.
FinalizeTracing();
sweeper_.FinishIfRunning();
if (isolate_->heap_profiler()) {
isolate_->heap_profiler()->RemoveBuildEmbedderGraphCallback(
&CppGraphBuilder::Run, this);
}
isolate_ = nullptr;
// Any future garbage collections will ignore the V8->C++ references.
isolate()->SetEmbedderHeapTracer(nullptr);
// Enter no GC scope.
no_gc_scope_++;
}
void CppHeap::RegisterV8References( void CppHeap::RegisterV8References(
const std::vector<std::pair<void*, void*> >& embedder_fields) { const std::vector<std::pair<void*, void*> >& embedder_fields) {
DCHECK(marker_); DCHECK(marker_);
...@@ -231,7 +267,7 @@ void CppHeap::TracePrologue(TraceFlags flags) { ...@@ -231,7 +267,7 @@ void CppHeap::TracePrologue(TraceFlags flags) {
} }
marker_ = marker_ =
cppgc::internal::MarkerFactory::CreateAndStartMarking<UnifiedHeapMarker>( cppgc::internal::MarkerFactory::CreateAndStartMarking<UnifiedHeapMarker>(
*isolate_.heap(), AsBase(), platform_.get(), marking_config); *isolate_->heap(), AsBase(), platform_.get(), marking_config);
marking_done_ = false; marking_done_ = false;
} }
......
...@@ -32,7 +32,7 @@ class V8_EXPORT_PRIVATE CppHeap final ...@@ -32,7 +32,7 @@ class V8_EXPORT_PRIVATE CppHeap final
} }
CppHeap( CppHeap(
v8::Isolate* isolate, v8::Platform* platform,
const std::vector<std::unique_ptr<cppgc::CustomSpaceBase>>& custom_spaces, const std::vector<std::unique_ptr<cppgc::CustomSpaceBase>>& custom_spaces,
std::unique_ptr<cppgc::internal::MetricRecorder> metric_recorder = std::unique_ptr<cppgc::internal::MetricRecorder> metric_recorder =
nullptr); nullptr);
...@@ -44,6 +44,9 @@ class V8_EXPORT_PRIVATE CppHeap final ...@@ -44,6 +44,9 @@ class V8_EXPORT_PRIVATE CppHeap final
HeapBase& AsBase() { return *this; } HeapBase& AsBase() { return *this; }
const HeapBase& AsBase() const { return *this; } const HeapBase& AsBase() const { return *this; }
void AttachIsolate(Isolate* isolate);
void DetachIsolate();
void Terminate(); void Terminate();
// v8::EmbedderHeapTracer interface. // v8::EmbedderHeapTracer interface.
...@@ -69,7 +72,7 @@ class V8_EXPORT_PRIVATE CppHeap final ...@@ -69,7 +72,7 @@ class V8_EXPORT_PRIVATE CppHeap final
void ReportBufferedAllocationSizeIfPossible(); void ReportBufferedAllocationSizeIfPossible();
Isolate& isolate_; Isolate* isolate_ = nullptr;
bool marking_done_ = false; bool marking_done_ = false;
// Buffered allocated bytes. Reporting allocated bytes to V8 can trigger a GC // Buffered allocated bytes. Reporting allocated bytes to V8 can trigger a GC
......
...@@ -105,7 +105,6 @@ void HeapBase::AdvanceIncrementalGarbageCollectionOnAllocationIfNeeded() { ...@@ -105,7 +105,6 @@ void HeapBase::AdvanceIncrementalGarbageCollectionOnAllocationIfNeeded() {
void HeapBase::Terminate() { void HeapBase::Terminate() {
DCHECK(!IsMarking()); DCHECK(!IsMarking());
DCHECK(!in_no_gc_scope());
CHECK(!in_disallow_gc_scope()); CHECK(!in_disallow_gc_scope());
sweeper().FinishIfRunning(); sweeper().FinishIfRunning();
......
...@@ -4732,12 +4732,6 @@ void Heap::ConfigureHeap(const v8::ResourceConstraints& constraints) { ...@@ -4732,12 +4732,6 @@ void Heap::ConfigureHeap(const v8::ResourceConstraints& constraints) {
configured_ = true; configured_ = true;
} }
void Heap::ConfigureCppHeap(std::shared_ptr<CppHeapCreateParams> params) {
cpp_heap_ = std::make_unique<CppHeap>(
reinterpret_cast<v8::Isolate*>(isolate()), params->custom_spaces);
SetEmbedderHeapTracer(CppHeap::From(cpp_heap_.get()));
}
void Heap::AddToRingBuffer(const char* string) { void Heap::AddToRingBuffer(const char* string) {
size_t first_part = size_t first_part =
std::min(strlen(string), kTraceRingBufferSize - ring_buffer_end_); std::min(strlen(string), kTraceRingBufferSize - ring_buffer_end_);
...@@ -5379,6 +5373,8 @@ void Heap::NotifyOldGenerationExpansion(AllocationSpace space, ...@@ -5379,6 +5373,8 @@ void Heap::NotifyOldGenerationExpansion(AllocationSpace space,
void Heap::SetEmbedderHeapTracer(EmbedderHeapTracer* tracer) { void Heap::SetEmbedderHeapTracer(EmbedderHeapTracer* tracer) {
DCHECK_EQ(gc_state(), HeapState::NOT_IN_GC); DCHECK_EQ(gc_state(), HeapState::NOT_IN_GC);
// Setting a tracer is only supported when CppHeap is not used.
DCHECK_IMPLIES(tracer, !cpp_heap_);
local_embedder_heap_tracer()->SetRemoteTracer(tracer); local_embedder_heap_tracer()->SetRemoteTracer(tracer);
} }
...@@ -5386,6 +5382,16 @@ EmbedderHeapTracer* Heap::GetEmbedderHeapTracer() const { ...@@ -5386,6 +5382,16 @@ EmbedderHeapTracer* Heap::GetEmbedderHeapTracer() const {
return local_embedder_heap_tracer()->remote_tracer(); return local_embedder_heap_tracer()->remote_tracer();
} }
void Heap::AttachCppHeap(v8::CppHeap* cpp_heap) {
CppHeap::From(cpp_heap)->AttachIsolate(isolate());
cpp_heap_ = cpp_heap;
}
void Heap::DetachCppHeap() {
CppHeap::From(cpp_heap_)->DetachIsolate();
cpp_heap_ = nullptr;
}
EmbedderHeapTracer::TraceFlags Heap::flags_for_embedder_tracer() const { EmbedderHeapTracer::TraceFlags Heap::flags_for_embedder_tracer() const {
if (is_current_gc_forced()) { if (is_current_gc_forced()) {
return EmbedderHeapTracer::TraceFlags::kForced; return EmbedderHeapTracer::TraceFlags::kForced;
...@@ -5512,7 +5518,10 @@ void Heap::TearDown() { ...@@ -5512,7 +5518,10 @@ void Heap::TearDown() {
dead_object_stats_.reset(); dead_object_stats_.reset();
local_embedder_heap_tracer_.reset(); local_embedder_heap_tracer_.reset();
cpp_heap_.reset(); if (cpp_heap_) {
CppHeap::From(cpp_heap_)->DetachIsolate();
cpp_heap_ = nullptr;
}
external_string_table_.TearDown(); external_string_table_.TearDown();
......
...@@ -1137,10 +1137,10 @@ class Heap { ...@@ -1137,10 +1137,10 @@ class Heap {
// Unified heap (C++) support. =============================================== // Unified heap (C++) support. ===============================================
// =========================================================================== // ===========================================================================
V8_EXPORT_PRIVATE void ConfigureCppHeap( V8_EXPORT_PRIVATE void AttachCppHeap(v8::CppHeap* cpp_heap);
std::shared_ptr<CppHeapCreateParams> params); V8_EXPORT_PRIVATE void DetachCppHeap();
v8::CppHeap* cpp_heap() const { return cpp_heap_.get(); } v8::CppHeap* cpp_heap() const { return cpp_heap_; }
// =========================================================================== // ===========================================================================
// External string table API. ================================================ // External string table API. ================================================
...@@ -2236,7 +2236,9 @@ class Heap { ...@@ -2236,7 +2236,9 @@ class Heap {
std::unique_ptr<AllocationObserver> stress_concurrent_allocation_observer_; std::unique_ptr<AllocationObserver> stress_concurrent_allocation_observer_;
std::unique_ptr<LocalEmbedderHeapTracer> local_embedder_heap_tracer_; std::unique_ptr<LocalEmbedderHeapTracer> local_embedder_heap_tracer_;
std::unique_ptr<MarkingBarrier> marking_barrier_; std::unique_ptr<MarkingBarrier> marking_barrier_;
std::unique_ptr<v8::CppHeap> cpp_heap_;
// The embedder owns the C++ heap.
v8::CppHeap* cpp_heap_ = nullptr;
StrongRootsEntry* strong_roots_head_ = nullptr; StrongRootsEntry* strong_roots_head_ = nullptr;
base::Mutex strong_roots_mutex_; base::Mutex strong_roots_mutex_;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "include/v8.h" #include "include/v8.h"
#include "src/api/api-inl.h" #include "src/api/api-inl.h"
#include "src/heap/cppgc-js/cpp-heap.h" #include "src/heap/cppgc-js/cpp-heap.h"
#include "src/heap/cppgc/sweeper.h"
#include "src/objects/objects-inl.h" #include "src/objects/objects-inl.h"
#include "test/unittests/heap/heap-utils.h" #include "test/unittests/heap/heap-utils.h"
#include "test/unittests/heap/unified-heap-utils.h" #include "test/unittests/heap/unified-heap-utils.h"
...@@ -39,6 +40,8 @@ class Wrappable final : public cppgc::GarbageCollected<Wrappable> { ...@@ -39,6 +40,8 @@ class Wrappable final : public cppgc::GarbageCollected<Wrappable> {
size_t Wrappable::destructor_callcount = 0; size_t Wrappable::destructor_callcount = 0;
using UnifiedHeapDetachedTest = TestWithHeapInternals;
} // namespace } // namespace
TEST_F(UnifiedHeapTest, OnlyGC) { CollectGarbageWithEmbedderStack(); } TEST_F(UnifiedHeapTest, OnlyGC) { CollectGarbageWithEmbedderStack(); }
...@@ -119,5 +122,29 @@ TEST_F(UnifiedHeapTest, WriteBarrierCppToV8Reference) { ...@@ -119,5 +122,29 @@ TEST_F(UnifiedHeapTest, WriteBarrierCppToV8Reference) {
wrappable->wrapper()->GetAlignedPointerFromInternalField(1)); wrappable->wrapper()->GetAlignedPointerFromInternalField(1));
} }
TEST_F(UnifiedHeapDetachedTest, AllocationBeforeConfigureHeap) {
auto heap =
v8::CppHeap::Create(V8::GetCurrentPlatform(), CppHeapCreateParams{});
auto* object =
cppgc::MakeGarbageCollected<Wrappable>(heap->GetAllocationHandle());
cppgc::WeakPersistent<Wrappable> weak_holder{object};
auto& js_heap = *isolate()->heap();
js_heap.AttachCppHeap(heap.get());
auto& cpp_heap = *CppHeap::From(isolate()->heap()->cpp_heap());
{
CollectGarbage(OLD_SPACE);
cpp_heap.AsBase().sweeper().FinishIfRunning();
EXPECT_TRUE(weak_holder);
}
{
js_heap.SetEmbedderStackStateForNextFinalization(
EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers);
CollectGarbage(OLD_SPACE);
cpp_heap.AsBase().sweeper().FinishIfRunning();
EXPECT_FALSE(weak_holder);
}
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -14,8 +14,10 @@ ...@@ -14,8 +14,10 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
UnifiedHeapTest::UnifiedHeapTest() { UnifiedHeapTest::UnifiedHeapTest()
isolate()->heap()->ConfigureCppHeap(std::make_unique<CppHeapCreateParams>()); : cpp_heap_(v8::CppHeap::Create(V8::GetCurrentPlatform(),
CppHeapCreateParams{})) {
isolate()->heap()->AttachCppHeap(cpp_heap_.get());
} }
void UnifiedHeapTest::CollectGarbageWithEmbedderStack( void UnifiedHeapTest::CollectGarbageWithEmbedderStack(
......
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
#include "test/unittests/heap/heap-utils.h" #include "test/unittests/heap/heap-utils.h"
namespace v8 { namespace v8 {
class CppHeap;
namespace internal { namespace internal {
class CppHeap; class CppHeap;
...@@ -27,6 +30,9 @@ class UnifiedHeapTest : public TestWithHeapInternals { ...@@ -27,6 +30,9 @@ class UnifiedHeapTest : public TestWithHeapInternals {
CppHeap& cpp_heap() const; CppHeap& cpp_heap() const;
cppgc::AllocationHandle& allocation_handle(); cppgc::AllocationHandle& allocation_handle();
private:
std::unique_ptr<v8::CppHeap> cpp_heap_;
}; };
class WrapperHelper { class WrapperHelper {
......
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