diff options
author | legendecas <legendecas@gmail.com> | 2020-10-06 21:05:15 +0300 |
---|---|---|
committer | Node.js GitHub Bot <github-bot@iojs.org> | 2020-10-31 03:17:59 +0300 |
commit | 3a7537de7dd6c833cbe3f14b58e289c0a905afa5 (patch) | |
tree | 5f85a2026d2b199faf32337320359505285fab4e /src/node_api.cc | |
parent | 5da2512a72f56031a11c928792392110d4001b24 (diff) |
n-api: napi_make_callback emit async init with resource of async_context
instead of emit async init with receiver of the callback.
PR-URL: https://github.com/nodejs/node/pull/32930
Fixes: https://github.com/nodejs/node/issues/32898
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
Reviewed-By: Michael Dawson <midawson@redhat.com>
Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de>
Diffstat (limited to 'src/node_api.cc')
-rw-r--r-- | src/node_api.cc | 187 |
1 files changed, 143 insertions, 44 deletions
diff --git a/src/node_api.cc b/src/node_api.cc index 12f369a8097..4e932c19c2b 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -1,12 +1,15 @@ +#include "async_wrap-inl.h" #include "env-inl.h" #define NAPI_EXPERIMENTAL #include "js_native_api_v8.h" +#include "memory_tracker-inl.h" #include "node_api.h" #include "node_binding.h" #include "node_buffer.h" #include "node_errors.h" #include "node_internals.h" #include "threadpoolwork-inl.h" +#include "tracing/traced_value.h" #include "util-inl.h" #include <memory> @@ -104,16 +107,6 @@ static inline napi_env NewEnv(v8::Local<v8::Context> context) { return result; } -static inline napi_callback_scope -JsCallbackScopeFromV8CallbackScope(node::CallbackScope* s) { - return reinterpret_cast<napi_callback_scope>(s); -} - -static inline node::CallbackScope* -V8CallbackScopeFromJsCallbackScope(napi_callback_scope s) { - return reinterpret_cast<node::CallbackScope*>(s); -} - static inline void trigger_fatal_exception( napi_env env, v8::Local<v8::Value> local_err) { v8::Local<v8::Message> local_msg = @@ -435,6 +428,111 @@ class ThreadSafeFunction : public node::AsyncResource { bool handles_closing; }; +/** + * Compared to node::AsyncResource, the resource object in AsyncContext is + * gc-able. AsyncContext holds a weak reference to the resource object. + * AsyncContext::MakeCallback doesn't implicitly set the receiver of the + * callback to the resource object. + */ +class AsyncContext { + public: + AsyncContext(node_napi_env env, + v8::Local<v8::Object> resource_object, + const v8::Local<v8::String> resource_name, + bool externally_managed_resource) + : env_(env) { + async_id_ = node_env()->new_async_id(); + trigger_async_id_ = node_env()->get_default_trigger_async_id(); + resource_.Reset(node_env()->isolate(), resource_object); + lost_reference_ = false; + if (externally_managed_resource) { + resource_.SetWeak( + this, AsyncContext::WeakCallback, v8::WeakCallbackType::kParameter); + } + + node::AsyncWrap::EmitAsyncInit(node_env(), + resource_object, + resource_name, + async_id_, + trigger_async_id_); + } + + ~AsyncContext() { + resource_.Reset(); + lost_reference_ = true; + node::AsyncWrap::EmitDestroy(node_env(), async_id_); + } + + inline v8::MaybeLocal<v8::Value> MakeCallback( + v8::Local<v8::Object> recv, + const v8::Local<v8::Function> callback, + int argc, + v8::Local<v8::Value> argv[]) { + EnsureReference(); + return node::InternalMakeCallback(node_env(), + resource(), + recv, + callback, + argc, + argv, + {async_id_, trigger_async_id_}); + } + + inline napi_callback_scope OpenCallbackScope() { + EnsureReference(); + napi_callback_scope it = + reinterpret_cast<napi_callback_scope>(new CallbackScope(this)); + env_->open_callback_scopes++; + return it; + } + + inline void EnsureReference() { + if (lost_reference_) { + const v8::HandleScope handle_scope(node_env()->isolate()); + resource_.Reset(node_env()->isolate(), + v8::Object::New(node_env()->isolate())); + lost_reference_ = false; + } + } + + inline node::Environment* node_env() { return env_->node_env(); } + inline v8::Local<v8::Object> resource() { + return resource_.Get(node_env()->isolate()); + } + inline node::async_context async_context() { + return {async_id_, trigger_async_id_}; + } + + static inline void CloseCallbackScope(node_napi_env env, + napi_callback_scope s) { + CallbackScope* callback_scope = reinterpret_cast<CallbackScope*>(s); + delete callback_scope; + env->open_callback_scopes--; + } + + static void WeakCallback(const v8::WeakCallbackInfo<AsyncContext>& data) { + AsyncContext* async_context = data.GetParameter(); + async_context->resource_.Reset(); + async_context->lost_reference_ = true; + } + + private: + class CallbackScope : public node::CallbackScope { + public: + explicit CallbackScope(AsyncContext* async_context) + : node::CallbackScope(async_context->node_env()->isolate(), + async_context->resource_.Get( + async_context->node_env()->isolate()), + async_context->async_context()) {} + }; + + node_napi_env env_; + double async_id_; + double trigger_async_id_; + v8::Global<v8::Object> resource_; + bool lost_reference_; +}; + } // end of anonymous namespace } // end of namespace v8impl @@ -627,7 +725,7 @@ NAPI_NO_RETURN void napi_fatal_error(const char* location, } napi_status napi_open_callback_scope(napi_env env, - napi_value resource_object, + napi_value /** ignored */, napi_async_context async_context_handle, napi_callback_scope* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw @@ -635,20 +733,11 @@ napi_status napi_open_callback_scope(napi_env env, CHECK_ENV(env); CHECK_ARG(env, result); - v8::Local<v8::Context> context = env->context(); - - node::async_context* node_async_context = - reinterpret_cast<node::async_context*>(async_context_handle); - - v8::Local<v8::Object> resource; - CHECK_TO_OBJECT(env, context, resource, resource_object); + v8impl::AsyncContext* node_async_context = + reinterpret_cast<v8impl::AsyncContext*>(async_context_handle); - *result = v8impl::JsCallbackScopeFromV8CallbackScope( - new node::CallbackScope(env->isolate, - resource, - *node_async_context)); + *result = node_async_context->OpenCallbackScope(); - env->open_callback_scopes++; return napi_clear_last_error(env); } @@ -661,8 +750,9 @@ napi_status napi_close_callback_scope(napi_env env, napi_callback_scope scope) { return napi_callback_scope_mismatch; } - env->open_callback_scopes--; - delete v8impl::V8CallbackScopeFromJsCallbackScope(scope); + v8impl::AsyncContext::CloseCallbackScope(reinterpret_cast<node_napi_env>(env), + scope); + return napi_clear_last_error(env); } @@ -678,20 +768,24 @@ napi_status napi_async_init(napi_env env, v8::Local<v8::Context> context = env->context(); v8::Local<v8::Object> v8_resource; + bool externally_managed_resource; if (async_resource != nullptr) { CHECK_TO_OBJECT(env, context, v8_resource, async_resource); + externally_managed_resource = true; } else { v8_resource = v8::Object::New(isolate); + externally_managed_resource = false; } v8::Local<v8::String> v8_resource_name; CHECK_TO_STRING(env, context, v8_resource_name, async_resource_name); - // TODO(jasongin): Consider avoiding allocation here by using - // a tagged pointer with 2×31 bit fields instead. - node::async_context* async_context = new node::async_context(); + auto async_context = + new v8impl::AsyncContext(reinterpret_cast<node_napi_env>(env), + v8_resource, + v8_resource_name, + externally_managed_resource); - *async_context = node::EmitAsyncInit(isolate, v8_resource, v8_resource_name); *result = reinterpret_cast<napi_async_context>(async_context); return napi_clear_last_error(env); @@ -702,11 +796,8 @@ napi_status napi_async_destroy(napi_env env, CHECK_ENV(env); CHECK_ARG(env, async_context); - node::async_context* node_async_context = - reinterpret_cast<node::async_context*>(async_context); - node::EmitAsyncDestroy( - reinterpret_cast<node_napi_env>(env)->node_env(), - *node_async_context); + v8impl::AsyncContext* node_async_context = + reinterpret_cast<v8impl::AsyncContext*>(async_context); delete node_async_context; @@ -734,17 +825,25 @@ napi_status napi_make_callback(napi_env env, v8::Local<v8::Function> v8func; CHECK_TO_FUNCTION(env, v8func, func); - node::async_context* node_async_context = - reinterpret_cast<node::async_context*>(async_context); - if (node_async_context == nullptr) { - static node::async_context empty_context = { 0, 0 }; - node_async_context = &empty_context; - } + v8::MaybeLocal<v8::Value> callback_result; - v8::MaybeLocal<v8::Value> callback_result = node::MakeCallback( - env->isolate, v8recv, v8func, argc, - reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)), - *node_async_context); + if (async_context == nullptr) { + callback_result = node::MakeCallback( + env->isolate, + v8recv, + v8func, + argc, + reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)), + {0, 0}); + } else { + auto node_async_context = + reinterpret_cast<v8impl::AsyncContext*>(async_context); + callback_result = node_async_context->MakeCallback( + v8recv, + v8func, + argc, + reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv))); + } if (try_catch.HasCaught()) { return napi_set_last_error(env, napi_pending_exception); |