diff options
author | Anna Henningsen <anna@addaleax.net> | 2020-07-31 03:10:14 +0300 |
---|---|---|
committer | Anna Henningsen <anna@addaleax.net> | 2020-08-07 01:02:02 +0300 |
commit | 22cbbcf9d9374d4b663bf1409f292212fa57623a (patch) | |
tree | 59a9e22bdae3900b961e4c58f53aa6c9b0a511bd /src/api | |
parent | a4e1755df2f9bb8c712d7ca767a5944d9d71e33b (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.cc | 68 |
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; |