Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src/api
diff options
context:
space:
mode:
authorAnna Henningsen <anna@addaleax.net>2020-07-31 03:10:14 +0300
committerAnna Henningsen <anna@addaleax.net>2020-08-07 01:02:02 +0300
commit22cbbcf9d9374d4b663bf1409f292212fa57623a (patch)
tree59a9e22bdae3900b961e4c58f53aa6c9b0a511bd /src/api
parenta4e1755df2f9bb8c712d7ca767a5944d9d71e33b (diff)
n-api,src: provide asynchronous cleanup hooks
Sometimes addons need to perform cleanup actions, for example closing libuv handles or waiting for requests to finish, that cannot be performed synchronously. Add C++ API and N-API functions that allow providing such asynchronous cleanup hooks. Fixes: https://github.com/nodejs/node/issues/34567 PR-URL: https://github.com/nodejs/node/pull/34572 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com>
Diffstat (limited to 'src/api')
-rw-r--r--src/api/hooks.cc68
1 files changed, 66 insertions, 2 deletions
diff --git a/src/api/hooks.cc b/src/api/hooks.cc
index 037bdda6f41..3b16c0350d8 100644
--- a/src/api/hooks.cc
+++ b/src/api/hooks.cc
@@ -73,8 +73,35 @@ int EmitExit(Environment* env) {
.ToChecked();
}
+typedef void (*CleanupHook)(void* arg);
+typedef void (*AsyncCleanupHook)(void* arg, void(*)(void*), void*);
+
+struct AsyncCleanupHookInfo final {
+ Environment* env;
+ AsyncCleanupHook fun;
+ void* arg;
+ bool started = false;
+ // Use a self-reference to make sure the storage is kept alive while the
+ // cleanup hook is registered but not yet finished.
+ std::shared_ptr<AsyncCleanupHookInfo> self;
+};
+
+// Opaque type that is basically an alias for `shared_ptr<AsyncCleanupHookInfo>`
+// (but not publicly so for easier ABI/API changes). In particular,
+// std::shared_ptr does not generally maintain a consistent ABI even on a
+// specific platform.
+struct ACHHandle final {
+ std::shared_ptr<AsyncCleanupHookInfo> info;
+};
+// This is implemented as an operator on a struct because otherwise you can't
+// default-initialize AsyncCleanupHookHandle, because in C++ for a
+// std::unique_ptr to be default-initializable the deleter type also needs
+// to be default-initializable; in particular, function types don't satisfy
+// this.
+void DeleteACHHandle::operator ()(ACHHandle* handle) const { delete handle; }
+
void AddEnvironmentCleanupHook(Isolate* isolate,
- void (*fun)(void* arg),
+ CleanupHook fun,
void* arg) {
Environment* env = Environment::GetCurrent(isolate);
CHECK_NOT_NULL(env);
@@ -82,13 +109,50 @@ void AddEnvironmentCleanupHook(Isolate* isolate,
}
void RemoveEnvironmentCleanupHook(Isolate* isolate,
- void (*fun)(void* arg),
+ CleanupHook fun,
void* arg) {
Environment* env = Environment::GetCurrent(isolate);
CHECK_NOT_NULL(env);
env->RemoveCleanupHook(fun, arg);
}
+static void FinishAsyncCleanupHook(void* arg) {
+ AsyncCleanupHookInfo* info = static_cast<AsyncCleanupHookInfo*>(arg);
+ std::shared_ptr<AsyncCleanupHookInfo> keep_alive = info->self;
+
+ info->env->DecreaseWaitingRequestCounter();
+ info->self.reset();
+}
+
+static void RunAsyncCleanupHook(void* arg) {
+ AsyncCleanupHookInfo* info = static_cast<AsyncCleanupHookInfo*>(arg);
+ info->env->IncreaseWaitingRequestCounter();
+ info->started = true;
+ info->fun(info->arg, FinishAsyncCleanupHook, info);
+}
+
+AsyncCleanupHookHandle AddEnvironmentCleanupHook(
+ Isolate* isolate,
+ AsyncCleanupHook fun,
+ void* arg) {
+ Environment* env = Environment::GetCurrent(isolate);
+ CHECK_NOT_NULL(env);
+ auto info = std::make_shared<AsyncCleanupHookInfo>();
+ info->env = env;
+ info->fun = fun;
+ info->arg = arg;
+ info->self = info;
+ env->AddCleanupHook(RunAsyncCleanupHook, info.get());
+ return AsyncCleanupHookHandle(new ACHHandle { info });
+}
+
+void RemoveEnvironmentCleanupHook(
+ AsyncCleanupHookHandle handle) {
+ if (handle->info->started) return;
+ handle->info->self.reset();
+ handle->info->env->RemoveCleanupHook(RunAsyncCleanupHook, handle->info.get());
+}
+
async_id AsyncHooksGetExecutionAsyncId(Isolate* isolate) {
Environment* env = Environment::GetCurrent(isolate);
if (env == nullptr) return -1;