Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement AsyncContext class #252

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ The following is the documentation for node-addon-api.
- [Memory Management](doc/memory_management.md)
- [Async Operations](doc/async_operations.md)
- [AsyncWorker](doc/async_worker.md)
- [AsyncContext](doc/async_context.md)
NickNaso marked this conversation as resolved.
Show resolved Hide resolved
- [Promises](doc/promises.md)
- [Version management](doc/version_management.md)

Expand Down
76 changes: 76 additions & 0 deletions doc/async_context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# AsyncContext

The [Napi::AsyncWorker](async_worker.md) class may not be appropriate for every
scenario. When using any other async mechanism, introducing a new class
`Napi::AsyncContext` is necessary to ensure an async operation is properly
tracked by the runtime. The `Napi::AsyncContext` class can be passed to
[Napi::Function::MakeCallback()](function.md) method to properly restore the
correct async execution context.

## Methods

### Constructor

Creates a new `Napi::AsyncContext`.

```cpp
explicit Napi::AsyncContext::AsyncContext(napi_env env, const char* resource_name);
```

- `[in] env`: The environment in which to create the `Napi::AsyncContext`.
- `[in] resource_name`: Null-terminated strings that represents the
identifier for the kind of resource that is being provided for diagnostic
information exposed by the `async_hooks` API.

### Constructor

Creates a new `Napi::AsyncContext`.

```cpp
explicit Napi::AsyncContext::AsyncContext(napi_env env, const char* resource_name, const Napi::Object& resource);
```

- `[in] env`: The environment in which to create the `Napi::AsyncContext`.
- `[in] resource_name`: Null-terminated strings that represents the
identifier for the kind of resource that is being provided for diagnostic
information exposed by the `async_hooks` API.
- `[in] resource`: Object associated with the asynchronous operation that
will be passed to possible `async_hooks`.

### Destructor

The `Napi::AsyncContext` to be destroyed.

```cpp
virtual Napi::AsyncContext::~AsyncContext();
```

## Operator

```cpp
Napi::AsyncContext::operator napi_async_context() const;
```

Returns the N-API `napi_async_context` wrapped by the `Napi::AsyncContext`
object. This can be used to mix usage of the C N-API and node-addon-api.

## Example

```cpp
#include "napi.h"

void MakeCallbackWithAsyncContext(const Napi::CallbackInfo& info) {
Napi::Function callback = info[0].As<Napi::Function>();
Napi::Object resource = info[1].As<Napi::Object>();

// Creat a new async context instance.
Napi::AsyncContext context(info.Env(), "async_context_test", resource);

// Invoke the callback with the async context instance.
callback.MakeCallback(Napi::Object::New(info.Env()),
std::initializer_list<napi_value>{}, context);

// The async context instance is automatically destroyed here because it's
// block-scope like `Napi::HandleScope`.
}
```
6 changes: 6 additions & 0 deletions doc/async_operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,9 @@ asynchronous operations:

These class helps manage asynchronous operations through an abstraction
of the concept of moving data between the **event loop** and **worker threads**.

Also, the above class may not be appropriate for every scenario. When using any
other asynchronous mechanism, the following API is necessary to ensure an
asynchronous operation is properly tracked by the runtime:

- **[AsyncContext](async_context.md)**
18 changes: 15 additions & 3 deletions doc/function.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,12 +233,16 @@ Returns a `Napi::Value` representing the JavaScript value returned by the functi
Calls a Javascript function from a native add-on after an asynchronous operation.

```cpp
Napi::Value Napi::Function::MakeCallback(napi_value recv, const std::initializer_list<napi_value>& args) const;
Napi::Value Napi::Function::MakeCallback(napi_value recv, const std::initializer_list<napi_value>& args, napi_async_context context = nullptr) const;
```

- `[in] recv`: The `this` object passed to the called function.
- `[in] args`: Initializer list of JavaScript values as `napi_value` representing
the arguments of the function.
- `[in] context`: Context for the async operation that is invoking the callback.
This should normally be a value previously obtained from [Napi::AsyncContext](async_context.md).
However `nullptr` is also allowed, which indicates the current async context
(if any) is to be used for the callback.

Returns a `Napi::Value` representing the JavaScript value returned by the function.

Expand All @@ -247,12 +251,16 @@ Returns a `Napi::Value` representing the JavaScript value returned by the functi
Calls a Javascript function from a native add-on after an asynchronous operation.

