Skip to content

Commit dfcb939

Browse files
romandevmhdawson
authored andcommitted
src: implement AsyncContext class
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]>
1 parent 4f76262 commit dfcb939

12 files changed

+323
-24
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ The following is the documentation for node-addon-api.
103103
- [Memory Management](doc/memory_management.md)
104104
- [Async Operations](doc/async_operations.md)
105105
- [AsyncWorker](doc/async_worker.md)
106+
- [AsyncContext](doc/async_context.md)
106107
- [Promises](doc/promises.md)
107108
- [Version management](doc/version_management.md)
108109

doc/async_context.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# AsyncContext
2+
3+
The [Napi::AsyncWorker](async_worker.md) class may not be appropriate for every
4+
scenario. When using any other async mechanism, introducing a new class
5+
`Napi::AsyncContext` is necessary to ensure an async operation is properly
6+
tracked by the runtime. The `Napi::AsyncContext` class can be passed to
7+
[Napi::Function::MakeCallback()](function.md) method to properly restore the
8+
correct async execution context.
9+
10+
## Methods
11+
12+
### Constructor
13+
14+
Creates a new `Napi::AsyncContext`.
15+
16+
```cpp
17+
explicit Napi::AsyncContext::AsyncContext(napi_env env, const char* resource_name);
18+
```
19+
20+
- `[in] env`: The environment in which to create the `Napi::AsyncContext`.
21+
- `[in] resource_name`: Null-terminated strings that represents the
22+
identifier for the kind of resource that is being provided for diagnostic
23+
information exposed by the `async_hooks` API.
24+
25+
### Constructor
26+
27+
Creates a new `Napi::AsyncContext`.
28+
29+
```cpp
30+
explicit Napi::AsyncContext::AsyncContext(napi_env env, const char* resource_name, const Napi::Object& resource);
31+
```
32+
33+
- `[in] env`: The environment in which to create the `Napi::AsyncContext`.
34+
- `[in] resource_name`: Null-terminated strings that represents the
35+
identifier for the kind of resource that is being provided for diagnostic
36+
information exposed by the `async_hooks` API.
37+
- `[in] resource`: Object associated with the asynchronous operation that
38+
will be passed to possible `async_hooks`.
39+
40+
### Destructor
41+
42+
The `Napi::AsyncContext` to be destroyed.
43+
44+
```cpp
45+
virtual Napi::AsyncContext::~AsyncContext();
46+
```
47+
48+
## Operator
49+
50+
```cpp
51+
Napi::AsyncContext::operator napi_async_context() const;
52+
```
53+
54+
Returns the N-API `napi_async_context` wrapped by the `Napi::AsyncContext`
55+
object. This can be used to mix usage of the C N-API and node-addon-api.
56+
57+
## Example
58+
59+
```cpp
60+
#include "napi.h"
61+
62+
void MakeCallbackWithAsyncContext(const Napi::CallbackInfo& info) {
63+
Napi::Function callback = info[0].As<Napi::Function>();
64+
Napi::Object resource = info[1].As<Napi::Object>();
65+
66+
// Creat a new async context instance.
67+
Napi::AsyncContext context(info.Env(), "async_context_test", resource);
68+
69+
// Invoke the callback with the async context instance.
70+
callback.MakeCallback(Napi::Object::New(info.Env()),
71+
std::initializer_list<napi_value>{}, context);
72+
73+
// The async context instance is automatically destroyed here because it's
74+
// block-scope like `Napi::HandleScope`.
75+
}
76+
```

doc/async_operations.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,9 @@ asynchronous operations:
2121

2222
These class helps manage asynchronous operations through an abstraction
2323
of the concept of moving data between the **event loop** and **worker threads**.
24+
25+
Also, the above class may not be appropriate for every scenario. When using any
26+
other asynchronous mechanism, the following API is necessary to ensure an
27+
asynchronous operation is properly tracked by the runtime:
28+
29+
- **[AsyncContext](async_context.md)**

doc/function.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,12 +233,16 @@ Returns a `Napi::Value` representing the JavaScript value returned by the functi
233233
Calls a Javascript function from a native add-on after an asynchronous operation.
234234

235235
```cpp
236-
Napi::Value Napi::Function::MakeCallback(napi_value recv, const std::initializer_list<napi_value>& args) const;
236+
Napi::Value Napi::Function::MakeCallback(napi_value recv, const std::initializer_list<napi_value>& args, napi_async_context context = nullptr) const;
237237
```
238238
239239
- `[in] recv`: The `this` object passed to the called function.
240240
- `[in] args`: Initializer list of JavaScript values as `napi_value` representing
241241
the arguments of the function.
242+
- `[in] context`: Context for the async operation that is invoking the callback.
243+
This should normally be a value previously obtained from [Napi::AsyncContext](async_context.md).
244+
However `nullptr` is also allowed, which indicates the current async context
245+
(if any) is to be used for the callback.
242246
243247
Returns a `Napi::Value` representing the JavaScript value returned by the function.
244248
@@ -247,12 +251,16 @@ Returns a `Napi::Value` representing the JavaScript value returned by the functi
247251
Calls a Javascript function from a native add-on after an asynchronous operation.
248252
249253
```cpp
250-
Napi::Value Napi::Function::MakeCallback(napi_value recv, const std::vector<napi_value>& args) const;
254+
Napi::Value Napi::Function::MakeCallback(napi_value recv, const std::vector<napi_value>& args, napi_async_context context = nullptr) const;
251255
```
252256

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

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

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

