Skip to content

Commit 7588eea

Browse files
addaleaxMylesBorins
authored andcommittedApr 16, 2018
n-api: use AsyncResource for Work tracking
Enable combining N-API async work with async-hooks. Backport-PR-URL: #19447 PR-URL: #14697 Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jason Ginchereau <jasongin@microsoft.com> Reviewed-By: Michael Dawson <mhdawson@ibm.com>
1 parent 676cff4 commit 7588eea

File tree

5 files changed

+69
-20
lines changed

5 files changed

+69
-20
lines changed
 

‎doc/api/n-api.md

+20
Original file line numberDiff line numberDiff line change
@@ -3256,17 +3256,27 @@ callback invocation, even when it was cancelled.
32563256
### napi_create_async_work
32573257
<!-- YAML
32583258
added: v8.0.0
3259+
changes:
3260+
- version: REPLACEME
3261+
pr-url: https://github.com/nodejs/node/pull/14697
3262+
description: Added `async_resource` and `async_resource_name` parameters.
32593263
-->
32603264
```C
32613265
NAPI_EXTERN
32623266
napi_status napi_create_async_work(napi_env env,
3267+
napi_value async_resource,
3268+
const char* async_resource_name,
32633269
napi_async_execute_callback execute,
32643270
napi_async_complete_callback complete,
32653271
void* data,
32663272
napi_async_work* result);
32673273
```
32683274

32693275
- `[in] env`: The environment that the API is invoked under.
3276+
- `[in] async_resource`: An optional object associated with the async work
3277+
that will be passed to possible async_hooks [`init` hooks][].
3278+
- `[in] async_resource_name`: An identifier for the kind of resource that is
3279+
being provided for diagnostic information exposed by the `async_hooks` API.
32703280
- `[in] execute`: The native function which should be called to excute
32713281
the logic asynchronously.
32723282
- `[in] complete`: The native function which will be called when the
@@ -3282,6 +3292,14 @@ This API allocates a work object that is used to execute logic asynchronously.
32823292
It should be freed using [`napi_delete_async_work`][] once the work is no longer
32833293
required.
32843294

