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 CallbackScope class #362

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
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`: The 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`: The pre-existing `napi_async_context` or `Napi::AsyncContext`.

### Destructor

devsnek marked this conversation as resolved.
Show resolved Hide resolved
Deletes the instance of `Napi::CallbackScope` object.

```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