```cpp
Napi::Value Napi::Function::MakeCallback(napi_value recv, const std::vector<napi_value>& args) const;
Napi::Value Napi::Function::MakeCallback(napi_value recv, const std::vector<napi_value>& args, napi_async_context context = nullptr) const;
```

- `[in] recv`: The `this` object passed to the called function.
- `[in] args`: List of JavaScript values as `napi_value` representing the
arguments of the function.
- `[in] context`: Context for the async operation that is invoking the callback.
This should normally be a value previously obtained from [Napi::AsyncContext](async_context.md).
However `nullptr` is also allowed, which indicates the current async context
(if any) is to be used for the callback.

Returns a `Napi::Value` representing the JavaScript value returned by the function.

Expand All @@ -261,13 +269,17 @@ Returns a `Napi::Value` representing the JavaScript value returned by the functi
Calls a Javascript function from a native add-on after an asynchronous operation.

```cpp
Napi::Value Napi::Function::MakeCallback(napi_value recv, size_t argc, const napi_value* args) const;
Napi::Value Napi::Function::MakeCallback(napi_value recv, size_t argc, const napi_value* args, napi_async_context context = nullptr) const;
```

- `[in] recv`: The `this` object passed to the called function.
- `[in] argc`: The number of the arguments passed to the function.
- `[in] args`: Array of JavaScript values as `napi_value` representing the
arguments of the function.
- `[in] context`: Context for the async operation that is invoking the callback.
This should normally be a value previously obtained from [Napi::AsyncContext](async_context.md).
However `nullptr` is also allowed, which indicates the current async context
(if any) is to be used for the callback.

Returns a `Napi::Value` representing the JavaScript value returned by the function.

Expand Down
18 changes: 15 additions & 3 deletions doc/function_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,16 @@ Calls a referenced JavaScript function from a native add-on after an asynchronou
operation.

```cpp
Napi::Value Napi::FunctionReference::MakeCallback(napi_value recv, const std::initializer_list<napi_value>& args) const;
Napi::Value Napi::FunctionReference::MakeCallback(napi_value recv, const std::initializer_list<napi_value>& args, napi_async_context = nullptr) const;
```

- `[in] recv`: The `this` object passed to the referenced function when it's called.
- `[in] args`: Initializer list of JavaScript values as `napi_value` representing
the arguments of the referenced function.
- `[in] context`: Context for the async operation that is invoking the callback.
This should normally be a value previously obtained from [Napi::AsyncContext](async_context.md).
However `nullptr` is also allowed, which indicates the current async context
(if any) is to be used for the callback.

Returns a `Napi::Value` representing the JavaScript object returned by the referenced
function.
Expand All @@ -187,12 +191,16 @@ Calls a referenced JavaScript function from a native add-on after an asynchronou
operation.

```cpp
Napi::Value Napi::FunctionReference::MakeCallback(napi_value recv, const std::vector<napi_value>& args) const;
Napi::Value Napi::FunctionReference::MakeCallback(napi_value recv, const std::vector<napi_value>& args, napi_async_context context = nullptr) const;
```

- `[in] recv`: The `this` object passed to the referenced function when it's called.
- `[in] args`: Vector of JavaScript values as `napi_value` representing the
arguments of the referenced function.
- `[in] context`: Context for the async operation that is invoking the callback.
This should normally be a value previously obtained from [Napi::AsyncContext](async_context.md).
However `nullptr` is also allowed, which indicates the current async context
(if any) is to be used for the callback.

Returns a `Napi::Value` representing the JavaScript object returned by the referenced
function.
Expand All @@ -203,13 +211,17 @@ Calls a referenced JavaScript function from a native add-on after an asynchronou
operation.

```cpp
Napi::Value Napi::FunctionReference::MakeCallback(napi_value recv, size_t argc, const napi_value* args) const;
Napi::Value Napi::FunctionReference::MakeCallback(napi_value recv, size_t argc, const napi_value* args, napi_async_context context = nullptr) const;
```

- `[in] recv`: The `this` object passed to the referenced function when it's called.
- `[in] argc`: The number of arguments passed to the referenced function.
- `[in] args`: Array of JavaScript values as `napi_value` representing the
arguments of the referenced function.
- `[in] context`: Context for the async operation that is invoking the callback.
This should normally be a value previously obtained from [Napi::AsyncContext](async_context.md).
However `nullptr` is also allowed, which indicates the current async context
(if any) is to be used for the callback.