3295+
`async_resource_name` should be a null-terminated, UTF-8-encoded string.
3296+
3297+
*Note*: The `async_resource_name` identifier is provided by the user and should
3298+
be representative of the type of async work being performed. It is also
3299+
recommended to apply namespacing to the identifier, e.g. by including the
3300+
module name. See the [`async_hooks` documentation][async_hooks `type`]
3301+
for more information.
3302+
32853303
### napi_delete_async_work
32863304
<!-- YAML
32873305
added: v8.0.0
@@ -3637,3 +3655,5 @@ NAPI_EXTERN napi_status napi_run_script(napi_env env,
36373655
[`napi_wrap`]: #n_api_napi_wrap
36383656

36393657
[`process.release`]: process.html#process_process_release
3658+
[`init` hooks]: async_hooks.html#async_hooks_init_asyncid_type_triggerasyncid_resource
3659+
[async_hooks `type`]: async_hooks.html#async_hooks_type

‎src/node_api.cc

+27-5
Original file line numberDiff line numberDiff line change
@@ -3251,13 +3251,18 @@ static napi_status ConvertUVErrorCode(int code) {
32513251
}
32523252

32533253
// Wrapper around uv_work_t which calls user-provided callbacks.
3254-
class Work {
3254+
class Work : public node::AsyncResource {
32553255
private:
32563256
explicit Work(napi_env env,
3257-
napi_async_execute_callback execute = nullptr,
3257+
v8::Local<v8::Object> async_resource,
3258+
const char* async_resource_name,
3259+
napi_async_execute_callback execute,
32583260
napi_async_complete_callback complete = nullptr,
32593261
void* data = nullptr)
3260-
: _env(env),
3262+
: AsyncResource(env->isolate,
3263+
async_resource,
3264+
async_resource_name),
3265+
_env(env),
32613266
_data(data),
32623267
_execute(execute),
32633268
_complete(complete) {
@@ -3269,10 +3274,13 @@ class Work {
32693274

32703275
public:
32713276
static Work* New(napi_env env,
3277+
v8::Local<v8::Object> async_resource,
3278+
const char* async_resource_name,
32723279
napi_async_execute_callback execute,
32733280
napi_async_complete_callback complete,
32743281
void* data) {
3275-
return new Work(env, execute, complete, data);
3282+
return new Work(env, async_resource, async_resource_name,
3283+
execute, complete, data);
32763284
}
32773285

32783286
static void Delete(Work* work) {
@@ -3293,6 +3301,7 @@ class Work {
32933301
// Establish a handle scope here so that every callback doesn't have to.
32943302
// Also it is needed for the exception-handling below.
32953303
v8::HandleScope scope(env->isolate);
3304+
CallbackScope callback_scope(work);
32963305

32973306
work->_complete(env, ConvertUVErrorCode(status), work->_data);
32983307

@@ -3335,6 +3344,8 @@ class Work {
33353344
} while (0)
33363345

33373346
napi_status napi_create_async_work(napi_env env,
3347+
napi_value async_resource,
3348+
const char* async_resource_name,
33383349
napi_async_execute_callback execute,
33393350
napi_async_complete_callback complete,
33403351
void* data,
@@ -3343,7 +3354,18 @@ napi_status napi_create_async_work(napi_env env,
33433354
CHECK_ARG(env, execute);
33443355
CHECK_ARG(env, result);
33453356

3346-
uvimpl::Work* work = uvimpl::Work::New(env, execute, complete, data);
3357+
v8::Local<v8::Object> resource;
3358+
if (async_resource != nullptr) {
3359+
auto value = v8impl::V8LocalValueFromJsValue(async_resource);
3360+
RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg);
3361+
resource = value.As<v8::Object>();
3362+
} else {
3363+
resource = v8::Object::New(env->isolate);
3364+
}
3365+
3366+
uvimpl::Work* work =
3367+
uvimpl::Work::New(env, resource, async_resource_name,
3368+
execute, complete, data);
33473369

33483370
*result = reinterpret_cast<napi_async_work>(work);
33493371

‎src/node_api.h

+2
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,8 @@ NAPI_EXTERN napi_status napi_get_dataview_info(napi_env env,
524524
// Methods to manage simple async operations
525525
NAPI_EXTERN
526526
napi_status napi_create_async_work(napi_env env,
527+
napi_value async_resource,
528+
const char* async_resource_name,
527529
napi_async_execute_callback execute,
528530
napi_async_complete_callback complete,
529531
void* data,

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const testException = 'test_async_cb_exception';
99
// Exception thrown from async completion callback.
1010
// (Tested in a spawned process because the exception is fatal.)
1111
if (process.argv[2] === 'child') {
12-
test_async.Test(1, common.mustCall(function() {
12+
test_async.Test(1, {}, common.mustCall(function() {
1313
throw new Error(testException);
1414
}));
1515
return;
@@ -20,7 +20,7 @@ assert.ifError(p.error);
2020
assert.ok(p.stderr.toString().includes(testException));
2121

2222
// Successful async execution and completion callback.
23-
test_async.Test(5, common.mustCall(function(err, val) {
23+
test_async.Test(5, {}, common.mustCall(function(err, val) {
2424
assert.strictEqual(err, null);
2525
assert.strictEqual(val, 10);
2626
process.nextTick(common.mustCall());

‎test/addons-napi/test_async/test_async.cc

+18-13
Original file line numberDiff line numberDiff line change
@@ -61,37 +61,40 @@ void Complete(napi_env env, napi_status status, void* data) {
6161

6262
napi_value result;
6363
NAPI_CALL_RETURN_VOID(env,
64-
napi_make_callback(env, global, callback, 2, argv, &result));
64+
napi_call_function(env, global, callback, 2, argv, &result));
6565

6666
NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, c->_callback));
6767
NAPI_CALL_RETURN_VOID(env, napi_delete_async_work(env, c->_request));
6868
}
6969

7070
napi_value Test(napi_env env, napi_callback_info info) {
71-
size_t argc = 2;
72-
napi_value argv[2];
71+
size_t argc = 3;
72+
napi_value argv[3];
7373
napi_value _this;
7474
void* data;
7575
NAPI_CALL(env,
7676
napi_get_cb_info(env, info, &argc, argv, &_this, &data));
77-
NAPI_ASSERT(env, argc >= 2, "Not enough arguments, expected 2.");
77+
NAPI_ASSERT(env, argc >= 3, "Not enough arguments, expected 2.");
7878

7979
napi_valuetype t;
8080
NAPI_CALL(env, napi_typeof(env, argv[0], &t));
8181
NAPI_ASSERT(env, t == napi_number,
8282
"Wrong first argument, integer expected.");
8383
NAPI_CALL(env, napi_typeof(env, argv[1], &t));
84+
NAPI_ASSERT(env, t == napi_object,
85+
"Wrong second argument, object expected.");
86+
NAPI_CALL(env, napi_typeof(env, argv[2], &t));
8487
NAPI_ASSERT(env, t == napi_function,
85-
"Wrong second argument, function expected.");
88+
"Wrong third argument, function expected.");
8689

8790
the_carrier._output = 0;
8891

8992
NAPI_CALL(env,
9093
napi_get_value_int32(env, argv[0], &the_carrier._input));
9194
NAPI_CALL(env,
92-
napi_create_reference(env, argv[1], 1, &the_carrier._callback));
93-
NAPI_CALL(env, napi_create_async_work(
94-
env, Execute, Complete, &the_carrier, &the_carrier._request));
95+
napi_create_reference(env, argv[2], 1, &the_carrier._callback));
96+
NAPI_CALL(env, napi_create_async_work(env, argv[1], "TestResource",
97+
Execute, Complete, &the_carrier, &the_carrier._request));
9598
NAPI_CALL(env,
9699
napi_queue_async_work(env, the_carrier._request));
97100

@@ -116,7 +119,7 @@ void CancelComplete(napi_env env, napi_status status, void* data) {
116119
NAPI_CALL_RETURN_VOID(env, napi_get_global(env, &global));
117120
napi_value result;
118121
NAPI_CALL_RETURN_VOID(env,
119-
napi_make_callback(env, global, callback, 0, nullptr, &result));
122+
napi_call_function(env, global, callback, 0, nullptr, &result));
120123
}
121124

122125
NAPI_CALL_RETURN_VOID(env, napi_delete_async_work(env, c->_request));
@@ -140,8 +143,9 @@ napi_value TestCancel(napi_env env, napi_callback_info info) {
140143
// make sure the work we are going to cancel will not be
141144
// able to start by using all the threads in the pool
142145
for (int i = 1; i < MAX_CANCEL_THREADS; i++) {
143-
NAPI_CALL(env, napi_create_async_work(env, CancelExecute,
144-
BusyCancelComplete, &async_carrier[i], &async_carrier[i]._request));
146+
NAPI_CALL(env, napi_create_async_work(env, nullptr, "TestCancelBusy",
147+
CancelExecute, BusyCancelComplete,
148+
&async_carrier[i], &async_carrier[i]._request));
145149
NAPI_CALL(env, napi_queue_async_work(env, async_carrier[i]._request));
146150
}
147151

@@ -151,8 +155,9 @@ napi_value TestCancel(napi_env env, napi_callback_info info) {
151155
// workers above.
152156
NAPI_CALL(env,
153157
napi_get_cb_info(env, info, &argc, argv, &_this, &data));
154-
NAPI_CALL(env, napi_create_async_work(env, CancelExecute,
155-
CancelComplete, &async_carrier[0], &async_carrier[0]._request));
158+
NAPI_CALL(env, napi_create_async_work(env, nullptr, "TestCancelled",
159+
CancelExecute, CancelComplete,
160+
&async_carrier[0], &async_carrier[0]._request));
156161
NAPI_CALL(env,
157162
napi_create_reference(env, argv[0], 1, &async_carrier[0]._callback));
158163
NAPI_CALL(env, napi_queue_async_work(env, async_carrier[0]._request));

0 commit comments

Comments
 (0)
Please sign in to comment.