summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/api/n-api.md214
-rw-r--r--src/node_api.cc61
-rw-r--r--src/node_api.h24
-rw-r--r--src/node_api_types.h1
-rw-r--r--test/addons-napi/test_make_callback/binding.cc12
-rw-r--r--test/addons-napi/test_make_callback/test.js39
-rw-r--r--test/addons-napi/test_make_callback_recurse/binding.cc4
7 files changed, 259 insertions, 96 deletions
diff --git a/doc/api/n-api.md b/doc/api/n-api.md
index 4d95ad1ab0..d039e92589 100644
--- a/doc/api/n-api.md
+++ b/doc/api/n-api.md
@@ -41,7 +41,8 @@ The documentation for N-API is structured as follows:
* [Working with JavaScript Properties][]
* [Working with JavaScript Functions][]
* [Object Wrap][]
-* [Asynchronous Operations][]
+* [Simple Asynchronous Operations][]
+* [Custom Asynchronous Operations][]
* [Promises][]
* [Script Execution][]
@@ -264,7 +265,7 @@ It is intended only for logging purposes.
added: v8.0.0
-->
```C
-NAPI_EXTERN napi_status
+napi_status
napi_get_last_error_info(napi_env env,
const napi_extended_error_info** result);
```
@@ -515,8 +516,8 @@ This API returns a JavaScript RangeError with the text provided.
added: v8.0.0
-->
```C
-NAPI_EXTERN napi_status napi_get_and_clear_last_exception(napi_env env,
- napi_value* result);
+napi_status napi_get_and_clear_last_exception(napi_env env,
+ napi_value* result);
```
- `[in] env`: The environment that the API is invoked under.
@@ -531,7 +532,7 @@ This API returns true if an exception is pending.
added: v8.0.0
-->
```C
-NAPI_EXTERN napi_status napi_is_exception_pending(napi_env env, bool* result);
+napi_status napi_is_exception_pending(napi_env env, bool* result);
```
- `[in] env`: The environment that the API is invoked under.
@@ -551,7 +552,7 @@ thrown to immediately terminate the process.
added: v8.2.0
-->
```C
-NAPI_EXTERN NAPI_NO_RETURN void napi_fatal_error(const char* location, const char* message);
+NAPI_NO_RETURN void napi_fatal_error(const char* location, const char* message);
```
- `[in] location`: Optional location at which the error occurred.
@@ -718,10 +719,10 @@ reverse order from which they were created.
added: v8.0.0
-->
```C
-NAPI_EXTERN napi_status napi_escape_handle(napi_env env,
- napi_escapable_handle_scope scope,
- napi_value escapee,
- napi_value* result);
+napi_status napi_escape_handle(napi_env env,
+ napi_escapable_handle_scope scope,
+ napi_value escapee,
+ napi_value* result);
```
- `[in] env`: The environment that the API is invoked under.
@@ -1478,10 +1479,10 @@ of the ECMAScript Language Specification.
added: v8.0.0
-->
```C
-NAPI_EXTERN napi_status napi_create_string_latin1(napi_env env,
- const char* str,
- size_t length,
- napi_value* result);
+napi_status napi_create_string_latin1(napi_env env,
+ const char* str,
+ size_t length,
+ napi_value* result);
```
- `[in] env`: The environment that the API is invoked under.
@@ -1811,11 +1812,11 @@ JavaScript Number
added: v8.0.0
-->
```C
-NAPI_EXTERN napi_status napi_get_value_string_latin1(napi_env env,
- napi_value value,
- char* buf,
- size_t bufsize,
- size_t* result)
+napi_status napi_get_value_string_latin1(napi_env env,
+ napi_value value,
+ char* buf,
+ size_t bufsize,
+ size_t* result)
```
- `[in] env`: The environment that the API is invoked under.
@@ -2790,8 +2791,8 @@ in as arguments to the function.
Returns `napi_ok` if the API succeeded.
This method allows a JavaScript function object to be called from a native
-add-on. This is an primary mechanism of calling back *from* the add-on's
-native code *into* JavaScript. For special cases like calling into JavaScript
+add-on. This is the primary mechanism of calling back *from* the add-on's
+native code *into* JavaScript. For the special case of calling into JavaScript
after an async operation, see [`napi_make_callback`][].
A sample use case might look as follows. Consider the following JavaScript
@@ -3003,39 +3004,6 @@ status = napi_new_instance(env, constructor, argc, argv, &value);
Returns `napi_ok` if the API succeeded.
-### *napi_make_callback*
-<!-- YAML
-added: v8.0.0
--->
-```C
-napi_status napi_make_callback(napi_env env,
- napi_value recv,
- napi_value func,
- int argc,
- const napi_value* argv,
- napi_value* result)
-```
-
-- `[in] env`: The environment that the API is invoked under.
-- `[in] recv`: The `this` object passed to the called function.
-- `[in] func`: `napi_value` representing the JavaScript function
-to be invoked.
-- `[in] argc`: The count of elements in the `argv` array.
-- `[in] argv`: Array of JavaScript values as `napi_value`
-representing the arguments to the function.
-- `[out] result`: `napi_value` representing the JavaScript object returned.
-
-Returns `napi_ok` if the API succeeded.
-
-This method allows a JavaScript function object to be called from a native
-add-on. This API is similar to `napi_call_function`. However, it is used to call
-*from* native code back *into* JavaScript *after* returning from an async
-operation (when there is no other script on the stack). It is a fairly simple
-wrapper around `node::MakeCallback`.
-
-For an example on how to use `napi_make_callback`, see the section on
-[Asynchronous Operations][].
-
## Object Wrap
N-API offers a way to "wrap" C++ classes and instances so that the class
@@ -3214,7 +3182,7 @@ restoring the JavaScript object's prototype chain. If a finalize callback was
associated with the wrapping, it will no longer be called when the JavaScript
object becomes garbage-collected.
-## Asynchronous Operations
+## Simple Asynchronous Operations
Addon modules often need to leverage async helpers from libuv as part of their
implementation. This allows them to schedule work to be executed asynchronously
@@ -3250,8 +3218,8 @@ Once created the async worker can be queued
for execution using the [`napi_queue_async_work`][] function:
```C
-NAPI_EXTERN napi_status napi_queue_async_work(napi_env env,
- napi_async_work work);
+napi_status napi_queue_async_work(napi_env env,
+ napi_async_work work);
```
[`napi_cancel_async_work`][] can be used if the work needs
@@ -3271,7 +3239,6 @@ changes:
description: Added `async_resource` and `async_resource_name` parameters.
-->
```C
-NAPI_EXTERN
napi_status napi_create_async_work(napi_env env,
napi_value async_resource,
napi_value async_resource_name,
@@ -3314,8 +3281,8 @@ for more information.
added: v8.0.0
-->
```C
-NAPI_EXTERN napi_status napi_delete_async_work(napi_env env,
- napi_async_work work);
+napi_status napi_delete_async_work(napi_env env,
+ napi_async_work work);
```
- `[in] env`: The environment that the API is invoked under.
@@ -3330,8 +3297,8 @@ This API frees a previously allocated work object.
added: v8.0.0
-->
```C
-NAPI_EXTERN napi_status napi_queue_async_work(napi_env env,
- napi_async_work work);
+napi_status napi_queue_async_work(napi_env env,
+ napi_async_work work);
```
- `[in] env`: The environment that the API is invoked under.
@@ -3347,8 +3314,8 @@ for execution.
added: v8.0.0
-->
```C
-NAPI_EXTERN napi_status napi_cancel_async_work(napi_env env,
- napi_async_work work);
+napi_status napi_cancel_async_work(napi_env env,
+ napi_async_work work);
```
- `[in] env`: The environment that the API is invoked under.
@@ -3363,6 +3330,93 @@ the `complete` callback will be invoked with a status value of
`napi_cancelled`. The work should not be deleted before the `complete`
callback invocation, even if it has been successfully cancelled.
+## Custom Asynchronous Operations
+The simple asynchronous work APIs above may not be appropriate for every
+scenario, because with those the async execution still happens on the main
+event loop. When using any other async mechanism, the following APIs are
+necessary to ensure an async operation is properly tracked by the runtime.
+
+### *napi_async_init**
+<!-- YAML
+added: REPLACEME
+-->
+```C
+napi_status napi_async_init(napi_env env,
+ napi_value async_resource,
+ napi_value async_resource_name,
+ napi_async_context* result)
+```
+
+- `[in] env`: The environment that the API is invoked under.
+- `[in] async_resource`: An optional object associated with the async work
+ that will be passed to possible `async_hooks` [`init` hooks][].
+- `[in] async_resource_name`: Required identifier for the kind of resource
+ that is being provided for diagnostic information exposed by the
+ `async_hooks` API.
+- `[out] result`: The initialized async context.
+
+Returns `napi_ok` if the API succeeded.
+
+### *napi_async_destroy**
+<!-- YAML
+added: REPLACEME
+-->
+```C
+napi_status napi_async_destroy(napi_env env,
+ napi_async_context async_context);
+```
+
+- `[in] env`: The environment that the API is invoked under.
+- `[in] async_context`: The async context to be destroyed.
+
+Returns `napi_ok` if the API succeeded.
+
+### *napi_make_callback*
+<!-- YAML
+added: v8.0.0
+changes:
+ - version: REPLACEME
+ description: Added `async_context` parameter.
+-->
+```C
+napi_status napi_make_callback(napi_env env,
+ napi_async_context async_context,
+ napi_value recv,
+ napi_value func,
+ int argc,
+ const napi_value* argv,
+ napi_value* result)
+```
+
+- `[in] env`: The environment that the API is invoked under.
+- `[in] async_context`: Context for the async operation that is
+ invoking the callback. This should normally be a value previously
+ obtained from [`napi_async_init`][]. However `NULL` is also allowed,
+ which indicates the current async context (if any) is to be used
+ for the callback.
+- `[in] recv`: The `this` object passed to the called function.
+- `[in] func`: `napi_value` representing the JavaScript function
+to be invoked.
+- `[in] argc`: The count of elements in the `argv` array.
+- `[in] argv`: Array of JavaScript values as `napi_value`
+representing the arguments to the function.
+- `[out] result`: `napi_value` representing the JavaScript object returned.
+
+Returns `napi_ok` if the API succeeded.
+
+This method allows a JavaScript function object to be called from a native
+add-on. This API is similar to `napi_call_function`. However, it is used to call
+*from* native code back *into* JavaScript *after* returning from an async
+operation (when there is no other script on the stack). It is a fairly simple
+wrapper around `node::MakeCallback`.
+
+Note it is *not* necessary to use `napi_make_callback` from within a
+`napi_async_complete_callback`; in that situation the callback's async
+context has already been set up, so a direct call to `napi_call_function`
+is sufficient and appropriate. Use of the `napi_make_callback` function
+may be required when implementing custom async behavior that does not use
+`napi_create_async_work`.
+
## Version Management
### napi_get_node_version
@@ -3378,7 +3432,6 @@ typedef struct {
const char* release;
} napi_node_version;
-NAPI_EXTERN
napi_status napi_get_node_version(napi_env env,
const napi_node_version** version);
```
@@ -3399,8 +3452,8 @@ The returned buffer is statically allocated and does not need to be freed.
added: v8.0.0
-->
```C
-NAPI_EXTERN napi_status napi_get_version(napi_env env,
- uint32_t* result);
+napi_status napi_get_version(napi_env env,
+ uint32_t* result);
```
- `[in] env`: The environment that the API is invoked under.
@@ -3508,9 +3561,9 @@ deferred = NULL;
added: v8.5.0
-->
```C
-NAPI_EXTERN napi_status napi_create_promise(napi_env env,
- napi_deferred* deferred,
- napi_value* promise);
+napi_status napi_create_promise(napi_env env,
+ napi_deferred* deferred,
+ napi_value* promise);
```
- `[in] env`: The environment that the API is invoked under.
@@ -3528,9 +3581,9 @@ This API creates a deferred object and a JavaScript promise.
added: v8.5.0
-->
```C
-NAPI_EXTERN napi_status napi_resolve_deferred(napi_env env,
- napi_deferred deferred,
- napi_value resolution);
+napi_status napi_resolve_deferred(napi_env env,
+ napi_deferred deferred,
+ napi_value resolution);
```
- `[in] env`: The environment that the API is invoked under.
@@ -3551,9 +3604,9 @@ The deferred object is freed upon successful completion.
added: v8.5.0
-->
```C
-NAPI_EXTERN napi_status napi_reject_deferred(napi_env env,
- napi_deferred deferred,
- napi_value rejection);
+napi_status napi_reject_deferred(napi_env env,
+ napi_deferred deferred,
+ napi_value rejection);
```
- `[in] env`: The environment that the API is invoked under.
@@ -3574,9 +3627,9 @@ The deferred object is freed upon successful completion.
added: v8.5.0
-->
```C
-NAPI_EXTERN napi_status napi_is_promise(napi_env env,
- napi_value promise,
- bool* is_promise);
+napi_status napi_is_promise(napi_env env,
+ napi_value promise,
+ bool* is_promise);
```
- `[in] env`: The environment that the API is invoked under.
@@ -3604,7 +3657,8 @@ NAPI_EXTERN napi_status napi_run_script(napi_env env,
- `[out] result`: The value resulting from having executed the script.
[Promises]: #n_api_promises
-[Asynchronous Operations]: #n_api_asynchronous_operations
+[Simple Asynchronous Operations]: #n_api_asynchronous_operations
+[Custom Asynchronous Operations]: #n_api_custom_asynchronous_operations
[Basic N-API Data Types]: #n_api_basic_n_api_data_types
[ECMAScript Language Specification]: https://tc39.github.io/ecma262/
[Error Handling]: #n_api_error_handling
diff --git a/src/node_api.cc b/src/node_api.cc
index a8b449ece5..ef51599648 100644
--- a/src/node_api.cc
+++ b/src/node_api.cc
@@ -2765,7 +2765,52 @@ napi_status napi_instanceof(napi_env env,
return GET_RETURN_STATUS(env);
}
+napi_status napi_async_init(napi_env env,
+ napi_value async_resource,
+ napi_value async_resource_name,
+ napi_async_context* result) {
+ CHECK_ENV(env);
+ CHECK_ARG(env, async_resource_name);
+ CHECK_ARG(env, result);
+
+ v8::Isolate* isolate = env->isolate;
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+ v8::Local<v8::Object> v8_resource;
+ if (async_resource != nullptr) {
+ CHECK_TO_OBJECT(env, context, v8_resource, async_resource);
+ } else {
+ v8_resource = v8::Object::New(isolate);
+ }
+
+ v8::Local<v8::String> v8_resource_name;
+ CHECK_TO_STRING(env, context, v8_resource_name, async_resource_name);
+
+ // TODO(jasongin): Consider avoiding allocation here by using
+ // a tagged pointer with 2×31 bit fields instead.
+ node::async_context* async_context = new node::async_context();
+
+ *async_context = node::EmitAsyncInit(isolate, v8_resource, v8_resource_name);
+ *result = reinterpret_cast<napi_async_context>(async_context);
+
+ return napi_clear_last_error(env);
+}
+
+napi_status napi_async_destroy(napi_env env,
+ napi_async_context async_context) {
+ CHECK_ENV(env);
+ CHECK_ARG(env, async_context);
+
+ v8::Isolate* isolate = env->isolate;
+ node::async_context* node_async_context =
+ reinterpret_cast<node::async_context*>(async_context);
+ node::EmitAsyncDestroy(isolate, *node_async_context);
+
+ return napi_clear_last_error(env);
+}
+
napi_status napi_make_callback(napi_env env,
+ napi_async_context async_context,
napi_value recv,
napi_value func,
size_t argc,
@@ -2786,12 +2831,22 @@ napi_status napi_make_callback(napi_env env,
v8::Local<v8::Function> v8func;
CHECK_TO_FUNCTION(env, v8func, func);
- v8::Local<v8::Value> callback_result = node::MakeCallback(
+ node::async_context* node_async_context =
+ reinterpret_cast<node::async_context*>(async_context);
+ if (node_async_context == nullptr) {
+ static node::async_context empty_context = { 0, 0 };
+ node_async_context = &empty_context;
+ }
+
+ v8::MaybeLocal<v8::Value> callback_result = node::MakeCallback(
isolate, v8recv, v8func, argc,
- reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)));
+ reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)),
+ *node_async_context);
+ CHECK_MAYBE_EMPTY(env, callback_result, napi_generic_failure);
if (result != nullptr) {
- *result = v8impl::JsValueFromV8LocalValue(callback_result);
+ *result = v8impl::JsValueFromV8LocalValue(
+ callback_result.ToLocalChecked());
}
return GET_RETURN_STATUS(env);
diff --git a/src/node_api.h b/src/node_api.h
index cdc3a6bdb7..c7d5e746f1 100644
--- a/src/node_api.h
+++ b/src/node_api.h
@@ -318,14 +318,6 @@ NAPI_EXTERN napi_status napi_instanceof(napi_env env,
napi_value constructor,
bool* result);
-// Napi version of node::MakeCallback(...)
-NAPI_EXTERN napi_status napi_make_callback(napi_env env,
- napi_value recv,
- napi_value func,
- size_t argc,
- const napi_value* argv,
- napi_value* result);
-
// Methods to work with napi_callbacks
// Gets all callback info in a single call. (Ugly, but faster.)
@@ -535,6 +527,22 @@ NAPI_EXTERN napi_status napi_queue_async_work(napi_env env,
NAPI_EXTERN napi_status napi_cancel_async_work(napi_env env,
napi_async_work work);
+// Methods for custom handling of async operations
+NAPI_EXTERN napi_status napi_async_init(napi_env env,
+ napi_value async_resource,
+ napi_value async_resource_name,
+ napi_async_context* result);
+
+NAPI_EXTERN napi_status napi_async_destroy(napi_env env,
+ napi_async_context async_context);
+
+NAPI_EXTERN napi_status napi_make_callback(napi_env env,
+ napi_async_context async_context,
+ napi_value recv,
+ napi_value func,
+ size_t argc,
+ const napi_value* argv,
+ napi_value* result);
// version management
NAPI_EXTERN napi_status napi_get_version(napi_env env, uint32_t* result);
diff --git a/src/node_api_types.h b/src/node_api_types.h
index ac8482bf9d..574cb6ff98 100644
--- a/src/node_api_types.h
+++ b/src/node_api_types.h
@@ -16,6 +16,7 @@ typedef struct napi_ref__ *napi_ref;
typedef struct napi_handle_scope__ *napi_handle_scope;
typedef struct napi_escapable_handle_scope__ *napi_escapable_handle_scope;
typedef struct napi_callback_info__ *napi_callback_info;
+typedef struct napi_async_context__ *napi_async_context;
typedef struct napi_async_work__ *napi_async_work;
typedef struct napi_deferred__ *napi_deferred;
diff --git a/test/addons-napi/test_make_callback/binding.cc b/test/addons-napi/test_make_callback/binding.cc
index 2f52112e74..df2c4dc672 100644
--- a/test/addons-napi/test_make_callback/binding.cc
+++ b/test/addons-napi/test_make_callback/binding.cc
@@ -24,14 +24,22 @@ napi_value MakeCallback(napi_env env, napi_callback_info info) {
NAPI_CALL(env, napi_typeof(env, func, &func_type));
+ napi_value resource_name;
+ NAPI_CALL(env, napi_create_string_utf8(env, "test", -1, &resource_name));
+
+ napi_async_context context;
+ NAPI_CALL(env, napi_async_init(env, func, resource_name, &context));
+
napi_value result;
if (func_type == napi_function) {
- NAPI_CALL(env,
- napi_make_callback(env, recv, func, argv.size(), argv.data(), &result));
+ NAPI_CALL(env, napi_make_callback(
+ env, context, recv, func, argv.size(), argv.data(), &result));
} else {
NAPI_ASSERT(env, false, "Unexpected argument type");
}
+ NAPI_CALL(env, napi_async_destroy(env, context));
+
return result;
}
diff --git a/test/addons-napi/test_make_callback/test.js b/test/addons-napi/test_make_callback/test.js
index c4f24872bd..0e94caf1d9 100644
--- a/test/addons-napi/test_make_callback/test.js
+++ b/test/addons-napi/test_make_callback/test.js
@@ -2,12 +2,12 @@
const common = require('../../common');
const assert = require('assert');
+const async_hooks = require('async_hooks');
const vm = require('vm');
const binding = require(`./build/${common.buildType}/binding`);
const makeCallback = binding.makeCallback;
function myMultiArgFunc(arg1, arg2, arg3) {
- console.log(`MyFunc was called with ${arguments.length} arguments`);
assert.strictEqual(arg1, 1);
assert.strictEqual(arg2, 2);
assert.strictEqual(arg3, 3);
@@ -81,3 +81,40 @@ function endpoint($Object) {
}
assert.strictEqual(Object, makeCallback(process, forward, endpoint));
+
+// Check async hooks integration using async context.
+const hook_result = {
+ id: null,
+ init_called: false,
+ before_called: false,
+ after_called: false,
+ destroy_called: false,
+};
+const test_hook = async_hooks.createHook({
+ init: (id, type) => {
+ if (type === 'test') {
+ hook_result.id = id;
+ hook_result.init_called = true;
+ }
+ },
+ before: (id) => {
+ if (id === hook_result.id) hook_result.before_called = true;
+ },
+ after: (id) => {
+ if (id === hook_result.id) hook_result.after_called = true;
+ },
+ destroy: (id) => {
+ if (id === hook_result.id) hook_result.destroy_called = true;
+ },
+});
+
+test_hook.enable();
+makeCallback(process, function() {});
+
+assert.strictEqual(hook_result.init_called, true);
+assert.strictEqual(hook_result.before_called, true);
+assert.strictEqual(hook_result.after_called, true);
+setImmediate(() => {
+ assert.strictEqual(hook_result.destroy_called, true);
+ test_hook.disable();
+});
diff --git a/test/addons-napi/test_make_callback_recurse/binding.cc b/test/addons-napi/test_make_callback_recurse/binding.cc
index ce75adc5b8..1e0c16c80e 100644
--- a/test/addons-napi/test_make_callback_recurse/binding.cc
+++ b/test/addons-napi/test_make_callback_recurse/binding.cc
@@ -12,8 +12,8 @@ napi_value MakeCallback(napi_env env, napi_callback_info info) {
napi_value recv = args[0];
napi_value func = args[1];
- napi_make_callback(env,
- recv, func, 0 /* argc */, nullptr /* argv */, nullptr /* result */);
+ napi_make_callback(env, nullptr /* async_context */,
+ recv, func, 0 /* argc */, nullptr /* argv */, nullptr /* result */);
return recv;
}