diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 0f04c21bd7fa37..cf0b6cc6edd46f 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -3265,7 +3265,7 @@ changes: NAPI_EXTERN napi_status napi_create_async_work(napi_env env, napi_value async_resource, - const char* async_resource_name, + napi_value async_resource_name, napi_async_execute_callback execute, napi_async_complete_callback complete, void* data, diff --git a/node.gyp b/node.gyp index e37aadc036ef59..7458e84621b983 100644 --- a/node.gyp +++ b/node.gyp @@ -241,6 +241,7 @@ 'src/handle_wrap.cc', 'src/js_stream.cc', 'src/node.cc', + 'src/node_api_backport.cc', 'src/node_api.cc', 'src/node_api.h', 'src/node_api_types.h', diff --git a/src/node_api.cc b/src/node_api.cc index 7a7f6d1b75c8c3..a62ab51f88b024 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -3255,7 +3255,7 @@ class Work : public node::AsyncResource { private: explicit Work(napi_env env, v8::Local async_resource, - const char* async_resource_name, + v8::Local async_resource_name, napi_async_execute_callback execute, napi_async_complete_callback complete = nullptr, void* data = nullptr) @@ -3275,7 +3275,7 @@ class Work : public node::AsyncResource { public: static Work* New(napi_env env, v8::Local async_resource, - const char* async_resource_name, + v8::Local async_resource_name, napi_async_execute_callback execute, napi_async_complete_callback complete, void* data) { @@ -3345,7 +3345,7 @@ class Work : public node::AsyncResource { napi_status napi_create_async_work(napi_env env, napi_value async_resource, - const char* async_resource_name, + napi_value async_resource_name, napi_async_execute_callback execute, napi_async_complete_callback complete, void* data, @@ -3354,17 +3354,20 @@ napi_status napi_create_async_work(napi_env env, CHECK_ARG(env, execute); CHECK_ARG(env, result); + v8::Local context = env->isolate->GetCurrentContext(); + v8::Local resource; if (async_resource != nullptr) { - auto value = v8impl::V8LocalValueFromJsValue(async_resource); - RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); - resource = value.As(); + CHECK_TO_OBJECT(env, context, resource, async_resource); } else { resource = v8::Object::New(env->isolate); } + v8::Local resource_name; + CHECK_TO_STRING(env, context, resource_name, async_resource_name); + uvimpl::Work* work = - uvimpl::Work::New(env, resource, async_resource_name, + uvimpl::Work::New(env, resource, resource_name, execute, complete, data); *result = reinterpret_cast(work); diff --git a/src/node_api.h b/src/node_api.h index 227f26a2ffb6fb..b127fd7fe80070 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -525,7 +525,7 @@ NAPI_EXTERN napi_status napi_get_dataview_info(napi_env env, NAPI_EXTERN napi_status napi_create_async_work(napi_env env, napi_value async_resource, - const char* async_resource_name, + napi_value async_resource_name, napi_async_execute_callback execute, napi_async_complete_callback complete, void* data, diff --git a/src/node_api_backport.cc b/src/node_api_backport.cc new file mode 100644 index 00000000000000..249e3e8100efef --- /dev/null +++ b/src/node_api_backport.cc @@ -0,0 +1,89 @@ +#include "node_api_backport.h" + +using v8::Function; +using v8::HandleScope; +using v8::Local; +using v8::MaybeLocal; +using v8::Object; +using v8::TryCatch; +using v8::Value; + +namespace node { + +CallbackScope::CallbackScope(v8::Isolate *_isolate, + v8::Local _object, + node::async_context context) : + isolate(_isolate), + env(node::Environment::GetCurrent(isolate->GetCurrentContext())), + _try_catch(isolate), + object(_object), + callback_scope( + node::Environment::GetCurrent(isolate->GetCurrentContext())) { + Local pre_fn = env->async_hooks_pre_function(); + + Local async_queue_v = object->Get(env->async_queue_string()); + if (async_queue_v->IsObject()) + ran_init_callback = true; + + if (ran_init_callback && !pre_fn.IsEmpty()) { + TryCatch try_catch(env->isolate()); + MaybeLocal ar = pre_fn->Call(env->context(), object, 0, nullptr); + if (ar.IsEmpty()) { + ClearFatalExceptionHandlers(env); + FatalException(env->isolate(), try_catch); + } + } +} + +CallbackScope::~CallbackScope() { + Local post_fn = env->async_hooks_post_function(); + if (ran_init_callback && !post_fn.IsEmpty()) { + Local did_throw = v8::Boolean::New(isolate, _try_catch.HasCaught()); + // Currently there's no way to retrieve an uid from node::MakeCallback(). + // This needs to be fixed. + Local vals[] = + { Undefined(env->isolate()).As(), did_throw }; + TryCatch try_catch(env->isolate()); + MaybeLocal ar = + post_fn->Call(env->context(), object, arraysize(vals), vals); + if (ar.IsEmpty()) { + ClearFatalExceptionHandlers(env); + FatalException(env->isolate(), try_catch); + return; + } + } + + if (callback_scope.in_makecallback()) { + return; + } + + Environment::TickInfo* tick_info = env->tick_info(); + + if (tick_info->length() == 0) { + env->isolate()->RunMicrotasks(); + } + + if (tick_info->length() == 0) { + tick_info->set_index(0); + return; + } + + env->tick_callback_function()->Call(env->process_object(), 0, nullptr); +} + +AsyncResource::AsyncResource(v8::Isolate* _isolate, + v8::Local _object, + v8::Local name) : isolate(_isolate) { + object.Reset(isolate, _object); +} + +AsyncResource::~AsyncResource() { + object.Reset(); +} + +} // end of namespace node + +CallbackScope::CallbackScope(node::AsyncResource* work) : + scope(work->isolate, + v8::Local::New(work->isolate, + work->object), {0, 0}) {} diff --git a/src/node_api_backport.h b/src/node_api_backport.h index da607735a36b74..549b3ed42033de 100644 --- a/src/node_api_backport.h +++ b/src/node_api_backport.h @@ -5,6 +5,48 @@ // which have appeared in later versions of Node.js, and which are required for // N-API. -#define ToChecked FromJust +#include "node_internals.h" + +namespace node { + +typedef int async_id; + +typedef struct async_context { + node::async_id async_id; + node::async_id trigger_async_id; +} async_context; + +class CallbackScope { + public: + CallbackScope(v8::Isolate *isolate, + v8::Local object, + node::async_context context); + ~CallbackScope(); + private: + v8::Isolate* isolate = nullptr; + node::Environment* env = nullptr; + v8::TryCatch _try_catch; + bool ran_init_callback = false; + v8::Local object; + Environment::AsyncCallbackScope callback_scope; +}; + +class AsyncResource { + public: + AsyncResource(v8::Isolate* _isolate, + v8::Local _object, + v8::Local name); + ~AsyncResource(); + v8::Isolate* isolate; + v8::Persistent object; +}; +} // end of namespace node + +class CallbackScope { + public: + explicit CallbackScope(node::AsyncResource* work); + private: + node::CallbackScope scope; +}; #endif // SRC_NODE_API_BACKPORT_H_ diff --git a/test/addons-napi/test_async/test_async.cc b/test/addons-napi/test_async/test_async.cc index bd54c77efe5e58..71b1c03fba0cf8 100644 --- a/test/addons-napi/test_async/test_async.cc +++ b/test/addons-napi/test_async/test_async.cc @@ -71,6 +71,7 @@ napi_value Test(napi_env env, napi_callback_info info) { size_t argc = 3; napi_value argv[3]; napi_value _this; + napi_value resource_name; void* data; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &_this, &data)); @@ -93,7 +94,10 @@ napi_value Test(napi_env env, napi_callback_info info) { napi_get_value_int32(env, argv[0], &the_carrier._input)); NAPI_CALL(env, napi_create_reference(env, argv[2], 1, &the_carrier._callback)); - NAPI_CALL(env, napi_create_async_work(env, argv[1], "TestResource", + + NAPI_CALL(env, + napi_create_string_utf8(env, "TestResource", -1, &resource_name)); + NAPI_CALL(env, napi_create_async_work(env, argv[1], resource_name, Execute, Complete, &the_carrier, &the_carrier._request)); NAPI_CALL(env, napi_queue_async_work(env, the_carrier._request)); @@ -138,12 +142,16 @@ napi_value TestCancel(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value argv[1]; napi_value _this; + napi_value resource_name; void* data; + NAPI_CALL(env, + napi_create_string_utf8(env, "TestResource", -1, &resource_name)); + // make sure the work we are going to cancel will not be // able to start by using all the threads in the pool for (int i = 1; i < MAX_CANCEL_THREADS; i++) { - NAPI_CALL(env, napi_create_async_work(env, nullptr, "TestCancelBusy", + NAPI_CALL(env, napi_create_async_work(env, nullptr, resource_name, CancelExecute, BusyCancelComplete, &async_carrier[i], &async_carrier[i]._request)); NAPI_CALL(env, napi_queue_async_work(env, async_carrier[i]._request)); @@ -155,7 +163,7 @@ napi_value TestCancel(napi_env env, napi_callback_info info) { // workers above. NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &_this, &data)); - NAPI_CALL(env, napi_create_async_work(env, nullptr, "TestCancelled", + NAPI_CALL(env, napi_create_async_work(env, nullptr, resource_name, CancelExecute, CancelComplete, &async_carrier[0], &async_carrier[0]._request)); NAPI_CALL(env,