src: add Cleanable class to Environment

We store a linked list of `Cleanable` objects on the
`node::Environment` and invoke their `Clean()` method during env
teardown.

This eliminates the need for adding many cleanup hooks.

PR-URL: https://github.com/nodejs/node/pull/54880
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
This commit is contained in:
Gabriel Schulhof 2024-09-13 07:30:32 -07:00 committed by GitHub
parent 99bbf80608
commit d38dc99a95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 31 additions and 12 deletions

View File

@ -1279,6 +1279,11 @@ void Environment::RunCleanup() {
// Defer the BaseObject cleanup after handles are cleaned up.
CleanupHandles();
while (!cleanable_queue_.IsEmpty()) {
Cleanable* cleanable = cleanable_queue_.PopFront();
cleanable->Clean();
}
while (!cleanup_queue_.empty() || principal_realm_->HasCleanupHooks() ||
native_immediates_.size() > 0 ||
native_immediates_threadsafe_.size() > 0 ||

View File

@ -594,6 +594,18 @@ void DefaultProcessExitHandlerInternal(Environment* env, ExitCode exit_code);
v8::Maybe<ExitCode> SpinEventLoopInternal(Environment* env);
v8::Maybe<ExitCode> EmitProcessExitInternal(Environment* env);
class Cleanable {
public:
virtual ~Cleanable() = default;
protected:
ListNode<Cleanable> cleanable_queue_;
private:
virtual void Clean() = 0;
friend class Environment;
};
/**
* Environment is a per-isolate data structure that represents an execution
* environment. Each environment has a principal realm. An environment can
@ -902,8 +914,12 @@ class Environment final : public MemoryRetainer {
typedef ListHead<HandleWrap, &HandleWrap::handle_wrap_queue_> HandleWrapQueue;
typedef ListHead<ReqWrapBase, &ReqWrapBase::req_wrap_queue_> ReqWrapQueue;
typedef ListHead<Cleanable, &Cleanable::cleanable_queue_> CleanableQueue;
inline HandleWrapQueue* handle_wrap_queue() { return &handle_wrap_queue_; }
inline CleanableQueue* cleanable_queue() {
return &cleanable_queue_;
}
inline ReqWrapQueue* req_wrap_queue() { return &req_wrap_queue_; }
// https://w3c.github.io/hr-time/#dfn-time-origin
@ -1192,6 +1208,7 @@ class Environment final : public MemoryRetainer {
// memory are predictable. For more information please refer to
// `doc/contributing/node-postmortem-support.md`
friend int GenDebugSymbols();
CleanableQueue cleanable_queue_;
HandleWrapQueue handle_wrap_queue_;
ReqWrapQueue req_wrap_queue_;
std::list<HandleCleanup> handle_cleanup_queue_;

View File

@ -84,7 +84,7 @@ using v8::Value;
namespace {
class CallbackInfo {
class CallbackInfo : public Cleanable {
public:
static inline Local<ArrayBuffer> CreateTrackedArrayBuffer(
Environment* env,
@ -97,7 +97,7 @@ class CallbackInfo {
CallbackInfo& operator=(const CallbackInfo&) = delete;
private:
static void CleanupHook(void* data);
void Clean();
inline void OnBackingStoreFree();
inline void CallAndResetCallback();
inline CallbackInfo(Environment* env,
@ -112,7 +112,6 @@ class CallbackInfo {
Environment* const env_;
};
Local<ArrayBuffer> CallbackInfo::CreateTrackedArrayBuffer(
Environment* env,
char* data,
@ -152,25 +151,23 @@ CallbackInfo::CallbackInfo(Environment* env,
data_(data),
hint_(hint),
env_(env) {
env->AddCleanupHook(CleanupHook, this);
env->cleanable_queue()->PushFront(this);
env->isolate()->AdjustAmountOfExternalAllocatedMemory(sizeof(*this));
}
void CallbackInfo::CleanupHook(void* data) {
CallbackInfo* self = static_cast<CallbackInfo*>(data);
void CallbackInfo::Clean() {
{
HandleScope handle_scope(self->env_->isolate());
Local<ArrayBuffer> ab = self->persistent_.Get(self->env_->isolate());
HandleScope handle_scope(env_->isolate());
Local<ArrayBuffer> ab = persistent_.Get(env_->isolate());
if (!ab.IsEmpty() && ab->IsDetachable()) {
ab->Detach(Local<Value>()).Check();
self->persistent_.Reset();
persistent_.Reset();
}
}
// Call the callback in this case, but don't delete `this` yet because the
// BackingStore deleter callback will do so later.
self->CallAndResetCallback();
CallAndResetCallback();
}
void CallbackInfo::CallAndResetCallback() {
@ -182,7 +179,7 @@ void CallbackInfo::CallAndResetCallback() {
}
if (callback != nullptr) {
// Clean up all Environment-related state and run the callback.
env_->RemoveCleanupHook(CleanupHook, this);
cleanable_queue_.Remove();
int64_t change_in_bytes = -static_cast<int64_t>(sizeof(*this));
env_->isolate()->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);