mirror of
https://github.com/nodejs/node.git
synced 2025-08-15 13:48:44 +02:00
fixup! src: track cppgc wrappers with a list in Realm
This commit is contained in:
parent
6b36802325
commit
68803ba5af
6 changed files with 99 additions and 12 deletions
|
@ -52,6 +52,12 @@ Environment* CppgcMixin::env() const {
|
||||||
return realm_->env();
|
return realm_->env();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CppgcMixin::~CppgcMixin() {
|
||||||
|
if (realm_ != nullptr) {
|
||||||
|
realm_->set_should_purge_empty_cppgc_wrappers(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
|
||||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||||
|
|
|
@ -4,15 +4,34 @@
|
||||||
namespace node {
|
namespace node {
|
||||||
|
|
||||||
void CppgcWrapperList::Cleanup() {
|
void CppgcWrapperList::Cleanup() {
|
||||||
for (auto handle : *this) {
|
for (auto node : *this) {
|
||||||
handle->Finalize();
|
CppgcMixin* ptr = node->persistent.Get();
|
||||||
|
if (ptr != nullptr) {
|
||||||
|
ptr->Finalize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CppgcWrapperList::MemoryInfo(MemoryTracker* tracker) const {
|
void CppgcWrapperList::MemoryInfo(MemoryTracker* tracker) const {
|
||||||
for (auto handle : *this) {
|
for (auto node : *this) {
|
||||||
tracker->Track(handle);
|
CppgcMixin* ptr = node->persistent.Get();
|
||||||
|
if (ptr != nullptr) {
|
||||||
|
tracker->Track(ptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CppgcWrapperList::PurgeEmpty() {
|
||||||
|
for (auto weak_it = begin(); weak_it != end();) {
|
||||||
|
CppgcWrapperListNode* node = *weak_it;
|
||||||
|
auto next_it = ++weak_it;
|
||||||
|
// The underlying cppgc wrapper has already been garbage collected.
|
||||||
|
// Remove it from the list.
|
||||||
|
if (!node->persistent) {
|
||||||
|
node->persistent.Clear();
|
||||||
|
delete node;
|
||||||
|
}
|
||||||
|
weak_it = next_it;
|
||||||
|
}
|
||||||
|
}
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <type_traits> // std::remove_reference
|
#include <type_traits> // std::remove_reference
|
||||||
#include "cppgc/garbage-collected.h"
|
#include "cppgc/garbage-collected.h"
|
||||||
#include "cppgc/name-provider.h"
|
#include "cppgc/name-provider.h"
|
||||||
|
#include "cppgc/persistent.h"
|
||||||
#include "memory_tracker.h"
|
#include "memory_tracker.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "v8-cppgc.h"
|
#include "v8-cppgc.h"
|
||||||
|
@ -16,7 +17,7 @@ namespace node {
|
||||||
|
|
||||||
class Environment;
|
class Environment;
|
||||||
class Realm;
|
class Realm;
|
||||||
class CppgcWrapperList;
|
class CppgcWrapperListNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a helper mixin with a BaseObject-like interface to help
|
* This is a helper mixin with a BaseObject-like interface to help
|
||||||
|
@ -100,17 +101,20 @@ class CppgcMixin : public cppgc::GarbageCollectedMixin, public MemoryRetainer {
|
||||||
realm_ = nullptr;
|
realm_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The default implementation of Clean() is a no-op. Subclasses
|
// The default implementation of Clean() is a no-op. If subclasses wish
|
||||||
// should override it to perform cleanup that require a living Realm,
|
// to perform cleanup that require a living Realm, they should
|
||||||
// instead of doing these cleanups directly in the destructor.
|
// should put the cleanups in a Clean() override, and call this->Finalize()
|
||||||
|
// in the destructor, instead of doing those cleanups directly in the
|
||||||
|
// destructor.
|
||||||
virtual void Clean(Realm* realm) {}
|
virtual void Clean(Realm* realm) {}
|
||||||
|
|
||||||
friend class CppgcWrapperList;
|
inline ~CppgcMixin();
|
||||||
|
|
||||||
|
friend class CppgcWrapperListNode;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Realm* realm_ = nullptr;
|
Realm* realm_ = nullptr;
|
||||||
v8::TracedReference<v8::Object> traced_reference_;
|
v8::TracedReference<v8::Object> traced_reference_;
|
||||||
ListNode<CppgcMixin> wrapper_list_node_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the class doesn't have additional owned traceable data, use this macro to
|
// If the class doesn't have additional owned traceable data, use this macro to
|
||||||
|
|
|
@ -130,9 +130,11 @@ void Realm::TrackBaseObject(BaseObject* bo) {
|
||||||
++base_object_count_;
|
++base_object_count_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CppgcWrapperListNode::CppgcWrapperListNode(CppgcMixin* ptr) : persistent(ptr) {}
|
||||||
|
|
||||||
void Realm::TrackCppgcWrapper(CppgcMixin* handle) {
|
void Realm::TrackCppgcWrapper(CppgcMixin* handle) {
|
||||||
DCHECK_EQ(handle->realm(), this);
|
DCHECK_EQ(handle->realm(), this);
|
||||||
cppgc_wrapper_list_.PushFront(handle);
|
cppgc_wrapper_list_.PushFront(new CppgcWrapperListNode(handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Realm::UntrackBaseObject(BaseObject* bo) {
|
void Realm::UntrackBaseObject(BaseObject* bo) {
|
||||||
|
|
|
@ -10,7 +10,10 @@ namespace node {
|
||||||
|
|
||||||
using v8::Context;
|
using v8::Context;
|
||||||
using v8::EscapableHandleScope;
|
using v8::EscapableHandleScope;
|
||||||
|
using v8::GCCallbackFlags;
|
||||||
|
using v8::GCType;
|
||||||
using v8::HandleScope;
|
using v8::HandleScope;
|
||||||
|
using v8::Isolate;
|
||||||
using v8::Local;
|
using v8::Local;
|
||||||
using v8::MaybeLocal;
|
using v8::MaybeLocal;
|
||||||
using v8::Object;
|
using v8::Object;
|
||||||
|
@ -22,12 +25,28 @@ Realm::Realm(Environment* env, v8::Local<v8::Context> context, Kind kind)
|
||||||
: env_(env), isolate_(context->GetIsolate()), kind_(kind) {
|
: env_(env), isolate_(context->GetIsolate()), kind_(kind) {
|
||||||
context_.Reset(isolate_, context);
|
context_.Reset(isolate_, context);
|
||||||
env->AssignToContext(context, this, ContextInfo(""));
|
env->AssignToContext(context, this, ContextInfo(""));
|
||||||
|
// The environment can also purge empty wrappers in the check callback,
|
||||||
|
// though that may be a bit excessive depending on usage patterns.
|
||||||
|
// For now using the GC epilogue is adequate.
|
||||||
|
isolate_->AddGCEpilogueCallback(PurgeEmptyCppgcWrappers, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Realm::~Realm() {
|
Realm::~Realm() {
|
||||||
|
isolate_->RemoveGCEpilogueCallback(PurgeEmptyCppgcWrappers, this);
|
||||||
CHECK_EQ(base_object_count_, 0);
|
CHECK_EQ(base_object_count_, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Realm::PurgeEmptyCppgcWrappers(Isolate* isolate,
|
||||||
|
GCType type,
|
||||||
|
GCCallbackFlags flags,
|
||||||
|
void* data) {
|
||||||
|
Realm* realm = static_cast<Realm*>(data);
|
||||||
|
if (realm->should_purge_empty_cppgc_wrappers_) {
|
||||||
|
realm->cppgc_wrapper_list_.PurgeEmpty();
|
||||||
|
realm->should_purge_empty_cppgc_wrappers_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Realm::MemoryInfo(MemoryTracker* tracker) const {
|
void Realm::MemoryInfo(MemoryTracker* tracker) const {
|
||||||
#define V(PropertyName, TypeName) \
|
#define V(PropertyName, TypeName) \
|
||||||
tracker->TrackField(#PropertyName, PropertyName());
|
tracker->TrackField(#PropertyName, PropertyName());
|
||||||
|
|
|
@ -26,11 +26,34 @@ using BindingDataStore =
|
||||||
std::array<BaseObjectWeakPtr<BaseObject>,
|
std::array<BaseObjectWeakPtr<BaseObject>,
|
||||||
static_cast<size_t>(BindingDataType::kBindingDataTypeCount)>;
|
static_cast<size_t>(BindingDataType::kBindingDataTypeCount)>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a wrapper around a weak persistent of CppgcMixin, used in the
|
||||||
|
* CppgcWrapperList to avoid accessing already garbage collected CppgcMixins.
|
||||||
|
*/
|
||||||
|
class CppgcWrapperListNode {
|
||||||
|
public:
|
||||||
|
explicit inline CppgcWrapperListNode(CppgcMixin* ptr);
|
||||||
|
inline explicit operator bool() const { return !persistent; }
|
||||||
|
inline CppgcMixin* operator->() const { return persistent.Get(); }
|
||||||
|
inline CppgcMixin* operator*() const { return persistent.Get(); }
|
||||||
|
|
||||||
|
cppgc::WeakPersistent<CppgcMixin> persistent;
|
||||||
|
// Used by ContainerOf in the ListNode implementation for fast manipulation of
|
||||||
|
// CppgcWrapperList.
|
||||||
|
ListNode<CppgcWrapperListNode> wrapper_list_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A per-realm list of weak persistent of cppgc wrappers, which implements
|
||||||
|
* iterations that require iterate over cppgc wrappers created by Node.js.
|
||||||
|
*/
|
||||||
class CppgcWrapperList
|
class CppgcWrapperList
|
||||||
: public ListHead<CppgcMixin, &CppgcMixin::wrapper_list_node_>,
|
: public ListHead<CppgcWrapperListNode,
|
||||||
|
&CppgcWrapperListNode::wrapper_list_node>,
|
||||||
public MemoryRetainer {
|
public MemoryRetainer {
|
||||||
public:
|
public:
|
||||||
void Cleanup();
|
void Cleanup();
|
||||||
|
void PurgeEmpty();
|
||||||
|
|
||||||
SET_MEMORY_INFO_NAME(CppgcWrapperList)
|
SET_MEMORY_INFO_NAME(CppgcWrapperList)
|
||||||
SET_SELF_SIZE(CppgcWrapperList)
|
SET_SELF_SIZE(CppgcWrapperList)
|
||||||
|
@ -141,6 +164,14 @@ class Realm : public MemoryRetainer {
|
||||||
// it's only used for tests.
|
// it's only used for tests.
|
||||||
std::vector<std::string> builtins_in_snapshot;
|
std::vector<std::string> builtins_in_snapshot;
|
||||||
|
|
||||||
|
// This used during the destruction of cppgc wrappers to inform a GC epilogue
|
||||||
|
// callback to clean up the weak persistents used to track cppgc wrappers if
|
||||||
|
// the wrappers are already garbage collected to prevent holding on to
|
||||||
|
// excessive useless persistents.
|
||||||
|
inline void set_should_purge_empty_cppgc_wrappers(bool value) {
|
||||||
|
should_purge_empty_cppgc_wrappers_ = value;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
~Realm();
|
~Realm();
|
||||||
|
|
||||||
|
@ -150,11 +181,17 @@ class Realm : public MemoryRetainer {
|
||||||
// Shorthand for isolate pointer.
|
// Shorthand for isolate pointer.
|
||||||
v8::Isolate* isolate_;
|
v8::Isolate* isolate_;
|
||||||
v8::Global<v8::Context> context_;
|
v8::Global<v8::Context> context_;
|
||||||
|
bool should_purge_empty_cppgc_wrappers_ = false;
|
||||||
|
|
||||||
#define V(PropertyName, TypeName) v8::Global<TypeName> PropertyName##_;
|
#define V(PropertyName, TypeName) v8::Global<TypeName> PropertyName##_;
|
||||||
PER_REALM_STRONG_PERSISTENT_VALUES(V)
|
PER_REALM_STRONG_PERSISTENT_VALUES(V)
|
||||||
#undef V
|
#undef V
|
||||||
|
|
||||||
|
static void PurgeEmptyCppgcWrappers(v8::Isolate* isolate,
|
||||||
|
v8::GCType type,
|
||||||
|
v8::GCCallbackFlags flags,
|
||||||
|
void* data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void InitializeContext(v8::Local<v8::Context> context,
|
void InitializeContext(v8::Local<v8::Context> context,
|
||||||
const RealmSerializeInfo* realm_info);
|
const RealmSerializeInfo* realm_info);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue