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/+/2844657
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74135}
This commit is contained in:
Michael Lippautz 2021-04-22 15:56:56 +02:00 committed by Commit Bot
parent 302ed166bc
commit f5371cef82
7 changed files with 129 additions and 46 deletions

View File

@ -905,8 +905,8 @@ class TracedReferenceBase {
* The exact semantics are:
* - Tracing garbage collections use |v8::EmbedderHeapTracer| or cppgc.
* - Non-tracing garbage collections refer to
* |v8::EmbedderHeapTracer::IsRootForNonTracingGC()| whether the handle should
* be treated as root or not.
* |v8::EmbedderRootsHandler::IsRoot()| whether the handle should
* be treated as root or not.
*
* Note that the base class cannot be instantiated itself. Choose from
* - TracedGlobal
@ -7932,6 +7932,45 @@ class V8_EXPORT PersistentHandleVisitor { // NOLINT
*/
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
* collection, V8 collects hidden fields of all potential wrappers, and at the
@ -8057,34 +8096,14 @@ class V8_EXPORT EmbedderHeapTracer {
void FinalizeTracing();
/**
* 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 |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.
* See documentation on EmbedderRootsHandler.
*/
virtual bool IsRootForNonTracingGC(
const v8::TracedReference<v8::Value>& handle);
virtual bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle);
/**
* Used in combination with |IsRootForNonTracingGC|. 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.
* See documentation on EmbedderRootsHandler.
*/
virtual void ResetHandleInNonTracingGC(
const v8::TracedReference<v8::Value>& handle);
@ -8934,6 +8953,18 @@ class V8_EXPORT Isolate {
*/
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
* embedder maintains ownership of the CppHeap. At most one C++ heap can be

View File

@ -8136,6 +8136,11 @@ EmbedderHeapTracer* Isolate::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) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
isolate->heap()->AttachCppHeap(cpp_heap);

View File

@ -8,6 +8,7 @@
#include <cstdint>
#include <map>
#include "include/v8.h"
#include "src/api/api-inl.h"
#include "src/base/compiler-specific.h"
#include "src/execution/vm-state-inl.h"
@ -1252,18 +1253,21 @@ void GlobalHandles::IdentifyWeakUnmodifiedObjects(
WeakSlotCallback is_unmodified) {
if (!FLAG_reclaim_unmodified_wrappers) return;
LocalEmbedderHeapTracer* const tracer =
isolate()->heap()->local_embedder_heap_tracer();
// Treat all objects as roots during incremental marking to avoid corrupting
// marking worklists.
if (isolate()->heap()->incremental_marking()->IsMarking()) return;
auto* const handler = isolate()->heap()->GetEmbedderRootsHandler();
for (TracedNode* node : traced_young_nodes_) {
if (node->IsInUse()) {
DCHECK(node->is_root());
if (is_unmodified(node->location())) {
v8::Value* value = ToApi<v8::Value>(node->handle());
if (node->has_destructor()) {
node->set_root(tracer->IsRootForNonTracingGC(
node->set_root(handler->IsRoot(
*reinterpret_cast<v8::TracedGlobal<v8::Value>*>(&value)));
} else {
node->set_root(tracer->IsRootForNonTracingGC(
node->set_root(handler->IsRoot(
*reinterpret_cast<v8::TracedReference<v8::Value>*>(&value)));
}
}
@ -1337,8 +1341,7 @@ void GlobalHandles::IterateYoungWeakObjectsForPhantomHandles(
if (!FLAG_reclaim_unmodified_wrappers) return;
LocalEmbedderHeapTracer* const tracer =
isolate()->heap()->local_embedder_heap_tracer();
auto* const handler = isolate()->heap()->GetEmbedderRootsHandler();
for (TracedNode* node : traced_young_nodes_) {
if (!node->IsInUse()) continue;
@ -1353,7 +1356,7 @@ void GlobalHandles::IterateYoungWeakObjectsForPhantomHandles(
node->ResetPhantomHandle(HandleHolder::kLive);
} else {
v8::Value* value = ToApi<v8::Value>(node->handle());
tracer->ResetHandleInNonTracingGC(
handler->ResetRoot(
*reinterpret_cast<v8::TracedReference<v8::Value>*>(&value));
DCHECK(!node->IsInUse());
}

View File

@ -17,6 +17,7 @@ void LocalEmbedderHeapTracer::SetRemoteTracer(EmbedderHeapTracer* tracer) {
if (remote_tracer_) remote_tracer_->isolate_ = nullptr;
remote_tracer_ = tracer;
default_embedder_roots_handler_.SetTracer(tracer);
if (remote_tracer_)
remote_tracer_->isolate_ = reinterpret_cast<v8::Isolate*>(isolate_);
}
@ -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 v8

View File

@ -16,6 +16,19 @@ namespace internal {
class Heap;
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 {
public:
using WrapperInfo = std::pair<void*, void*>;
@ -74,21 +87,6 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
bool Trace(double deadline);
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() {
return !FLAG_incremental_marking_wrappers || !InUse() ||
(IsRemoteTracingDone() && embedder_worklist_empty_);
@ -130,6 +128,10 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
void UpdateRemoteStats(size_t, double);
DefaultEmbedderRootsHandler& default_embedder_roots_handler() {
return default_embedder_roots_handler_;
}
private:
static constexpr size_t kEmbedderAllocatedThreshold = 128 * KB;
@ -147,6 +149,7 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
Isolate* const isolate_;
EmbedderHeapTracer* remote_tracer_ = nullptr;
DefaultEmbedderRootsHandler default_embedder_roots_handler_;
EmbedderHeapTracer::EmbedderStackState embedder_stack_state_ =
EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers;

View File

@ -5396,6 +5396,8 @@ void Heap::SetUpSpaces() {
dead_object_stats_.reset(new ObjectStats(this));
}
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-available", Available()));
@ -5534,6 +5536,14 @@ void Heap::SetEmbedderHeapTracer(EmbedderHeapTracer* 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 {
return local_embedder_heap_tracer()->remote_tracer();
}
@ -5674,6 +5684,8 @@ void Heap::TearDown() {
dead_object_stats_.reset();
local_embedder_heap_tracer_.reset();
embedder_roots_handler_ = nullptr;
if (cpp_heap_) {
CppHeap::From(cpp_heap_)->DetachIsolate();
cpp_heap_ = nullptr;

View File

@ -1154,6 +1154,14 @@ class 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. ================================================
// ===========================================================================
@ -2261,6 +2269,8 @@ class Heap {
// The embedder owns the C++ heap.
v8::CppHeap* cpp_heap_ = nullptr;
EmbedderRootsHandler* embedder_roots_handler_ = nullptr;
StrongRootsEntry* strong_roots_head_ = nullptr;
base::Mutex strong_roots_mutex_;