diff options
author | Nicolai Stange <nicstange@gmail.com> | 2020-11-29 05:11:29 +0300 |
---|---|---|
committer | Rich Trott <rtrott@gmail.com> | 2020-12-11 15:43:27 +0300 |
commit | 348a3adacb5a3893ef07d89315e91c2e71036d10 (patch) | |
tree | cb538117ea2c8f6168a2ab6bbdb75366df677749 /src/api | |
parent | f49cef549c652945a119bc265d0e1675cfb26e2c (diff) |
src: introduce convenience node::MakeSyncCallback()
There are situations where one wants to invoke a JS callback's ->Call()
from C++ and in particular retain any existing async_context state, but
where it's not obvious that a plain ->Call() would be safe at the point
in question.
Such callsites usually resort to
node::MakeCallback(..., async_context{0, 0}), which unconditionally
pushes the async_context{0, 0} and takes the required provisions for the
->Call() itself such as triggering the tick after its return, if needed.
An example would be the PerformanceObserver invocation from
PerformanceEntry::Notify(): this can get called when coming from JS
through e.g. perf_hooks.performance.mark() and alike, but perhaps also
from nghttp2 (c.f. EmitStatistics() in node_http2.cc).
In the former case, a plain ->Call() would be safe and it would be
desirable to retain the current async_context so that
PerformanceObservers can access it resp. the associated
AsyncLocalStorage. However, in the second case the additional provisions
taken by node::MakeCallback() might potentially be strictly required.
So PerformanceEntry::Notify() bites the bullet and invokes the
PerformanceObservers through node::MakeCallback() unconditionally,
thereby always rendering any possibly preexisting async_context
inaccessible.
Introduce the convenience node::MakeSyncCallback() for such usecases,
which would basically forward to ->Call() if safe and to
node::MakeCallback(..., async_context{0, 0}) otherwise.
Co-Authored-By: ZauberNerd <zaubernerd@zaubernerd.de>
PR-URL: https://github.com/nodejs/node/pull/36343
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Diffstat (limited to 'src/api')
-rw-r--r-- | src/api/callback.cc | 28 |
1 files changed, 28 insertions, 0 deletions
diff --git a/src/api/callback.cc b/src/api/callback.cc index 3d4f91a866e..8a0b71cd362 100644 --- a/src/api/callback.cc +++ b/src/api/callback.cc @@ -266,6 +266,34 @@ MaybeLocal<Value> MakeCallback(Isolate* isolate, return ret; } +// Use this if you just want to safely invoke some JS callback and +// would like to retain the currently active async_context, if any. +// In case none is available, a fixed default context will be +// installed otherwise. +MaybeLocal<Value> MakeSyncCallback(Isolate* isolate, + Local<Object> recv, + Local<Function> callback, + int argc, + Local<Value> argv[]) { + Environment* env = Environment::GetCurrent(callback->CreationContext()); + CHECK_NOT_NULL(env); + if (!env->can_call_into_js()) return Local<Value>(); + + Context::Scope context_scope(env->context()); + if (env->async_callback_scope_depth()) { + // There's another MakeCallback() on the stack, piggy back on it. + // In particular, retain the current async_context. + return callback->Call(env->context(), recv, argc, argv); + } + + // This is a toplevel invocation and the caller (intentionally) + // didn't provide any async_context to run in. Install a default context. + MaybeLocal<Value> ret = + InternalMakeCallback(env, env->process_object(), recv, callback, argc, argv, + async_context{0, 0}); + return ret; +} + // Legacy MakeCallback()s Local<Value> MakeCallback(Isolate* isolate, |