Skip to content

Commit

Permalink
Implement CallbackScope class
Browse files Browse the repository at this point in the history
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
  • Loading branch information
romandev committed Oct 15, 2018
1 parent fd65078 commit 4adf148
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 0 deletions.
2 changes: 2 additions & 0 deletions doc/async_operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ other asynchronous mechanism, the following API is necessary to ensure an
asynchronous operation is properly tracked by the runtime:

- **[AsyncContext](async_context.md)**

- **[CallbackScope](callback_scope.md)**
54 changes: 54 additions & 0 deletions doc/callback_scope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# CallbackScope

There are cases (for example, resolving promises) where it is necessary to have
the equivalent of the scope associated with a callback in place when making
certain N-API calls.

## Methods

### Constructor

Creates a new callback scope on the stack.

```cpp
Napi::CallbackScope::CallbackScope(napi_env env, napi_callback_scope scope);
```
- `[in] env`: The environment in which to create the `Napi::CallbackScope`.
- `[in] scope`: pre-existing `napi_callback_scope` or `Napi::CallbackScope`.
### Constructor
Creates a new callback scope on the stack.
```cpp
Napi::CallbackScope::CallbackScope(napi_env env, napi_async_context context);
```

- `[in] env`: The environment in which to create the `Napi::CallbackScope`.
- `[in] async_context`: pre-existing `napi_async_context` or `Napi::AsyncContext`.

### Destructor

The `Napi::CallbackScope` to be destroyed.

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

### Env

```cpp
Napi::Env Napi::CallbackScope::Env() const;
```

Returns the `Napi::Env` associated with the `Napi::CallbackScope`.

## Operator

```cpp
Napi::CallbackScope::operator napi_callback_scope() const;
```

Returns the N-API `napi_callback_scope` wrapped by the `Napi::CallbackScope`
object. This can be used to mix usage of the C N-API and node-addon-api.
28 changes: 28 additions & 0 deletions napi-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3404,6 +3404,34 @@ inline Value EscapableHandleScope::Escape(napi_value escapee) {
return Value(_env, result);
}

////////////////////////////////////////////////////////////////////////////////
// CallbackScope class
////////////////////////////////////////////////////////////////////////////////

inline CallbackScope::CallbackScope(
napi_env env, napi_callback_scope scope) : _env(env), _scope(scope) {
}

inline CallbackScope::CallbackScope(napi_env env, napi_async_context context)
: _env(env),
_async_context(context) {
napi_status status = napi_open_callback_scope(
_env, Object::New(env), context, &_scope);
NAPI_THROW_IF_FAILED_VOID(_env, status);
}

inline CallbackScope::~CallbackScope() {
napi_close_callback_scope(_env, _scope);
}

inline CallbackScope::operator napi_callback_scope() const {
return _scope;
}

inline Napi::Env CallbackScope::Env() const {
return Napi::Env(_env);
}

////////////////////////////////////////////////////////////////////////////////
// AsyncContext class
////////////////////////////////////////////////////////////////////////////////
Expand Down
16 changes: 16 additions & 0 deletions napi.h
Original file line number Diff line number Diff line change
Expand Up @@ -1671,6 +1671,22 @@ namespace Napi {
napi_escapable_handle_scope _scope;
};

class CallbackScope {
public:
CallbackScope(napi_env env, napi_callback_scope scope);
CallbackScope(napi_env env, napi_async_context context);
virtual ~CallbackScope();

operator napi_callback_scope() const;

Napi::Env Env() const;

private:
napi_env _env;
napi_async_context _async_context;
napi_callback_scope _scope;
};

class AsyncContext {
public:
explicit AsyncContext(napi_env env, const char* resource_name);
Expand Down
2 changes: 2 additions & 0 deletions test/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Object InitBasicTypesValue(Env env);
Object InitBigInt(Env env);
#endif
Object InitBuffer(Env env);
Object InitCallbackScope(Env env);
Object InitDataView(Env env);
Object InitDataViewReadWrite(Env env);
Object InitError(Env env);
Expand Down Expand Up @@ -47,6 +48,7 @@ Object Init(Env env, Object exports) {
exports.Set("bigint", InitBigInt(env));
#endif
exports.Set("buffer", InitBuffer(env));
exports.Set("callbackscope", InitCallbackScope(env));
exports.Set("dataview", InitDataView(env));
exports.Set("dataview_read_write", InitDataView(env));
exports.Set("dataview_read_write", InitDataViewReadWrite(env));
Expand Down
1 change: 1 addition & 0 deletions test/binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
'bigint.cc',
'binding.cc',
'buffer.cc',
'callbackscope.cc',
'dataview/dataview.cc',
'dataview/dataview_read_write.cc',
'error.cc',
Expand Down
20 changes: 20 additions & 0 deletions test/callbackscope.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include "napi.h"

using namespace Napi;

namespace {

static void RunInCallbackScope(const CallbackInfo& info) {
Function callback = info[0].As<Function>();
AsyncContext context(info.Env(), "callback_scope_test");
CallbackScope scope(info.Env(), context);
callback.Call({});
}

} // end anonymous namespace

Object InitCallbackScope(Env env) {
Object exports = Object::New(env);
exports["runInCallbackScope"] = Function::New(env, RunInCallbackScope);
return exports;
}
48 changes: 48 additions & 0 deletions test/callbackscope.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict';
const buildType = process.config.target_defaults.default_configuration;
const assert = require('assert');
const common = require('./common');

// we only check async hooks on 8.x an higher were
// they are closer to working properly
const nodeVersion = process.versions.node.split('.')[0]
let async_hooks = undefined;
function checkAsyncHooks() {
if (nodeVersion >= 8) {
if (async_hooks == undefined) {
async_hooks = require('async_hooks');
}
return true;
}
return false;
}

test(require(`./build/${buildType}/binding.node`));
test(require(`./build/${buildType}/binding_noexcept.node`));

function test(binding) {
if (!checkAsyncHooks())
return;

let id;
let insideHook = false;
async_hooks.createHook({
init(asyncId, type, triggerAsyncId, resource) {
if (id === undefined && type === 'callback_scope_test') {
id = asyncId;
}
},
before(asyncId) {
if (asyncId === id)
insideHook = true;
},
after(asyncId) {
if (asyncId === id)
insideHook = false;
}
}).enable();

binding.callbackscope.runInCallbackScope(function() {
assert(insideHook);
});
}
1 change: 1 addition & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ let testModules = [
'basic_types/value',
'bigint',
'buffer',
'callbackscope',
'dataview/dataview',
'dataview/dataview_read_write',
'error',
Expand Down

0 comments on commit 4adf148

Please sign in to comment.