Returns a `Napi::Value` representing the JavaScript object returned by the referenced
function.
Expand Down
86 changes: 74 additions & 12 deletions napi-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1651,20 +1651,27 @@ inline Value Function::Call(napi_value recv, size_t argc, const napi_value* args
}

inline Value Function::MakeCallback(
napi_value recv, const std::initializer_list<napi_value>& args) const {
return MakeCallback(recv, args.size(), args.begin());
napi_value recv,
const std::initializer_list<napi_value>& args,
napi_async_context context) const {
return MakeCallback(recv, args.size(), args.begin(), context);
}

inline Value Function::MakeCallback(
napi_value recv, const std::vector<napi_value>& args) const {
return MakeCallback(recv, args.size(), args.data());
napi_value recv,
const std::vector<napi_value>& args,
napi_async_context context) const {
return MakeCallback(recv, args.size(), args.data(), context);
}

inline Value Function::MakeCallback(
napi_value recv, size_t argc, const napi_value* args) const {
napi_value recv,
size_t argc,
const napi_value* args,
napi_async_context context) const {
napi_value result;
napi_status status = napi_make_callback(
_env, nullptr, recv, _value, argc, args, &result);
_env, context, recv, _value, argc, args, &result);
NAPI_THROW_IF_FAILED(_env, status, Value());
return Value(_env, result);
}
Expand Down Expand Up @@ -2416,29 +2423,36 @@ inline Napi::Value FunctionReference::Call(
}

inline Napi::Value FunctionReference::MakeCallback(
napi_value recv, const std::initializer_list<napi_value>& args) const {
napi_value recv,
const std::initializer_list<napi_value>& args,
napi_async_context context) const {
EscapableHandleScope scope(_env);
Napi::Value result = Value().MakeCallback(recv, args);
Napi::Value result = Value().MakeCallback(recv, args, context);
if (scope.Env().IsExceptionPending()) {
return Value();
}
return scope.Escape(result);
}

inline Napi::Value FunctionReference::MakeCallback(
napi_value recv, const std::vector<napi_value>& args) const {
napi_value recv,
const std::vector<napi_value>& args,
napi_async_context context) const {
EscapableHandleScope scope(_env);
Napi::Value result = Value().MakeCallback(recv, args);
Napi::Value result = Value().MakeCallback(recv, args, context);
if (scope.Env().IsExceptionPending()) {
return Value();
}
return scope.Escape(result);
}

inline Napi::Value FunctionReference::MakeCallback(
napi_value recv, size_t argc, const napi_value* args) const {
napi_value recv,
size_t argc,
const napi_value* args,
napi_async_context context) const {
EscapableHandleScope scope(_env);
Napi::Value result = Value().MakeCallback(recv, argc, args);
Napi::Value result = Value().MakeCallback(recv, argc, args, context);
if (scope.Env().IsExceptionPending()) {
return Value();
}
Expand Down Expand Up @@ -3274,6 +3288,54 @@ inline Value EscapableHandleScope::Escape(napi_value escapee) {
return Value(_env, result);
}

////////////////////////////////////////////////////////////////////////////////
// AsyncContext class
////////////////////////////////////////////////////////////////////////////////

inline AsyncContext::AsyncContext(napi_env env, const char* resource_name)
: AsyncContext(env, resource_name, Object::New(env)) {
}

inline AsyncContext::AsyncContext(napi_env env,
const char* resource_name,
const Object& resource)
: _env(env),
_context(nullptr) {
napi_value resource_id;
napi_status status = napi_create_string_utf8(
_env, resource_name, NAPI_AUTO_LENGTH, &resource_id);
NAPI_THROW_IF_FAILED_VOID(_env, status);

status = napi_async_init(_env, resource, resource_id, &_context);
NAPI_THROW_IF_FAILED_VOID(_env, status);
}

inline AsyncContext::~AsyncContext() {
if (_context != nullptr) {
napi_async_destroy(_env, _context);
_context = nullptr;
}
}

inline AsyncContext::AsyncContext(AsyncContext&& other) {
_env = other._env;
other._env = nullptr;
_context = other._context;
other._context = nullptr;
}

inline AsyncContext& AsyncContext::operator =(AsyncContext&& other) {
_env = other._env;
other._env = nullptr;
_context = other._context;
other._context = nullptr;
return *this;
}

inline AsyncContext::operator napi_async_context() const {
return _context;
}

////////////////////////////////////////////////////////////////////////////////
// AsyncWorker class
////////////////////////////////////////////////////////////////////////////////
Expand Down
Loading