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,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
......
......@@ -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);
......
......@@ -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());
}
......
......@@ -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
......@@ -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;
......
......@@ -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;
......
......@@ -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_;
......
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