Skip to content

Commit 405f3e5

Browse files
romandevmhdawson
authored andcommitted
src: implement CallbackScope class
This is a wrapper class to support the following N-APIs. - napi_open_callback_scope() - napi_close_callback_scope() Refs: https://nodejs.org/api/n-api.html#n_api_napi_open_callback_scope PR-URL: nodejs#362 Refs: https://nodejs.org/api/n-api.html#n_api_napi_open_callback_scope Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Nicola Del Gobbo <[email protected]>
1 parent 015d953 commit 405f3e5

9 files changed

+172
-0
lines changed

Diff for: doc/async_operations.md

+2
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,5 @@ other asynchronous mechanism, the following API is necessary to ensure an
2727
asynchronous operation is properly tracked by the runtime:
2828

2929
- **[AsyncContext](async_context.md)**
30+
31+
- **[CallbackScope](callback_scope.md)**

Diff for: doc/callback_scope.md

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# CallbackScope
2+
3+
There are cases (for example, resolving promises) where it is necessary to have
4+
the equivalent of the scope associated with a callback in place when making
5+
certain N-API calls.
6+
7+
## Methods
8+
9+
### Constructor
10+
11+
Creates a new callback scope on the stack.
12+
13+
```cpp
14+
Napi::CallbackScope::CallbackScope(napi_env env, napi_callback_scope scope);
15+
```
16+
17+
- `[in] env`: The environment in which to create the `Napi::CallbackScope`.
18+
- `[in] scope`: The pre-existing `napi_callback_scope` or `Napi::CallbackScope`.
19+
20+
### Constructor
21+
22+
Creates a new callback scope on the stack.
23+
24+
```cpp
25+
Napi::CallbackScope::CallbackScope(napi_env env, napi_async_context context);
26+
```
27+
28+
- `[in] env`: The environment in which to create the `Napi::CallbackScope`.
29+
- `[in] async_context`: The pre-existing `napi_async_context` or `Napi::AsyncContext`.
30+
31+
### Destructor
32+
33+
Deletes the instance of `Napi::CallbackScope` object.
34+
35+
```cpp
36+
virtual Napi::CallbackScope::~CallbackScope();
37+
```
38+
39+
### Env
40+
41+
```cpp
42+
Napi::Env Napi::CallbackScope::Env() const;
43+
```
44+
45+
Returns the `Napi::Env` associated with the `Napi::CallbackScope`.
46+
47+
## Operator
48+
49+
```cpp
50+
Napi::CallbackScope::operator napi_callback_scope() const;
51+
```
52+
53+
Returns the N-API `napi_callback_scope` wrapped by the `Napi::CallbackScope`
54+
object. This can be used to mix usage of the C N-API and node-addon-api.

Diff for: napi-inl.h

+28
Original file line numberDiff line numberDiff line change
@@ -3404,6 +3404,34 @@ inline Value EscapableHandleScope::Escape(napi_value escapee) {
34043404
return Value(_env, result);
34053405
}
34063406

3407+
////////////////////////////////////////////////////////////////////////////////
3408+
// CallbackScope class
3409+
////////////////////////////////////////////////////////////////////////////////
3410+
3411+
inline CallbackScope::CallbackScope(
3412+
napi_env env, napi_callback_scope scope) : _env(env), _scope(scope) {
3413+
}
3414+
3415+
inline CallbackScope::CallbackScope(napi_env env, napi_async_context context)
3416+
: _env(env),
3417+
_async_context(context) {
3418+
napi_status status = napi_open_callback_scope(
3419+
_env, Object::New(env), context, &_scope);
3420+
NAPI_THROW_IF_FAILED_VOID(_env, status);
3421+
}
3422+
3423+
inline CallbackScope::~CallbackScope() {
3424+
napi_close_callback_scope(_env, _scope);
3425+
}
3426+
3427+
inline CallbackScope::operator napi_callback_scope() const {
3428+
return _scope;
3429+
}
3430+
3431+
inline Napi::Env CallbackScope::Env() const {
3432+
return Napi::Env(_env);
3433+
}
3434+
34073435
////////////////////////////////////////////////////////////////////////////////
34083436
// AsyncContext class
34093437
////////////////////////////////////////////////////////////////////////////////

Diff for: napi.h