263271
```cpp
264-
Napi::Value Napi::Function::MakeCallback(napi_value recv, size_t argc, const napi_value* args) const;
272+
Napi::Value Napi::Function::MakeCallback(napi_value recv, size_t argc, const napi_value* args, napi_async_context context = nullptr) const;
265273
```
266274
267275
- `[in] recv`: The `this` object passed to the called function.
268276
- `[in] argc`: The number of the arguments passed to the function.
269277
- `[in] args`: Array of JavaScript values as `napi_value` representing the
270278
arguments of the function.
279+
- `[in] context`: Context for the async operation that is invoking the callback.
280+
This should normally be a value previously obtained from [Napi::AsyncContext](async_context.md).
281+
However `nullptr` is also allowed, which indicates the current async context
282+
(if any) is to be used for the callback.
271283
272284
Returns a `Napi::Value` representing the JavaScript value returned by the function.
273285

doc/function_reference.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,12 +171,16 @@ Calls a referenced JavaScript function from a native add-on after an asynchronou
171171
operation.
172172

173173
```cpp
174-
Napi::Value Napi::FunctionReference::MakeCallback(napi_value recv, const std::initializer_list<napi_value>& args) const;
174+
Napi::Value Napi::FunctionReference::MakeCallback(napi_value recv, const std::initializer_list<napi_value>& args, napi_async_context = nullptr) const;
175175
```
176176
177177
- `[in] recv`: The `this` object passed to the referenced function when it's called.
178178
- `[in] args`: Initializer list of JavaScript values as `napi_value` representing
179179
the arguments of the referenced function.
180+
- `[in] context`: Context for the async operation that is invoking the callback.
181+
This should normally be a value previously obtained from [Napi::AsyncContext](async_context.md).
182+
However `nullptr` is also allowed, which indicates the current async context
183+
(if any) is to be used for the callback.
180184
181185
Returns a `Napi::Value` representing the JavaScript object returned by the referenced
182186
function.
@@ -187,12 +191,16 @@ Calls a referenced JavaScript function from a native add-on after an asynchronou
187191
operation.
188192
189193
```cpp
190-
Napi::Value Napi::FunctionReference::MakeCallback(napi_value recv, const std::vector<napi_value>& args) const;
194+
Napi::Value Napi::FunctionReference::MakeCallback(napi_value recv, const std::vector<napi_value>& args, napi_async_context context = nullptr) const;
191195
```
192196

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

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

205213
```cpp
206-
Napi::Value Napi::FunctionReference::MakeCallback(napi_value recv, size_t argc, const napi_value* args) const;
214+
Napi::Value Napi::FunctionReference::MakeCallback(napi_value recv, size_t argc, const napi_value* args, napi_async_context context = nullptr) const;
207215
```
208216
209217
- `[in] recv`: The `this` object passed to the referenced function when it's called.
210218
- `[in] argc`: The number of arguments passed to the referenced function.
211219
- `[in] args`: Array of JavaScript values as `napi_value` representing the
212220
arguments of the referenced function.
221+
- `[in] context`: Context for the async operation that is invoking the callback.
222+
This should normally be a value previously obtained from [Napi::AsyncContext](async_context.md).
223+
However `nullptr` is also allowed, which indicates the current async context
224+
(if any) is to be used for the callback.
213225
214226
Returns a `Napi::Value` representing the JavaScript object returned by the referenced
215227
function.

napi-inl.h

Lines changed: 74 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1651,20 +1651,27 @@ inline Value Function::Call(napi_value recv, size_t argc, const napi_value* args
16511651
}
16521652

16531653
inline Value Function::MakeCallback(
1654-
napi_value recv, const std::initializer_list<napi_value>& args) const {
1655-
return MakeCallback(recv, args.size(), args.begin());
1654+
napi_value recv,
1655+
const std::initializer_list<napi_value>& args,
1656+
napi_async_context context) const {
1657+
return MakeCallback(recv, args.size(), args.begin(), context);
16561658
}
16571659

