Skip to content

Commit

Permalink
src: implement AsyncContext class
Browse files Browse the repository at this point in the history
This class provides a wrapper for the following custom asynchronous
operation APIs.
  - napi_async_init()
  - napi_async_destroy()

PR-URL: #252
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Nicola Del Gobbo <[email protected]>
  • Loading branch information
romandev authored and mhdawson committed Oct 1, 2018
1 parent 4f76262 commit dfcb939
Show file tree
Hide file tree
Showing 12 changed files with 323 additions and 24 deletions.
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)
- [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

0 comments on commit dfcb939

Please sign in to comment.