Skip to content

Commit 937e9bb

Browse files
legendecasrichardlau
authored andcommitted
src: track BaseObjects with an efficient list
PR-URL: #55104 Backport-PR-URL: #59959 Refs: #54880 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Joyee Cheung <[email protected]>
1 parent a232989 commit 937e9bb

15 files changed

+85
-85
lines changed

src/README.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -850,10 +850,19 @@ a `void* hint` argument.
850850
Inside these cleanup hooks, new asynchronous operations _may_ be started on the
851851
event loop, although ideally that is avoided as much as possible.
852852
853-
Every [`BaseObject`][] has its own cleanup hook that deletes it. For
854-
[`ReqWrap`][] and [`HandleWrap`][] instances, cleanup of the associated libuv
855-
objects is performed automatically, i.e. handles are closed and requests
856-
are cancelled if possible.
853+
For every [`ReqWrap`][] and [`HandleWrap`][] instance, the cleanup of the
854+
associated libuv objects is performed automatically, i.e. handles are closed
855+
and requests are cancelled if possible.
856+
857+
#### Cleanup realms and BaseObjects
858+
859+
Realm cleanup depends on the realm types. All realms are destroyed when the
860+
[`Environment`][] is destroyed with the cleanup hook. A [`ShadowRealm`][] can
861+
also be destroyed by the garbage collection when there is no strong reference
862+
to it.
863+
864+
Every [`BaseObject`][] is tracked with its creation realm and will be destroyed
865+
when the realm is tearing down.
857866
858867
#### Closing libuv handles
859868

src/aliased_buffer-inl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
55

66
#include "aliased_buffer.h"
7+
#include "memory_tracker-inl.h"
78
#include "util-inl.h"
89

910
namespace node {

src/base_object.cc

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "base_object.h"
22
#include "env-inl.h"
3+
#include "memory_tracker-inl.h"
34
#include "node_messaging.h"
45
#include "node_realm-inl.h"
56

@@ -24,13 +25,11 @@ BaseObject::BaseObject(Realm* realm, Local<Object> object)
2425
CHECK_EQ(false, object.IsEmpty());
2526
CHECK_GE(object->InternalFieldCount(), BaseObject::kInternalFieldCount);
2627
SetInternalFields(realm->isolate_data(), object, static_cast<void*>(this));
27-
realm->AddCleanupHook(DeleteMe, static_cast<void*>(this));
28-
realm->modify_base_object_count(1);
28+
realm->TrackBaseObject(this);
2929
}
3030

