Skip to content

Commit f4391b9

Browse files
addaleaxMylesBorins
authored andcommittedApr 16, 2018
n-api: add helper for addons to get the event loop
Add a utility functions for addons to use when they need a reference to the current event loop. While the libuv API is not directly part of N-API, it provides a quite stable C API as well, and is tightly integrated with Node itself. As a particular use case, without access to the event loop it is hard to do something interesting from inside a N-API finalizer function, since calls into JS and therefore virtually all other N-API functions are not allowed. Backport-PR-URL: #19447 PR-URL: #17109 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
1 parent 35dc8ba commit f4391b9

File tree

6 files changed

+54
-10
lines changed

6 files changed

+54
-10
lines changed
 

‎doc/api/n-api.md

+17
Original file line numberDiff line numberDiff line change
@@ -3684,6 +3684,23 @@ NAPI_EXTERN napi_status napi_run_script(napi_env env,
36843684
- `[in] script`: A JavaScript string containing the script to execute.
36853685
- `[out] result`: The value resulting from having executed the script.
36863686

3687+
## libuv event loop
3688+
3689+
N-API provides a function for getting the current event loop associated with
3690+
a specific `napi_env`.
3691+
3692+
### napi_get_uv_event_loop
3693+
<!-- YAML
3694+
added: REPLACEME
3695+
-->
3696+
```C
3697+
NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env,
3698+
uv_loop_t** loop);
3699+
```
3700+
3701+
- `[in] env`: The environment that the API is invoked under.
3702+
- `[out] loop`: The current libuv loop instance.
3703+
36873704
[Promises]: #n_api_promises
36883705
[Simple Asynchronous Operations]: #n_api_simple_asynchronous_operations
36893706
[Custom Asynchronous Operations]: #n_api_custom_asynchronous_operations

‎src/node_api.cc

+20-9
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
#include "node_api_backport.h"
2323
#include "util.h"
2424

25-
#define NAPI_VERSION 1
25+
#define NAPI_VERSION 2
2626

2727
static
2828
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,
3131
static napi_status napi_clear_last_error(napi_env env);
3232

3333
struct napi_env__ {
34-
explicit napi_env__(v8::Isolate* _isolate): isolate(_isolate),
35-
has_instance_available(true), last_error() {}
34+
explicit napi_env__(v8::Isolate* _isolate, uv_loop_t *_loop):
35+
isolate(_isolate),
36+
has_instance_available(true),
37+
last_error(),
38+
loop(_loop) {}
3639
~napi_env__() {
3740
last_exception.Reset();
3841
has_instance.Reset();
@@ -49,6 +52,7 @@ struct napi_env__ {
4952
bool has_instance_available;
5053
napi_extended_error_info last_error;
5154
int open_handle_scopes = 0;
55+
uv_loop_t* loop = nullptr;
5256
};
5357

5458
#define ENV_OBJECT_TEMPLATE(env, prefix, destination, field_count) \
@@ -780,7 +784,7 @@ napi_env GetEnv(v8::Local<v8::Context> context) {
780784
if (value->IsExternal()) {
781785
result = static_cast<napi_env>(value.As<v8::External>()->Value());
782786
} else {
783-
result = new napi_env__(isolate);
787+
result = new napi_env__(isolate, node::GetCurrentEventLoop(isolate));
784788
auto external = v8::External::New(isolate, result);
785789

786790
// 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) {
34943498
return napi_clear_last_error(env);
34953499
}
34963500

3501+
napi_status napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) {
3502+
CHECK_ENV(env);
3503+
CHECK_ARG(env, loop);
3504+
*loop = env->loop;
3505+
return napi_clear_last_error(env);
3506+
}
3507+
34973508
napi_status napi_queue_async_work(napi_env env, napi_async_work work) {
34983509
CHECK_ENV(env);
34993510
CHECK_ARG(env, work);
35003511

3501-
// Consider: Encapsulate the uv_loop_t into an opaque pointer parameter.
3502-
// Currently the environment event loop is the same as the UV default loop.
3503-
// Someday (if node ever supports multiple isolates), it may be better to get
3504-
// the loop from node::Environment::GetCurrent(env->isolate)->event_loop();
3505-
uv_loop_t* event_loop = uv_default_loop();
3512+
napi_status status;
3513+
uv_loop_t* event_loop = nullptr;
3514+
status = napi_get_uv_event_loop(env, &event_loop);
3515+
if (status != napi_ok)
3516+
return napi_set_last_error(env, status);
35063517

35073518
uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
35083519

‎src/node_api.h

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include <stdbool.h>
1515
#include "node_api_types.h"
1616

17+
struct uv_loop_s; // Forward declaration.
18+
1719
#ifdef _WIN32
1820
#ifdef BUILDING_NODE_EXTENSION
1921
#ifdef EXTERNAL_NAPI
@@ -581,6 +583,10 @@ NAPI_EXTERN napi_status napi_run_script(napi_env env,
581583
napi_value script,
582584
napi_value* result);
583585

586+
// Return the current libuv event loop for a given environment
587+
NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env,
588+
struct uv_loop_s** loop);
589+
584590
EXTERN_C_END
585591

586592
#endif // SRC_NODE_API_H_

‎src/node_api_backport.cc

+8
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ CallbackScope::~CallbackScope() {
7171
env->tick_callback_function()->Call(env->process_object(), 0, nullptr);
7272
}
7373

74+
uv_loop_t *GetCurrentEventLoop(v8::Isolate *isolate) {
75+
HandleScope handle_scope(isolate);
76+
auto context = isolate->GetCurrentContext();
77+
if (context.IsEmpty())
78+
return nullptr;
79+
return Environment::GetCurrent(context)->event_loop();
80+
}
81+
7482
AsyncResource::AsyncResource(v8::Isolate* _isolate,
7583
v8::Local<v8::Object> _object,
7684
char* name) : isolate(_isolate) {

‎src/node_api_backport.h

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ class CallbackScope {
3131
Environment::AsyncCallbackScope callback_scope;
3232
};
3333

34+
uv_loop_t *GetCurrentEventLoop(v8::Isolate *isolate);
35+
3436
NODE_EXTERN async_context EmitAsyncInit(v8::Isolate* isolate,
3537
v8::Local<v8::Object> resource,
3638
v8::Local<v8::String> name,

‎test/addons-napi/test_general/test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ assert.ok(test_general.testGetPrototype(baseObject) !==
3434

3535
// test version management funcitons
3636
// expected version is currently 1
37-
assert.strictEqual(test_general.testGetVersion(), 1);
37+
assert.strictEqual(test_general.testGetVersion(), 2);
3838

3939
const [ major, minor, patch, release ] = test_general.testGetNodeVersion();
4040
assert.strictEqual(process.version.split('-')[0],

0 commit comments

Comments
 (0)
Please sign in to comment.