GeezerVison is showing github.com — n-api: use AsyncResource for Work tracking · nodejs/node@a47fe69
File treeExpand file treeCollapse file tree Open diff view settings test/addons-napi/test_async
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/n-api.md +20Lines changed: 20 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -3264,17 +3264,27 @@ callback invocation, even when it was cancelled. | 3264 | 3264 | ### napi_create_async_work | 3265 | 3265 | <!-- YAML | 3266 | 3266 | added: v8.0.0 | | 3267 | +changes: | | 3268 | + - version: REPLACEME | | 3269 | + pr-url: https://github.com/nodejs/node/pull/14697 | | 3270 | + description: Added `async_resource` and `async_resource_name` parameters. | 3267 | 3271 | --> | 3268 | 3272 | ```C | 3269 | 3273 | NAPI_EXTERN | 3270 | 3274 | napi_status napi_create_async_work(napi_env env, | | 3275 | + napi_value async_resource, | | 3276 | + const char* async_resource_name, | 3271 | 3277 | napi_async_execute_callback execute, | 3272 | 3278 | napi_async_complete_callback complete, | 3273 | 3279 | void* data, | 3274 | 3280 | napi_async_work* result); | 3275 | 3281 | ``` | 3276 | 3282 |
| 3277 | 3283 | - `[in] env`: The environment that the API is invoked under. | | 3284 | +- `[in] async_resource`: An optional object associated with the async work | | 3285 | + that will be passed to possible async_hooks [`init` hooks][]. | | 3286 | +- `[in] async_resource_name`: An identifier for the kind of resource that is | | 3287 | +being provided for diagnostic information exposed by the `async_hooks` API. | 3278 | 3288 | - `[in] execute`: The native function which should be called to excute | 3279 | 3289 | the logic asynchronously. | 3280 | 3290 | - `[in] complete`: The native function which will be called when the | @@ -3290,6 +3300,14 @@ This API allocates a work object that is used to execute logic asynchronously. | 3290 | 3300 | It should be freed using [`napi_delete_async_work`][] once the work is no longer | 3291 | 3301 | required. | 3292 | 3302 |
| | 3303 | +`async_resource_name` should be a null-terminated, UTF-8-encoded string. | | 3304 | + | | 3305 | +*Note*: The `async_resource_name` identifier is provided by the user and should | | 3306 | +be representative of the type of async work being performed. It is also | | 3307 | +recommended to apply namespacing to the identifier, e.g. by including the | | 3308 | +module name. See the [`async_hooks` documentation][async_hooks `type`] | | 3309 | +for more information. | | 3310 | + | 3293 | 3311 | ### napi_delete_async_work | 3294 | 3312 | <!-- YAML | 3295 | 3313 | added: v8.0.0 | @@ -3644,3 +3662,5 @@ NAPI_EXTERN napi_status napi_run_script(napi_env env, | 3644 | 3662 | [`napi_wrap`]: #n_api_napi_wrap | 3645 | 3663 |
| 3646 | 3664 | [`process.release`]: process.html#process_process_release | | 3665 | +[`init` hooks]: async_hooks.html#async_hooks_init_asyncid_type_triggerasyncid_resource | | 3666 | +[async_hooks `type`]: async_hooks.html#async_hooks_type |
Collapse file Copy file name to clipboardExpand all lines: src/node_api.cc +27-5Lines changed: 27 additions & 5 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -3251,13 +3251,18 @@ static napi_status ConvertUVErrorCode(int code) { | 3251 | 3251 | } | 3252 | 3252 |
| 3253 | 3253 | // Wrapper around uv_work_t which calls user-provided callbacks. | 3254 | | -class Work { | | 3254 | +class Work : public node::AsyncResource { | 3255 | 3255 | private: | 3256 | 3256 | 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, | 3258 | 3260 | napi_async_complete_callback complete = nullptr, | 3259 | 3261 | void* data = nullptr) | 3260 | | - : _env(env), | | 3262 | + : AsyncResource(env->isolate, | | 3263 | + async_resource, | | 3264 | + async_resource_name), | | 3265 | + _env(env), | 3261 | 3266 | _data(data), | 3262 | 3267 | _execute(execute), | 3263 | 3268 | _complete(complete) { | @@ -3269,10 +3274,13 @@ class Work { | 3269 | 3274 |
| 3270 | 3275 | public: | 3271 | 3276 | static Work* New(napi_env env, | | 3277 | + v8::Local<v8::Object> async_resource, | | 3278 | + const char* async_resource_name, | 3272 | 3279 | napi_async_execute_callback execute, | 3273 | 3280 | napi_async_complete_callback complete, | 3274 | 3281 | void* data) { | 3275 | | - return new Work(env, execute, complete, data); | | 3282 | + return new Work(env, async_resource, async_resource_name, | | 3283 | + execute, complete, data); | 3276 | 3284 | } | 3277 | 3285 |
| 3278 | 3286 | static void Delete(Work* work) { | @@ -3293,6 +3301,7 @@ class Work { | 3293 | 3301 | // Establish a handle scope here so that every callback doesn't have to. | 3294 | 3302 | // Also it is needed for the exception-handling below. | 3295 | 3303 | v8::HandleScope scope(env->isolate); | | 3304 | + CallbackScope callback_scope(work); | 3296 | 3305 |
| 3297 | 3306 | work->_complete(env, ConvertUVErrorCode(status), work->_data); | 3298 | 3307 |
| @@ -3335,6 +3344,8 @@ class Work { | 3335 | 3344 | } while (0) | 3336 | 3345 |
| 3337 | 3346 | napi_status napi_create_async_work(napi_env env, | | 3347 | + napi_value async_resource, | | 3348 | + const char* async_resource_name, | 3338 | 3349 | napi_async_execute_callback execute, | 3339 | 3350 | napi_async_complete_callback complete, | 3340 | 3351 | void* data, | @@ -3343,7 +3354,18 @@ napi_status napi_create_async_work(napi_env env, | 3343 | 3354 | CHECK_ARG(env, execute); | 3344 | 3355 | CHECK_ARG(env, result); | 3345 | 3356 |
| 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); | 3347 | 3369 |
| 3348 | 3370 | *result = reinterpret_cast<napi_async_work>(work); | 3349 | 3371 |
| |
Collapse file Copy file name to clipboardExpand all lines: src/node_api.h +2Lines changed: 2 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -522,6 +522,8 @@ NAPI_EXTERN napi_status napi_get_dataview_info(napi_env env, | 522 | 522 | // Methods to manage simple async operations | 523 | 523 | NAPI_EXTERN | 524 | 524 | napi_status napi_create_async_work(napi_env env, | | 525 | + napi_value async_resource, | | 526 | + const char* async_resource_name, | 525 | 527 | napi_async_execute_callback execute, | 526 | 528 | napi_async_complete_callback complete, | 527 | 529 | void* data, | |
Collapse file Copy file name to clipboard +60Lines changed: 60 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +'use strict'; | | 2 | +const common = require('../../common'); | | 3 | +const assert = require('assert'); | | 4 | +const async_hooks = require('async_hooks'); | | 5 | +const test_async = require(`./build/${common.buildType}/test_async`); | | 6 | + | | 7 | +const events = []; | | 8 | +let testId; | | 9 | +const initAsyncId = async_hooks.executionAsyncId(); | | 10 | + | | 11 | +async_hooks.createHook({ | | 12 | + init(id, provider, triggerAsyncId, resource) { | | 13 | + if (provider === 'TestResource') { | | 14 | + testId = id; | | 15 | + events.push({ type: 'init', id, provider, triggerAsyncId, resource }); | | 16 | + } | | 17 | + }, | | 18 | + before(id) { | | 19 | + if (testId === id) { | | 20 | + events.push({ type: 'before', id }); | | 21 | + } | | 22 | + }, | | 23 | + after(id) { | | 24 | + if (testId === id) { | | 25 | + events.push({ type: 'after', id }); | | 26 | + } | | 27 | + }, | | 28 | + destroy(id) { | | 29 | + if (testId === id) { | | 30 | + events.push({ type: 'destroy', id }); | | 31 | + } | | 32 | + } | | 33 | +}).enable(); | | 34 | + | | 35 | +const resource = { foo: 'foo' }; | | 36 | + | | 37 | +events.push({ type: 'start' }); | | 38 | +test_async.Test(5, resource, common.mustCall(function(err, val) { | | 39 | + assert.strictEqual(err, null); | | 40 | + assert.strictEqual(val, 10); | | 41 | + events.push({ type: 'complete' }); | | 42 | + process.nextTick(common.mustCall()); | | 43 | +})); | | 44 | +events.push({ type: 'scheduled' }); | | 45 | + | | 46 | +process.on('exit', () => { | | 47 | + assert.deepStrictEqual(events, [ | | 48 | + { type: 'start' }, | | 49 | + { type: 'init', | | 50 | + id: testId, | | 51 | + provider: 'TestResource', | | 52 | + triggerAsyncId: initAsyncId, | | 53 | + resource }, | | 54 | + { type: 'scheduled' }, | | 55 | + { type: 'before', id: testId }, | | 56 | + { type: 'complete' }, | | 57 | + { type: 'after', id: testId }, | | 58 | + { type: 'destroy', id: testId } | | 59 | + ]); | | 60 | +}); |
Collapse file Copy file name to clipboardExpand all lines: test/addons-napi/test_async/test.js +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -9,7 +9,7 @@ const testException = 'test_async_cb_exception'; | 9 | 9 | // Exception thrown from async completion callback. | 10 | 10 | // (Tested in a spawned process because the exception is fatal.) | 11 | 11 | if (process.argv[2] === 'child') { | 12 | | - test_async.Test(1, common.mustCall(function() { | | 12 | + test_async.Test(1, {}, common.mustCall(function() { | 13 | 13 | throw new Error(testException); | 14 | 14 | })); | 15 | 15 | return; | @@ -20,7 +20,7 @@ assert.ifError(p.error); | 20 | 20 | assert.ok(p.stderr.toString().includes(testException)); | 21 | 21 |
| 22 | 22 | // 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) { | 24 | 24 | assert.strictEqual(err, null); | 25 | 25 | assert.strictEqual(val, 10); | 26 | 26 | process.nextTick(common.mustCall()); | |
Collapse file Copy file name to clipboardExpand all lines: test/addons-napi/test_async/test_async.cc +18-13Lines changed: 18 additions & 13 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -61,37 +61,40 @@ void Complete(napi_env env, napi_status status, void* data) { | 61 | 61 |
| 62 | 62 | napi_value result; | 63 | 63 | 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)); | 65 | 65 |
| 66 | 66 | NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, c->_callback)); | 67 | 67 | NAPI_CALL_RETURN_VOID(env, napi_delete_async_work(env, c->_request)); | 68 | 68 | } | 69 | 69 |
| 70 | 70 | 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]; | 73 | 73 | napi_value _this; | 74 | 74 | void* data; | 75 | 75 | NAPI_CALL(env, | 76 | 76 | 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."); | 78 | 78 |
| 79 | 79 | napi_valuetype t; | 80 | 80 | NAPI_CALL(env, napi_typeof(env, argv[0], &t)); | 81 | 81 | NAPI_ASSERT(env, t == napi_number, | 82 | 82 | "Wrong first argument, integer expected."); | 83 | 83 | 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)); | 84 | 87 | NAPI_ASSERT(env, t == napi_function, | 85 | | - "Wrong second argument, function expected."); | | 88 | + "Wrong third argument, function expected."); | 86 | 89 |
| 87 | 90 | the_carrier._output = 0; | 88 | 91 |
| 89 | 92 | NAPI_CALL(env, | 90 | 93 | napi_get_value_int32(env, argv[0], &the_carrier._input)); | 91 | 94 | 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)); | 95 | 98 | NAPI_CALL(env, | 96 | 99 | napi_queue_async_work(env, the_carrier._request)); | 97 | 100 |
| @@ -116,7 +119,7 @@ void CancelComplete(napi_env env, napi_status status, void* data) { | 116 | 119 | NAPI_CALL_RETURN_VOID(env, napi_get_global(env, &global)); | 117 | 120 | napi_value result; | 118 | 121 | 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)); | 120 | 123 | } | 121 | 124 |
| 122 | 125 | 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) { | 140 | 143 | // make sure the work we are going to cancel will not be | 141 | 144 | // able to start by using all the threads in the pool | 142 | 145 | 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)); | 145 | 149 | NAPI_CALL(env, napi_queue_async_work(env, async_carrier[i]._request)); | 146 | 150 | } | 147 | 151 |
| @@ -151,8 +155,9 @@ napi_value TestCancel(napi_env env, napi_callback_info info) { | 151 | 155 | // workers above. | 152 | 156 | NAPI_CALL(env, | 153 | 157 | 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)); | 156 | 161 | NAPI_CALL(env, | 157 | 162 | napi_create_reference(env, argv[0], 1, &async_carrier[0]._callback)); | 158 | 163 | NAPI_CALL(env, napi_queue_async_work(env, async_carrier[0]._request)); | |
|