3131
BaseObject::~BaseObject() {
32-
realm()->modify_base_object_count(-1);
33-
realm()->RemoveCleanupHook(DeleteMe, static_cast<void*>(this));
32+
realm()->UntrackBaseObject(this);
3433

3534
if (has_pointer_data()) [[unlikely]] {
3635
PointerData* metadata = pointer_data();
@@ -147,12 +146,11 @@ void BaseObject::increase_refcount() {
147146
persistent_handle_.ClearWeak();
148147
}
149148

150-
void BaseObject::DeleteMe(void* data) {
151-
BaseObject* self = static_cast<BaseObject*>(data);
152-
if (self->has_pointer_data() && self->pointer_data()->strong_ptr_count > 0) {
153-
return self->Detach();
149+
void BaseObject::DeleteMe() {
150+
if (has_pointer_data() && pointer_data()->strong_ptr_count > 0) {
151+
return Detach();
154152
}
155-
delete self;
153+
delete this;
156154
}
157155

158156
bool BaseObject::IsDoneInitializing() const {
@@ -171,4 +169,17 @@ bool BaseObject::IsNotIndicativeOfMemoryLeakAtExit() const {
171169
return IsWeakOrDetached();
172170
}
173171

172+
void BaseObjectList::Cleanup() {
173+
while (!IsEmpty()) {
174+
BaseObject* bo = PopFront();
175+
bo->DeleteMe();
176+
}
177+
}
178+
179+
void BaseObjectList::MemoryInfo(node::MemoryTracker* tracker) const {
180+
for (auto bo : *this) {
181+
if (bo->IsDoneInitializing()) tracker->Track(bo);
182+
}
183+
}
184+
174185
} // namespace node

src/base_object.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <type_traits> // std::remove_reference
2828
#include "base_object_types.h"
2929
#include "memory_tracker.h"
30+
#include "util.h"
3031
#include "v8.h"
3132

3233
namespace node {
@@ -182,7 +183,7 @@ class BaseObject : public MemoryRetainer {
182183
private:
183184
v8::Local<v8::Object> WrappedObject() const override;
184185
bool IsRootNode() const override;
185-
static void DeleteMe(void* data);
186+
void DeleteMe();
186187

187188
// persistent_handle_ needs to be at a fixed offset from the start of the
188189
// class because it is used by src/node_postmortem_metadata.cc to calculate
@@ -227,6 +228,20 @@ class BaseObject : public MemoryRetainer {
227228

228229
Realm* realm_;
229230
PointerData* pointer_data_ = nullptr;
231+
ListNode<BaseObject> base_object_list_node_;
232+
233+
friend class BaseObjectList;
234+
};
235+
236+
class BaseObjectList
237+
: public ListHead<BaseObject, &BaseObject::base_object_list_node_>,
238+
public MemoryRetainer {
239+
public:
240+
void Cleanup();
241+
242+
SET_MEMORY_INFO_NAME(BaseObjectList)
243+
SET_SELF_SIZE(BaseObjectList)
244+
void MemoryInfo(node::MemoryTracker* tracker) const override;
230245
};
231246

232247
// Global alias for FromJSObject() to avoid churn.

src/cleanup_queue-inl.h

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,11 @@
33

44
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
55

6-
#include "base_object.h"
76
#include "cleanup_queue.h"
8-
#include "memory_tracker-inl.h"
97
#include "util.h"
108

119
namespace node {
1210

13-
inline void CleanupQueue::MemoryInfo(MemoryTracker* tracker) const {
14-
ForEachBaseObject([&](BaseObject* obj) {
15-
if (obj->IsDoneInitializing()) tracker->Track(obj);
16-
});
17-
}
18-
1911
inline size_t CleanupQueue::SelfSize() const {
2012
return sizeof(CleanupQueue) +
2113
cleanup_hooks_.size() * sizeof(CleanupHookCallback);
@@ -37,24 +29,6 @@ void CleanupQueue::Remove(Callback cb, void* arg) {
3729
cleanup_hooks_.erase(search);
3830
}
3931

40-
template <typename T>
41-
void CleanupQueue::ForEachBaseObject(T&& iterator) const {
42-
std::vector<CleanupHookCallback> callbacks = GetOrdered();
43-
44-
for (const auto& hook : callbacks) {
45-
BaseObject* obj = GetBaseObject(hook);
46-
if (obj != nullptr) iterator(obj);
47-
}
48-
}
49-
50-
BaseObject* CleanupQueue::GetBaseObject(
51-
const CleanupHookCallback& callback) const {
52-
if (callback.fn_ == BaseObject::DeleteMe)
53-
return static_cast<BaseObject*>(callback.arg_);
54-
else
55-
return nullptr;
56-
}
57-
5832
} // namespace node
5933

6034
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

src/cleanup_queue.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212

1313
namespace node {
1414

15-
class BaseObject;
16-
1715
class CleanupQueue : public MemoryRetainer {
1816
public:
1917
typedef void (*Callback)(void*);
@@ -24,7 +22,7 @@ class CleanupQueue : public MemoryRetainer {
2422
CleanupQueue(const CleanupQueue&) = delete;
2523

2624
SET_MEMORY_INFO_NAME(CleanupQueue)
27-
inline void MemoryInfo(node::MemoryTracker* tracker) const override;
25+
SET_NO_MEMORY_INFO()
2826
inline size_t SelfSize() const override;
2927

3028
inline bool empty() const;
@@ -33,9 +31,6 @@ class CleanupQueue : public MemoryRetainer {
3331
inline void Remove(Callback cb, void* arg);
3432
void Drain();
3533

36-
template <typename T>
37-
inline void ForEachBaseObject(T&& iterator) const;
38-
3934
private:
4035
class CleanupHookCallback {
4136
public:
@@ -68,7 +63,6 @@ class CleanupQueue : public MemoryRetainer {
6863
};
6964

7065
std::vector<CleanupHookCallback> GetOrdered() const;
71-
inline BaseObject* GetBaseObject(const CleanupHookCallback& callback) const;
7266

7367
// Use an unordered_set, so that we have efficient insertion and removal.
7468
std::unordered_set<CleanupHookCallback,

src/debug_utils-inl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include "debug_utils.h"
77
#include "env.h"
8+
#include "util-inl.h"
89

910
#include <type_traits>
1011

src/env.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1319,7 +1319,7 @@ void Environment::RunCleanup() {
13191319
cleanable->Clean();
13201320
}
13211321

1322-
while (!cleanup_queue_.empty() || principal_realm_->HasCleanupHooks() ||
1322+
while (!cleanup_queue_.empty() || principal_realm_->PendingCleanup() ||
13231323
native_immediates_.size() > 0 ||
13241324
native_immediates_threadsafe_.size() > 0 ||
13251325
native_immediates_interrupts_.size() > 0) {

src/node_messaging.cc

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ using v8::WasmModuleObject;
4343

4444
namespace node {
4545

46-
using BaseObjectList = std::vector<BaseObjectPtr<BaseObject>>;
46+
using BaseObjectPtrList = std::vector<BaseObjectPtr<BaseObject>>;
4747
using TransferMode = BaseObject::TransferMode;
4848

4949
// Hack to have WriteHostObject inform ReadHostObject that the value
@@ -1361,33 +1361,32 @@ std::unique_ptr<TransferData> JSTransferable::TransferOrClone() const {
13611361
Global<Value>(env()->isolate(), data));
13621362
}
13631363

1364-
Maybe<BaseObjectList>
1365-
JSTransferable::NestedTransferables() const {
1364+
Maybe<BaseObjectPtrList> JSTransferable::NestedTransferables() const {
13661365
// Call `this[kTransferList]()` and return the resulting list of BaseObjects.
13671366
HandleScope handle_scope(env()->isolate());
13681367
Local<Context> context = env()->isolate()->GetCurrentContext();
13691368
Local<Symbol> method_name = env()->messaging_transfer_list_symbol();
13701369

13711370
Local<Value> method;
13721371
if (!target()->Get(context, method_name).ToLocal(&method)) {
1373-
return Nothing<BaseObjectList>();
1372+
return Nothing<BaseObjectPtrList>();
13741373
}
1375-
if (!method->IsFunction()) return Just(BaseObjectList {});
1374+
if (!method->IsFunction()) return Just(BaseObjectPtrList{});
13761375

13771376
Local<Value> list_v;
13781377
if (!method.As<Function>()
13791378
->Call(context, target(), 0, nullptr)
13801379
.ToLocal(&list_v)) {
1381-
return Nothing<BaseObjectList>();
1380+
return Nothing<BaseObjectPtrList>();
13821381
}
1383-
if (!list_v->IsArray()) return Just(BaseObjectList {});
1382+
if (!list_v->IsArray()) return Just(BaseObjectPtrList{});
13841383
Local<Array> list = list_v.As<Array>();
13851384

1386-
BaseObjectList ret;
1385+
BaseObjectPtrList ret;
13871386
for (size_t i = 0; i < list->Length(); i++) {
13881387
Local<Value> value;
13891388
if (!list->Get(context, i).ToLocal(&value))
1390-
return Nothing<BaseObjectList>();
1389+
return Nothing<BaseObjectPtrList>();
13911390
if (!value->IsObject()) {
13921391
continue;
13931392
}

src/node_realm-inl.h

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
55

6-
#include "cleanup_queue-inl.h"
6+
#include "node_context_data.h"
77
#include "node_realm.h"
88

99
namespace node {
@@ -111,11 +111,9 @@ inline BindingDataStore* Realm::binding_data_store() {
111111

112112
template <typename T>
113113
void Realm::ForEachBaseObject(T&& iterator) const {
114-
cleanup_queue_.ForEachBaseObject(std::forward<T>(iterator));
115-
}
116-
117-
void Realm::modify_base_object_count(int64_t delta) {
118-
base_object_count_ += delta;
114+
for (auto bo : base_object_list_) {
115+
iterator(bo);
116+
}
119117
}
120118

121119
int64_t Realm::base_object_created_after_bootstrap() const {
@@ -126,16 +124,19 @@ int64_t Realm::base_object_count() const {
126124
return base_object_count_;
127125
}
128126

129-
void Realm::AddCleanupHook(CleanupQueue::Callback fn, void* arg) {
130-
cleanup_queue_.Add(fn, arg);
127+
void Realm::TrackBaseObject(BaseObject* bo) {
128+
DCHECK_EQ(bo->realm(), this);
129+
base_object_list_.PushBack(bo);
130+
++base_object_count_;
131131
}
132132

133-
void Realm::RemoveCleanupHook(CleanupQueue::Callback fn, void* arg) {
134-
cleanup_queue_.Remove(fn, arg);
133+
void Realm::UntrackBaseObject(BaseObject* bo) {
134+
DCHECK_EQ(bo->realm(), this);
135+
--base_object_count_;
135136
}
136137

137-
bool Realm::HasCleanupHooks() const {
138-
return !cleanup_queue_.empty();
138+
bool Realm::PendingCleanup() const {
139+
return !base_object_list_.IsEmpty();
139140
}
140141

141142
} // namespace node

0 commit comments

Comments
 (0)