+16
Original file line numberDiff line numberDiff line change
@@ -1671,6 +1671,22 @@ namespace Napi {
16711671
napi_escapable_handle_scope _scope;
16721672
};
16731673

1674+
class CallbackScope {
1675+
public:
1676+
CallbackScope(napi_env env, napi_callback_scope scope);
1677+
CallbackScope(napi_env env, napi_async_context context);
1678+
virtual ~CallbackScope();
1679+
1680+
operator napi_callback_scope() const;
1681+
1682+
Napi::Env Env() const;
1683+
1684+
private:
1685+
napi_env _env;
1686+
napi_async_context _async_context;
1687+
napi_callback_scope _scope;
1688+
};
1689+
16741690
class AsyncContext {
16751691
public:
16761692
explicit AsyncContext(napi_env env, const char* resource_name);

Diff for: test/binding.cc

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Object InitBasicTypesValue(Env env);
1515
Object InitBigInt(Env env);
1616
#endif
1717
Object InitBuffer(Env env);
18+
Object InitCallbackScope(Env env);
1819
Object InitDataView(Env env);
1920
Object InitDataViewReadWrite(Env env);
2021
Object InitError(Env env);
@@ -47,6 +48,7 @@ Object Init(Env env, Object exports) {
4748
exports.Set("bigint", InitBigInt(env));
4849
#endif
4950
exports.Set("buffer", InitBuffer(env));
51+
exports.Set("callbackscope", InitCallbackScope(env));
5052
exports.Set("dataview", InitDataView(env));
5153
exports.Set("dataview_read_write", InitDataView(env));
5254
exports.Set("dataview_read_write", InitDataViewReadWrite(env));

Diff for: test/binding.gyp

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
'bigint.cc',
1515
'binding.cc',
1616
'buffer.cc',
17+
'callbackscope.cc',
1718
'dataview/dataview.cc',
1819
'dataview/dataview_read_write.cc',
1920
'error.cc',

Diff for: test/callbackscope.cc

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include "napi.h"
2+
3+
using namespace Napi;
4+
5+
namespace {
6+
7+
static void RunInCallbackScope(const CallbackInfo& info) {
8+
Function callback = info[0].As<Function>();
9+
AsyncContext context(info.Env(), "callback_scope_test");
10+
CallbackScope scope(info.Env(), context);
11+
callback.Call({});
12+
}
13+
14+
} // end anonymous namespace
15+
16+
Object InitCallbackScope(Env env) {
17+
Object exports = Object::New(env);
18+
exports["runInCallbackScope"] = Function::New(env, RunInCallbackScope);
19+
return exports;
20+
}

Diff for: test/callbackscope.js

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict';
2+
const buildType = process.config.target_defaults.default_configuration;
3+
const assert = require('assert');
4+
const common = require('./common');
5+
6+
// we only check async hooks on 8.x an higher were
7+
// they are closer to working properly
8+
const nodeVersion = process.versions.node.split('.')[0]
9+
let async_hooks = undefined;
10+
function checkAsyncHooks() {
11+
if (nodeVersion >= 8) {
12+
if (async_hooks == undefined) {
13+
async_hooks = require('async_hooks');
14+
}
15+
return true;
16+
}
17+
return false;
18+
}
19+
20+
test(require(`./build/${buildType}/binding.node`));
21+
test(require(`./build/${buildType}/binding_noexcept.node`));
22+
23+
function test(binding) {
24+
if (!checkAsyncHooks())
25+
return;
26+
27+
let id;
28+
let insideHook = false;
29+
async_hooks.createHook({
30+
init(asyncId, type, triggerAsyncId, resource) {
31+
if (id === undefined && type === 'callback_scope_test') {
32+
id = asyncId;
33+
}
34+
},
35+
before(asyncId) {
36+
if (asyncId === id)
37+
insideHook = true;
38+
},
39+
after(asyncId) {
40+
if (asyncId === id)
41+
insideHook = false;
42+
}
43+
}).enable();
44+
45+
binding.callbackscope.runInCallbackScope(function() {
46+
assert(insideHook);
47+
});
48+
}

Diff for: test/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ let testModules = [
1616
'basic_types/value',
1717
'bigint',
1818
'buffer',
19+
'callbackscope',
1920
'dataview/dataview',
2021
'dataview/dataview_read_write',
2122
'error',

0 commit comments

Comments
 (0)