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

cppgc,heap: Refactor non-tracing GC handler

Untangles the non-tracing GC optimization (Scavenger) that allows for
dropping objects that are only reachable from certain API references
from EmbedderHeapTracer. Instead, allow setting it on Isolate.

This allows for using the optimization when using cppgc.

Chromium-side: https://crrev.com/c/2844587

Bug: chromium:1056170
Change-Id: I20f28dd84c808872c7f9559c8c168e828794dd1d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2844657Reviewed-by: 's avatarOmer Katz <omerkatz@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74135}
parent 302ed166
...@@ -905,7 +905,7 @@ class TracedReferenceBase { ...@@ -905,7 +905,7 @@ class TracedReferenceBase {
* The exact semantics are: * The exact semantics are:
* - Tracing garbage collections use |v8::EmbedderHeapTracer| or cppgc. * - Tracing garbage collections use |v8::EmbedderHeapTracer| or cppgc.
* - Non-tracing garbage collections refer to * - Non-tracing garbage collections refer to
* |v8::EmbedderHeapTracer::IsRootForNonTracingGC()| whether the handle should * |v8::EmbedderRootsHandler::IsRoot()| whether the handle should
* be treated as root or not. * be treated as root or not.
* *
* Note that the base class cannot be instantiated itself. Choose from * Note that the base class cannot be instantiated itself. Choose from
...@@ -7932,6 +7932,45 @@ class V8_EXPORT PersistentHandleVisitor { // NOLINT ...@@ -7932,6 +7932,45 @@ class V8_EXPORT PersistentHandleVisitor { // NOLINT
*/ */
enum class MemoryPressureLevel { kNone, kModerate, kCritical }; enum class MemoryPressureLevel { kNone, kModerate, kCritical };
/**
* Handler for embedder roots on non-unified heap garbage collections.
*/
class V8_EXPORT EmbedderRootsHandler {
public:
virtual ~EmbedderRootsHandler() = default;
/**
* Returns true if the TracedGlobal handle should be considered as root for
* the currently running non-tracing garbage collection and false otherwise.
* The default implementation will keep all TracedGlobal references as roots.
*
* If this returns false, then V8 may decide that the object referred to by
* such a handle is reclaimed. In that case:
* - No action is required if handles are used with destructors, i.e., by just
* using |TracedGlobal|.
* - When run without destructors, i.e., by using |TracedReference|, V8 calls
* |ResetRoot|.
*
* Note that the |handle| is different from the handle that the embedder holds
* for retaining the object. The embedder may use |WrapperClassId()| to
* distinguish cases where it wants handles to be treated as roots from not
* being treated as roots.
*/
virtual bool IsRoot(const v8::TracedReference<v8::Value>& handle) = 0;
virtual bool IsRoot(const v8::TracedGlobal<v8::Value>& handle) = 0;
/**
* Used in combination with |IsRoot|. Called by V8 when an
* object that is backed by a handle is reclaimed by a non-tracing garbage
* collection. It is up to the embedder to reset the original handle.
*
* Note that the |handle| is different from the handle that the embedder holds
* for retaining the object. It is up to the embedder to find the original
* handle via the object or class id.
*/
virtual void ResetRoot(const v8::TracedReference<v8::Value>& handle) = 0;
};
/** /**
* Interface for tracing through the embedder heap. During a V8 garbage * Interface for tracing through the embedder heap. During a V8 garbage
* collection, V8 collects hidden fields of all potential wrappers, and at the * collection, V8 collects hidden fields of all potential wrappers, and at the
...@@ -8057,34 +8096,14 @@ class V8_EXPORT EmbedderHeapTracer { ...@@ -8057,34 +8096,14 @@ class V8_EXPORT EmbedderHeapTracer {
void FinalizeTracing(); void FinalizeTracing();
/** /**
* Returns true if the TracedGlobal handle should be considered as root for * See documentation on EmbedderRootsHandler.
* the currently running non-tracing garbage collection and false otherwise.
* The default implementation will keep all TracedGlobal references as roots.
*
* If this returns false, then V8 may decide that the object referred to by
* such a handle is reclaimed. In that case:
* - No action is required if handles are used with destructors, i.e., by just
* using |TracedGlobal|.
* - When run without destructors, i.e., by using
* |TracedReference|, V8 calls |ResetHandleInNonTracingGC|.
*
* Note that the |handle| is different from the handle that the embedder holds
* for retaining the object. The embedder may use |WrapperClassId()| to
* distinguish cases where it wants handles to be treated as roots from not
* being treated as roots.
*/ */
virtual bool IsRootForNonTracingGC( virtual bool IsRootForNonTracingGC(
const v8::TracedReference<v8::Value>& handle); const v8::TracedReference<v8::Value>& handle);
virtual bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle); virtual bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle);
/** /**
* Used in combination with |IsRootForNonTracingGC|. Called by V8 when an * See documentation on EmbedderRootsHandler.
* object that is backed by a handle is reclaimed by a non-tracing garbage
* collection. It is up to the embedder to reset the original handle.
*
* Note that the |handle| is different from the handle that the embedder holds
* for retaining the object. It is up to the embedder to find the original
* handle via the object or class id.
*/ */
virtual void ResetHandleInNonTracingGC( virtual void ResetHandleInNonTracingGC(
const v8::TracedReference<v8::Value>& handle); const v8::TracedReference<v8::Value>& handle);
...@@ -8934,6 +8953,18 @@ class V8_EXPORT Isolate { ...@@ -8934,6 +8953,18 @@ class V8_EXPORT Isolate {
*/ */
EmbedderHeapTracer* GetEmbedderHeapTracer(); EmbedderHeapTracer* GetEmbedderHeapTracer();
/**
* Sets an embedder roots handle that V8 should consider when performing
* non-unified heap garbage collections.
*
* Using only EmbedderHeapTracer automatically sets up a default handler.
* The intended use case is for setting a custom handler after invoking
* `AttachCppHeap()`.
*
* V8 does not take ownership of the handler.
*/
void SetEmbedderRootsHandler(EmbedderRootsHandler* handler);
/** /**
* Attaches a managed C++ heap as an extension to the JavaScript heap. The * Attaches a managed C++ heap as an extension to the JavaScript heap. The
* embedder maintains ownership of the CppHeap. At most one C++ heap can be * embedder maintains ownership of the CppHeap. At most one C++ heap can be
......
...@@ -8136,6 +8136,11 @@ EmbedderHeapTracer* Isolate::GetEmbedderHeapTracer() { ...@@ -8136,6 +8136,11 @@ EmbedderHeapTracer* Isolate::GetEmbedderHeapTracer() {
return isolate->heap()->GetEmbedderHeapTracer(); return isolate->heap()->GetEmbedderHeapTracer();
} }
void Isolate::SetEmbedderRootsHandler(EmbedderRootsHandler* handler) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
isolate->heap()->SetEmbedderRootsHandler(handler);
}
void Isolate::AttachCppHeap(CppHeap* cpp_heap) { void Isolate::AttachCppHeap(CppHeap* cpp_heap) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this); i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
isolate->heap()->AttachCppHeap(cpp_heap); isolate->heap()->AttachCppHeap(cpp_heap);
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <cstdint> #include <cstdint>
#include <map> #include <map>
#include "include/v8.h"
#include "src/api/api-inl.h" #include "src/api/api-inl.h"
#include "src/base/compiler-specific.h" #include "src/base/compiler-specific.h"
#include "src/execution/vm-state-inl.h" #include "src/execution/vm-state-inl.h"
...@@ -1252,18 +1253,21 @@ void GlobalHandles::IdentifyWeakUnmodifiedObjects( ...@@ -1252,18 +1253,21 @@ void GlobalHandles::IdentifyWeakUnmodifiedObjects(
WeakSlotCallback is_unmodified) { WeakSlotCallback is_unmodified) {
if (!FLAG_reclaim_unmodified_wrappers) return; if (!FLAG_reclaim_unmodified_wrappers) return;
LocalEmbedderHeapTracer* const tracer = // Treat all objects as roots during incremental marking to avoid corrupting
isolate()->heap()->local_embedder_heap_tracer(); // marking worklists.
if (isolate()->heap()->incremental_marking()->IsMarking()) return;
auto* const handler = isolate()->heap()->GetEmbedderRootsHandler();
for (TracedNode* node : traced_young_nodes_) { for (TracedNode* node : traced_young_nodes_) {
if (node->IsInUse()) { if (node->IsInUse()) {
DCHECK(node->is_root()); DCHECK(node->is_root());
if (is_unmodified(node->location())) { if (is_unmodified(node->location())) {
v8::Value* value = ToApi<v8::Value>(node->handle()); v8::Value* value = ToApi<v8::Value>(node->handle());
if (node->has_destructor()) { if (node->has_destructor()) {
node->set_root(tracer->IsRootForNonTracingGC( node->set_root(handler->IsRoot(
*reinterpret_cast<v8::TracedGlobal<v8::Value>*>(&value))); *reinterpret_cast<v8::TracedGlobal<v8::Value>*>(&value)));
} else { } else {
node->set_root(tracer->IsRootForNonTracingGC( node->set_root(handler->IsRoot(
*reinterpret_cast<v8::TracedReference<v8::Value>*>(&value))); *reinterpret_cast<v8::TracedReference<v8::Value>*>(&value)));
} }
} }
...@@ -1337,8 +1341,7 @@ void GlobalHandles::IterateYoungWeakObjectsForPhantomHandles( ...@@ -1337,8 +1341,7 @@ void GlobalHandles::IterateYoungWeakObjectsForPhantomHandles(
if (!FLAG_reclaim_unmodified_wrappers) return; if (!FLAG_reclaim_unmodified_wrappers) return;
LocalEmbedderHeapTracer* const tracer = auto* const handler = isolate()->heap()->GetEmbedderRootsHandler();
isolate()->heap()->local_embedder_heap_tracer();
for (TracedNode* node : traced_young_nodes_) { for (TracedNode* node : traced_young_nodes_) {
if (!node->IsInUse()) continue; if (!node->IsInUse()) continue;
...@@ -1353,7 +1356,7 @@ void GlobalHandles::IterateYoungWeakObjectsForPhantomHandles( ...@@ -1353,7 +1356,7 @@ void GlobalHandles::IterateYoungWeakObjectsForPhantomHandles(
node->ResetPhantomHandle(HandleHolder::kLive); node->ResetPhantomHandle(HandleHolder::kLive);
} else { } else {
v8::Value* value = ToApi<v8::Value>(node->handle()); v8::Value* value = ToApi<v8::Value>(node->handle());
tracer->ResetHandleInNonTracingGC( handler->ResetRoot(
*reinterpret_cast<v8::TracedReference<v8::Value>*>(&value)); *reinterpret_cast<v8::TracedReference<v8::Value>*>(&value));
DCHECK(!node->IsInUse()); DCHECK(!node->IsInUse());
} }
......
...@@ -17,6 +17,7 @@ void LocalEmbedderHeapTracer::SetRemoteTracer(EmbedderHeapTracer* tracer) { ...@@ -17,6 +17,7 @@ void LocalEmbedderHeapTracer::SetRemoteTracer(EmbedderHeapTracer* tracer) {
if (remote_tracer_) remote_tracer_->isolate_ = nullptr; if (remote_tracer_) remote_tracer_->isolate_ = nullptr;
remote_tracer_ = tracer; remote_tracer_ = tracer;
default_embedder_roots_handler_.SetTracer(tracer);
if (remote_tracer_) if (remote_tracer_)
remote_tracer_->isolate_ = reinterpret_cast<v8::Isolate*>(isolate_); remote_tracer_->isolate_ = reinterpret_cast<v8::Isolate*>(isolate_);
} }
...@@ -164,5 +165,23 @@ void LocalEmbedderHeapTracer::StartIncrementalMarkingIfNeeded() { ...@@ -164,5 +165,23 @@ void LocalEmbedderHeapTracer::StartIncrementalMarkingIfNeeded() {
} }
} }
bool DefaultEmbedderRootsHandler::IsRoot(
const v8::TracedReference<v8::Value>& handle) {
return !tracer_ || tracer_->IsRootForNonTracingGC(handle);
}
bool DefaultEmbedderRootsHandler::IsRoot(
const v8::TracedGlobal<v8::Value>& handle) {
return !tracer_ || tracer_->IsRootForNonTracingGC(handle);
}
void DefaultEmbedderRootsHandler::ResetRoot(
const v8::TracedReference<v8::Value>& handle) {
// Resetting is only called when IsRoot() returns false which
// can only happen the EmbedderHeapTracer is set on API level.
DCHECK(tracer_);
tracer_->ResetHandleInNonTracingGC(handle);
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -16,6 +16,19 @@ namespace internal { ...@@ -16,6 +16,19 @@ namespace internal {
class Heap; class Heap;
class JSObject; class JSObject;
class V8_EXPORT_PRIVATE DefaultEmbedderRootsHandler final
: public EmbedderRootsHandler {
public:
bool IsRoot(const v8::TracedReference<v8::Value>& handle) final;
bool IsRoot(const v8::TracedGlobal<v8::Value>& handle) final;
void ResetRoot(const v8::TracedReference<v8::Value>& handle) final;
void SetTracer(EmbedderHeapTracer* tracer) { tracer_ = tracer; }
private:
EmbedderHeapTracer* tracer_ = nullptr;
};
class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final { class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
public: public:
using WrapperInfo = std::pair<void*, void*>; using WrapperInfo = std::pair<void*, void*>;
...@@ -74,21 +87,6 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final { ...@@ -74,21 +87,6 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
bool Trace(double deadline); bool Trace(double deadline);
bool IsRemoteTracingDone(); bool IsRemoteTracingDone();
bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle) {
return !InUse() || remote_tracer_->IsRootForNonTracingGC(handle);
}
bool IsRootForNonTracingGC(const v8::TracedReference<v8::Value>& handle) {
return !InUse() || remote_tracer_->IsRootForNonTracingGC(handle);
}
void ResetHandleInNonTracingGC(const v8::TracedReference<v8::Value>& handle) {
// Resetting is only called when IsRootForNonTracingGC returns false which
// can only happen the EmbedderHeapTracer is set on API level.
DCHECK(InUse());
remote_tracer_->ResetHandleInNonTracingGC(handle);
}
bool ShouldFinalizeIncrementalMarking() { bool ShouldFinalizeIncrementalMarking() {
return !FLAG_incremental_marking_wrappers || !InUse() || return !FLAG_incremental_marking_wrappers || !InUse() ||
(IsRemoteTracingDone() && embedder_worklist_empty_); (IsRemoteTracingDone() && embedder_worklist_empty_);
...@@ -130,6 +128,10 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final { ...@@ -130,6 +128,10 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
void UpdateRemoteStats(size_t, double); void UpdateRemoteStats(size_t, double);
DefaultEmbedderRootsHandler& default_embedder_roots_handler() {
return default_embedder_roots_handler_;
}
private: private:
static constexpr size_t kEmbedderAllocatedThreshold = 128 * KB; static constexpr size_t kEmbedderAllocatedThreshold = 128 * KB;
...@@ -147,6 +149,7 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final { ...@@ -147,6 +149,7 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
Isolate* const isolate_; Isolate* const isolate_;
EmbedderHeapTracer* remote_tracer_ = nullptr; EmbedderHeapTracer* remote_tracer_ = nullptr;
DefaultEmbedderRootsHandler default_embedder_roots_handler_;
EmbedderHeapTracer::EmbedderStackState embedder_stack_state_ = EmbedderHeapTracer::EmbedderStackState embedder_stack_state_ =
EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers; EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers;
......
...@@ -5396,6 +5396,8 @@ void Heap::SetUpSpaces() { ...@@ -5396,6 +5396,8 @@ void Heap::SetUpSpaces() {
dead_object_stats_.reset(new ObjectStats(this)); dead_object_stats_.reset(new ObjectStats(this));
} }
local_embedder_heap_tracer_.reset(new LocalEmbedderHeapTracer(isolate())); local_embedder_heap_tracer_.reset(new LocalEmbedderHeapTracer(isolate()));
embedder_roots_handler_ =
&local_embedder_heap_tracer()->default_embedder_roots_handler();
LOG(isolate_, IntPtrTEvent("heap-capacity", Capacity())); LOG(isolate_, IntPtrTEvent("heap-capacity", Capacity()));
LOG(isolate_, IntPtrTEvent("heap-available", Available())); LOG(isolate_, IntPtrTEvent("heap-available", Available()));
...@@ -5534,6 +5536,14 @@ void Heap::SetEmbedderHeapTracer(EmbedderHeapTracer* tracer) { ...@@ -5534,6 +5536,14 @@ void Heap::SetEmbedderHeapTracer(EmbedderHeapTracer* tracer) {
local_embedder_heap_tracer()->SetRemoteTracer(tracer); local_embedder_heap_tracer()->SetRemoteTracer(tracer);
} }
void Heap::SetEmbedderRootsHandler(EmbedderRootsHandler* handler) {
embedder_roots_handler_ = handler;
}
EmbedderRootsHandler* Heap::GetEmbedderRootsHandler() const {
return embedder_roots_handler_;
}
EmbedderHeapTracer* Heap::GetEmbedderHeapTracer() const { EmbedderHeapTracer* Heap::GetEmbedderHeapTracer() const {
return local_embedder_heap_tracer()->remote_tracer(); return local_embedder_heap_tracer()->remote_tracer();
} }
...@@ -5674,6 +5684,8 @@ void Heap::TearDown() { ...@@ -5674,6 +5684,8 @@ void Heap::TearDown() {
dead_object_stats_.reset(); dead_object_stats_.reset();
local_embedder_heap_tracer_.reset(); local_embedder_heap_tracer_.reset();
embedder_roots_handler_ = nullptr;
if (cpp_heap_) { if (cpp_heap_) {
CppHeap::From(cpp_heap_)->DetachIsolate(); CppHeap::From(cpp_heap_)->DetachIsolate();
cpp_heap_ = nullptr; cpp_heap_ = nullptr;
......
...@@ -1154,6 +1154,14 @@ class Heap { ...@@ -1154,6 +1154,14 @@ class Heap {
v8::CppHeap* cpp_heap() const { return cpp_heap_; } v8::CppHeap* cpp_heap() const { return cpp_heap_; }
// ===========================================================================
// Embedder roots optimizations. =============================================
// ===========================================================================
V8_EXPORT_PRIVATE void SetEmbedderRootsHandler(EmbedderRootsHandler* handler);
EmbedderRootsHandler* GetEmbedderRootsHandler() const;
// =========================================================================== // ===========================================================================
// External string table API. ================================================ // External string table API. ================================================
// =========================================================================== // ===========================================================================
...@@ -2261,6 +2269,8 @@ class Heap { ...@@ -2261,6 +2269,8 @@ class Heap {
// The embedder owns the C++ heap. // The embedder owns the C++ heap.
v8::CppHeap* cpp_heap_ = nullptr; v8::CppHeap* cpp_heap_ = nullptr;
EmbedderRootsHandler* embedder_roots_handler_ = nullptr;
StrongRootsEntry* strong_roots_head_ = nullptr; StrongRootsEntry* strong_roots_head_ = nullptr;
base::Mutex strong_roots_mutex_; base::Mutex strong_roots_mutex_;
......
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