16581660
inline Value Function::MakeCallback(
1659-
napi_value recv, const std::vector<napi_value>& args) const {
1660-
return MakeCallback(recv, args.size(), args.data());
1661+
napi_value recv,
1662+
const std::vector<napi_value>& args,
1663+
napi_async_context context) const {
1664+
return MakeCallback(recv, args.size(), args.data(), context);
16611665
}
16621666

16631667
inline Value Function::MakeCallback(
1664-
napi_value recv, size_t argc, const napi_value* args) const {
1668+
napi_value recv,
1669+
size_t argc,
1670+
const napi_value* args,
1671+
napi_async_context context) const {
16651672
napi_value result;
16661673
napi_status status = napi_make_callback(
1667-
_env, nullptr, recv, _value, argc, args, &result);
1674+
_env, context, recv, _value, argc, args, &result);
16681675
NAPI_THROW_IF_FAILED(_env, status, Value());
16691676
return Value(_env, result);
16701677
}
@@ -2416,29 +2423,36 @@ inline Napi::Value FunctionReference::Call(
24162423
}
24172424

24182425
inline Napi::Value FunctionReference::MakeCallback(
2419-
napi_value recv, const std::initializer_list<napi_value>& args) const {
2426+
napi_value recv,
2427+
const std::initializer_list<napi_value>& args,
2428+
napi_async_context context) const {
24202429
EscapableHandleScope scope(_env);
2421-
Napi::Value result = Value().MakeCallback(recv, args);
2430+
Napi::Value result = Value().MakeCallback(recv, args, context);
24222431
if (scope.Env().IsExceptionPending()) {
24232432
return Value();
24242433
}
24252434
return scope.Escape(result);
24262435
}
24272436

24282437
inline Napi::Value FunctionReference::MakeCallback(
2429-
napi_value recv, const std::vector<napi_value>& args) const {
2438+
napi_value recv,
2439+
const std::vector<napi_value>& args,
2440+
napi_async_context context) const {
24302441
EscapableHandleScope scope(_env);
2431-
Napi::Value result = Value().MakeCallback(recv, args);
2442+
Napi::Value result = Value().MakeCallback(recv, args, context);
24322443
if (scope.Env().IsExceptionPending()) {
24332444
return Value();
24342445
}
24352446
return scope.Escape(result);
24362447
}
24372448

24382449
inline Napi::Value FunctionReference::MakeCallback(
2439-
napi_value recv, size_t argc, const napi_value* args) const {
2450+
napi_value recv,
2451+
size_t argc,
2452+
const napi_value* args,
2453+
napi_async_context context) const {
24402454
EscapableHandleScope scope(_env);
2441-
Napi::Value result = Value().MakeCallback(recv, argc, args);
2455+
Napi::Value result = Value().MakeCallback(recv, argc, args, context);
24422456
if (scope.Env().IsExceptionPending()) {
24432457
return Value();
24442458
}
@@ -3274,6 +3288,54 @@ inline Value EscapableHandleScope::Escape(napi_value escapee) {
32743288
return Value(_env, result);
32753289
}
32763290

3291+
////////////////////////////////////////////////////////////////////////////////
3292+
// AsyncContext class
3293+
////////////////////////////////////////////////////////////////////////////////
3294+
3295+
inline AsyncContext::AsyncContext(napi_env env, const char* resource_name)
3296+
: AsyncContext(env, resource_name, Object::New(env)) {
3297+
}
3298+
3299+
inline AsyncContext::AsyncContext(napi_env env,
3300+
const char* resource_name,
3301+
const Object& resource)
3302+
: _env(env),
3303+
_context(nullptr) {
3304+
napi_value resource_id;
3305+
napi_status status = napi_create_string_utf8(
3306+
_env, resource_name, NAPI_AUTO_LENGTH, &resource_id);
3307+
NAPI_THROW_IF_FAILED_VOID(_env, status);
3308+
3309+
status = napi_async_init(_env, resource, resource_id, &_context);
3310+
NAPI_THROW_IF_FAILED_VOID(_env, status);
3311+
}
3312+
3313+
inline AsyncContext::~AsyncContext() {
3314+
if (_context != nullptr) {
3315+
napi_async_destroy(_env, _context);
3316+
_context = nullptr;
3317+
}
3318+
}
3319+
3320+
inline AsyncContext::AsyncContext(AsyncContext&& other) {
3321+
_env = other._env;
3322+
other._env = nullptr;
3323+
_context = other._context;
3324+
other._context = nullptr;
3325+
}
3326+
3327+
inline AsyncContext& AsyncContext::operator =(AsyncContext&& other) {
3328+
_env = other._env;
3329+
other._env = nullptr;
3330+
_context = other._context;
3331+
other._context = nullptr;
3332+
return *this;
3333+
}
3334+
3335+
inline AsyncContext::operator napi_async_context() const {
3336+
return _context;
3337+
}
3338+
32773339
////////////////////////////////////////////////////////////////////////////////
32783340
// AsyncWorker class
32793341
////////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)