Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8d222d4

Browse filesBrowse files
addaleaxMylesBorins
authored andcommitted
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. PR-URL: #17109 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
1 parent 84b7070 commit 8d222d4
Copy full SHA for 8d222d4

File tree

Expand file treeCollapse file tree

7 files changed

+134
-10
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

7 files changed

+134
-10
lines changed
Open diff view settings
Collapse file

‎doc/api/n-api.md‎

Copy file name to clipboardExpand all lines: doc/api/n-api.md
+17Lines changed: 17 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -3686,6 +3686,23 @@ NAPI_EXTERN napi_status napi_run_script(napi_env env,
36863686
- `[in] script`: A JavaScript string containing the script to execute.
36873687
- `[out] result`: The value resulting from having executed the script.
36883688

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

‎src/node_api.cc‎

Copy file name to clipboardExpand all lines: src/node_api.cc
+19-9Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#include "node_api.h"
1919
#include "node_internals.h"
2020

21-
#define NAPI_VERSION 1
21+
#define NAPI_VERSION 2
2222

2323
static
2424
napi_status napi_set_last_error(napi_env env, napi_status error_code,
@@ -28,8 +28,10 @@ static
2828
napi_status napi_clear_last_error(napi_env env);
2929

3030
struct napi_env__ {
31-
explicit napi_env__(v8::Isolate* _isolate): isolate(_isolate),
32-
last_error() {}
31+
explicit napi_env__(v8::Isolate* _isolate, uv_loop_t* _loop)
32+
: isolate(_isolate),
33+
last_error(),
34+
loop(_loop) {}
3335
~napi_env__() {
3436
last_exception.Reset();
3537
wrap_template.Reset();
@@ -43,6 +45,7 @@ struct napi_env__ {
4345
v8::Persistent<v8::ObjectTemplate> accessor_data_template;
4446
napi_extended_error_info last_error;
4547
int open_handle_scopes = 0;
48+
uv_loop_t* loop = nullptr;
4649
};
4750

4851
#define ENV_OBJECT_TEMPLATE(env, prefix, destination, field_count) \
@@ -771,7 +774,7 @@ napi_env GetEnv(v8::Local<v8::Context> context) {
771774
if (value->IsExternal()) {
772775
result = static_cast<napi_env>(value.As<v8::External>()->Value());
773776
} else {
774-
result = new napi_env__(isolate);
777+
result = new napi_env__(isolate, node::GetCurrentEventLoop(isolate));
775778
auto external = v8::External::New(isolate, result);
776779

777780
// We must also stop hard if the result of assigning the env to the global
@@ -3401,15 +3404,22 @@ napi_status napi_delete_async_work(napi_env env, napi_async_work work) {
34013404
return napi_clear_last_error(env);
34023405
}
34033406

3407+
napi_status napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) {
3408+
CHECK_ENV(env);
3409+
CHECK_ARG(env, loop);
3410+
*loop = env->loop;
3411+
return napi_clear_last_error(env);
3412+
}
3413+
34043414
napi_status napi_queue_async_work(napi_env env, napi_async_work work) {
34053415
CHECK_ENV(env);
34063416
CHECK_ARG(env, work);
34073417

3408-
// Consider: Encapsulate the uv_loop_t into an opaque pointer parameter.
3409-
// Currently the environment event loop is the same as the UV default loop.
3410-
// Someday (if node ever supports multiple isolates), it may be better to get
3411-
// the loop from node::Environment::GetCurrent(env->isolate)->event_loop();
3412-
uv_loop_t* event_loop = uv_default_loop();
3418+
napi_status status;
3419+
uv_loop_t* event_loop = nullptr;
3420+
status = napi_get_uv_event_loop(env, &event_loop);
3421+
if (status != napi_ok)
3422+
return napi_set_last_error(env, status);
34133423

34143424
uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
34153425

Collapse file

‎src/node_api.h‎

Copy file name to clipboardExpand all lines: src/node_api.h
+6Lines changed: 6 additions & 0 deletions
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_
Collapse file

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

Copy file name to clipboardExpand all lines: test/addons-napi/test_general/test.js
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ assert.ok(test_general.testGetPrototype(baseObject) !==
3434

3535
// test version management functions
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],
Collapse file
+8Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"targets": [
3+
{
4+
"target_name": "test_uv_loop",
5+
"sources": [ "test_uv_loop.cc" ]
6+
}
7+
]
8+
}
Collapse file
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
'use strict';
2+
const common = require('../../common');
3+
const { SetImmediate } = require(`./build/${common.buildType}/test_uv_loop`);
4+
5+
SetImmediate(common.mustCall());
Collapse file
+78Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#include <node_api.h>
2+
#include <uv.h>
3+
#include <utility>
4+
#include <memory>
5+
#include <assert.h>
6+
#include "../common.h"
7+
8+
template <typename T>
9+
void* SetImmediate(napi_env env, T&& cb) {
10+
T* ptr = new T(std::move(cb));
11+
uv_loop_t* loop = nullptr;
12+
uv_check_t* check = new uv_check_t;
13+
check->data = ptr;
14+
NAPI_ASSERT(env,
15+
napi_get_uv_event_loop(env, &loop) == napi_ok,
16+
"can get event loop");
17+
uv_check_init(loop, check);
18+
uv_check_start(check, [](uv_check_t* check) {
19+
std::unique_ptr<T> ptr {static_cast<T*>(check->data)};
20+
T cb = std::move(*ptr);
21+
uv_close(reinterpret_cast<uv_handle_t*>(check), [](uv_handle_t* handle) {
22+
delete reinterpret_cast<uv_check_t*>(handle);
23+
});
24+
25+
assert(cb() != nullptr);
26+
});
27+
return nullptr;
28+
}
29+
30+
static char dummy;
31+
32+
napi_value SetImmediateBinding(napi_env env, napi_callback_info info) {
33+
size_t argc = 1;
34+
napi_value argv[1];
35+
napi_value _this;
36+
void* data;
37+
NAPI_CALL(env,
38+
napi_get_cb_info(env, info, &argc, argv, &_this, &data));
39+
NAPI_ASSERT(env, argc >= 1, "Not enough arguments, expected 1.");
40+
41+
napi_valuetype t;
42+
NAPI_CALL(env, napi_typeof(env, argv[0], &t));
43+
NAPI_ASSERT(env, t == napi_function,
44+
"Wrong first argument, function expected.");
45+
46+
napi_ref cbref;
47+
NAPI_CALL(env,
48+
napi_create_reference(env, argv[0], 1, &cbref));
49+
50+
SetImmediate(env, [=]() -> char* {
51+
napi_value undefined;
52+
napi_value callback;
53+
napi_handle_scope scope;
54+
NAPI_CALL(env, napi_open_handle_scope(env, &scope));
55+
NAPI_CALL(env, napi_get_undefined(env, &undefined));
56+
NAPI_CALL(env, napi_get_reference_value(env, cbref, &callback));
57+
NAPI_CALL(env, napi_delete_reference(env, cbref));
58+
NAPI_CALL(env,
59+
napi_call_function(env, undefined, callback, 0, nullptr, nullptr));
60+
NAPI_CALL(env, napi_close_handle_scope(env, scope));
61+
return &dummy;
62+
});
63+
64+
return nullptr;
65+
}
66+
67+
napi_value Init(napi_env env, napi_value exports) {
68+
napi_property_descriptor properties[] = {
69+
DECLARE_NAPI_PROPERTY("SetImmediate", SetImmediateBinding)
70+
};
71+
72+
NAPI_CALL(env, napi_define_properties(
73+
env, exports, sizeof(properties) / sizeof(*properties), properties));
74+
75+
return exports;
76+
}
77+
78+
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.