diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 70960fcf6f5495..b31ea3a2688d43 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -3684,6 +3684,23 @@ NAPI_EXTERN napi_status napi_run_script(napi_env env, - `[in] script`: A JavaScript string containing the script to execute. - `[out] result`: The value resulting from having executed the script. +## libuv event loop + +N-API provides a function for getting the current event loop associated with +a specific `napi_env`. + +### napi_get_uv_event_loop + +```C +NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env, + uv_loop_t** loop); +``` + +- `[in] env`: The environment that the API is invoked under. +- `[out] loop`: The current libuv loop instance. + [Promises]: #n_api_promises [Simple Asynchronous Operations]: #n_api_simple_asynchronous_operations [Custom Asynchronous Operations]: #n_api_custom_asynchronous_operations diff --git a/src/node_api.cc b/src/node_api.cc index f1c13214b3ed96..10c8093f5a280b 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -22,7 +22,7 @@ #include "node_api_backport.h" #include "util.h" -#define NAPI_VERSION 1 +#define NAPI_VERSION 2 static napi_status napi_set_last_error(napi_env env, napi_status error_code, @@ -31,8 +31,11 @@ napi_status napi_set_last_error(napi_env env, napi_status error_code, static napi_status napi_clear_last_error(napi_env env); struct napi_env__ { - explicit napi_env__(v8::Isolate* _isolate): isolate(_isolate), - has_instance_available(true), last_error() {} + explicit napi_env__(v8::Isolate* _isolate, uv_loop_t *_loop): + isolate(_isolate), + has_instance_available(true), + last_error(), + loop(_loop) {} ~napi_env__() { last_exception.Reset(); has_instance.Reset(); @@ -49,6 +52,7 @@ struct napi_env__ { bool has_instance_available; napi_extended_error_info last_error; int open_handle_scopes = 0; + uv_loop_t* loop = nullptr; }; #define ENV_OBJECT_TEMPLATE(env, prefix, destination, field_count) \ @@ -780,7 +784,7 @@ napi_env GetEnv(v8::Local context) { if (value->IsExternal()) { result = static_cast(value.As()->Value()); } else { - result = new napi_env__(isolate); + result = new napi_env__(isolate, node::GetCurrentEventLoop(isolate)); auto external = v8::External::New(isolate, result); // We must also stop hard if the result of assigning the env to the global @@ -3494,15 +3498,22 @@ napi_status napi_delete_async_work(napi_env env, napi_async_work work) { return napi_clear_last_error(env); } +napi_status napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) { + CHECK_ENV(env); + CHECK_ARG(env, loop); + *loop = env->loop; + return napi_clear_last_error(env); +} + napi_status napi_queue_async_work(napi_env env, napi_async_work work) { CHECK_ENV(env); CHECK_ARG(env, work); - // Consider: Encapsulate the uv_loop_t into an opaque pointer parameter. - // Currently the environment event loop is the same as the UV default loop. - // Someday (if node ever supports multiple isolates), it may be better to get - // the loop from node::Environment::GetCurrent(env->isolate)->event_loop(); - uv_loop_t* event_loop = uv_default_loop(); + napi_status status; + uv_loop_t* event_loop = nullptr; + status = napi_get_uv_event_loop(env, &event_loop); + if (status != napi_ok) + return napi_set_last_error(env, status); uvimpl::Work* w = reinterpret_cast(work); diff --git a/src/node_api.h b/src/node_api.h index a3a07a64673366..8e5eef8a47728f 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -14,6 +14,8 @@ #include #include "node_api_types.h" +struct uv_loop_s; // Forward declaration. + #ifdef _WIN32 #ifdef BUILDING_NODE_EXTENSION #ifdef EXTERNAL_NAPI @@ -581,6 +583,10 @@ NAPI_EXTERN napi_status napi_run_script(napi_env env, napi_value script, napi_value* result); +// Return the current libuv event loop for a given environment +NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env, + struct uv_loop_s** loop); + EXTERN_C_END #endif // SRC_NODE_API_H_ diff --git a/src/node_api_backport.cc b/src/node_api_backport.cc index 655b61da5cf094..52a0653bfcc591 100644 --- a/src/node_api_backport.cc +++ b/src/node_api_backport.cc @@ -71,6 +71,14 @@ CallbackScope::~CallbackScope() { env->tick_callback_function()->Call(env->process_object(), 0, nullptr); } +uv_loop_t *GetCurrentEventLoop(v8::Isolate *isolate) { + HandleScope handle_scope(isolate); + auto context = isolate->GetCurrentContext(); + if (context.IsEmpty()) + return nullptr; + return Environment::GetCurrent(context)->event_loop(); +} + AsyncResource::AsyncResource(v8::Isolate* _isolate, v8::Local _object, char* name) : isolate(_isolate) { diff --git a/src/node_api_backport.h b/src/node_api_backport.h index 89ae33d0861529..968169ef3bd05f 100644 --- a/src/node_api_backport.h +++ b/src/node_api_backport.h @@ -31,6 +31,8 @@ class CallbackScope { Environment::AsyncCallbackScope callback_scope; }; +uv_loop_t *GetCurrentEventLoop(v8::Isolate *isolate); + NODE_EXTERN async_context EmitAsyncInit(v8::Isolate* isolate, v8::Local resource, v8::Local name, diff --git a/test/addons-napi/test_general/test.js b/test/addons-napi/test_general/test.js index 51ce9563a7196e..10b85ef727bb20 100644 --- a/test/addons-napi/test_general/test.js +++ b/test/addons-napi/test_general/test.js @@ -34,7 +34,7 @@ assert.ok(test_general.testGetPrototype(baseObject) !== // test version management funcitons // expected version is currently 1 -assert.strictEqual(test_general.testGetVersion(), 1); +assert.strictEqual(test_general.testGetVersion(), 2); const [ major, minor, patch, release ] = test_general.testGetNodeVersion(); assert.strictEqual(process.version.split('-')[0],