-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: add basic tests and doc for scopes
PR-URL: nodejs/node-addon-api#250 Reviewed-By: Kyle Farnung <[email protected]> Reviewed-By: Nicola Del Gobbo <[email protected]>
- Loading branch information
1 parent
ce96536
commit 6477cb7
Showing
11 changed files
with
306 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
# EscapableHandleScope | ||
|
||
The EscapableHandleScope class is used to manage the lifetime of object handles | ||
which are created through the use of node-addon-api. These handles | ||
keep an object alive in the heap in order to ensure that the objects | ||
are not collected by the garbage collector while native code is using them. | ||
A handle may be created when any new node-addon-api Value or one | ||
of its subclasses is created or returned. | ||
|
||
An EscapableHandleScope is a special type of HandleScope | ||
which allows a single handle to be "promoted" to an outer scope. | ||
|
||
For more details refer to the section titled | ||
(Object lifetime management)[object_lifetime_management]. | ||
|
||
## Methods | ||
|
||
### Constructor | ||
|
||
Creates a new escapable handle scope. | ||
|
||
```cpp | ||
EscapableHandleScope EscapableHandleScope::New(Napi:Env env); | ||
``` | ||
- `[in] Env`: The environment in which to construct the EscapableHandleScope object. | ||
Returns a new EscapableHandleScope | ||
### Constructor | ||
Creates a new escapable handle scope. | ||
```cpp | ||
EscapableHandleScope EscapableHandleScope::New(napi_env env, napi_handle_scope scope); | ||
``` | ||
|
||
- `[in] env`: napi_env in which the scope passed in was created. | ||
- `[in] scope`: pre-existing napi_handle_scope. | ||
|
||
Returns a new EscapableHandleScope instance which wraps the | ||
napi_escapable_handle_scope handle passed in. This can be used | ||
to mix usage of the C N-API and node-addon-api. | ||
|
||
operator EscapableHandleScope::napi_escapable_handle_scope | ||
|
||
```cpp | ||
operator EscapableHandleScope::napi_escapable_handle_scope() const | ||
``` | ||
|
||
Returns the N-API napi_escapable_handle_scope wrapped by the EscapableHandleScope object. | ||
This can be used to mix usage of the C N-API and node-addon-api by allowing | ||
the class to be used be converted to a napi_escapable_handle_scope. | ||
|
||
### Destructor | ||
```cpp | ||
~EscapableHandleScope(); | ||
``` | ||
|
||
Deletes the EscapableHandleScope instance and allows any objects/handles created | ||
in the scope to be collected by the garbage collector. There is no | ||
guarantee as to when the gargbage collector will do this. | ||
|
||
### Escape | ||
|
||
```cpp | ||
napi::Value EscapableHandleScope::Escape(napi_value escapee); | ||
``` | ||
- `[in] escapee`: Napi::Value or napi_env to promote to the outer scope | ||
Returns Napi:Value which can be used in the outer scope. This method can | ||
be called at most once on a given EscapableHandleScope. If it is called | ||
more than once an exception will be thrown. | ||
### Env | ||
```cpp | ||
Napi::Env Env() const; | ||
``` | ||
|
||
Returns the Napi:Env associated with the EscapableHandleScope. |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,66 @@ | ||
# Handle scope | ||
# HandleScope | ||
|
||
You are reading a draft of the next documentation and it's in continuos update so | ||
if you don't find what you need please refer to: | ||
[C++ wrapper classes for the ABI-stable C APIs for Node.js](https://nodejs.github.io/node-addon-api/) | ||
The HandleScope class is used to manage the lifetime of object handles | ||
which are created through the use of node-addon-api. These handles | ||
keep an object alive in the heap in order to ensure that the objects | ||
are not collected while native code is using them. | ||
A handle may be created when any new node-addon-api Value or one | ||
of its subclasses is created or returned. For more details refer to | ||
the section titled (Object lifetime management)[object_lifetime_management]. | ||
|
||
## Methods | ||
|
||
### Constructor | ||
|
||
Creates a new handle scope. | ||
|
||
```cpp | ||
HandleScope HandleScope::New(Napi:Env env); | ||
``` | ||
- `[in] Env`: The environment in which to construct the HandleScope object. | ||
Returns a new HandleScope | ||
### Constructor | ||
Creates a new handle scope. | ||
```cpp | ||
HandleScope HandleScope::New(napi_env env, napi_handle_scope scope); | ||
``` | ||
|
||
- `[in] env`: napi_env in which the scope passed in was created. | ||
- `[in] scope`: pre-existing napi_handle_scope. | ||
|
||
Returns a new HandleScope instance which wraps the napi_handle_scope | ||
handle passed in. This can be used to mix usage of the C N-API | ||
and node-addon-api. | ||
|
||
operator HandleScope::napi_handle_scope | ||
|
||
```cpp | ||
operator HandleScope::napi_handle_scope() const | ||
``` | ||
|
||
Returns the N-API napi_handle_scope wrapped by the EscapableHandleScope object. | ||
This can be used to mix usage of the C N-API and node-addon-api by allowing | ||
the class to be used be converted to a napi_handle_scope. | ||
|
||
### Destructor | ||
```cpp | ||
~HandleScope(); | ||
``` | ||
|
||
Deletes the HandleScope instance and allows any objects/handles created | ||
in the scope to be collected by the garbage collector. There is no | ||
guarantee as to when the gargbage collector will do this. | ||
|
||
### Env | ||
|
||
```cpp | ||
Napi::Env Env() const; | ||
``` | ||
|
||
Returns the Napi:Env associated with the HandleScope. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# Object lifetime management | ||
|
||
A handle may be created when any new node-addon-api Value and | ||
its subclasses is created or returned. | ||
|
||
As the methods and classes within the node-addon-api are used, | ||
handles to objects in the heap for the underlying | ||
VM may be created. A handle may be created when any new | ||
node-addon-api Value or one of its subclasses is created or returned. | ||
These handles must hold the objects 'live' until they are no | ||
longer required by the native code, otherwise the objects could be | ||
collected by the garbage collector before the native code was | ||
finished using them. | ||
|
||
As handles are created they are associated with a | ||
'scope'. The lifespan for the default scope is tied to the lifespan | ||
of the native method call. The result is that, by default, handles | ||
remain valid and the objects associated with these handles will be | ||
held live for the lifespan of the native method call. | ||
|
||
In many cases, however, it is necessary that the handles remain valid for | ||
either a shorter or longer lifespan than that of the native method. | ||
The sections which follow describe the node-addon-api classes and | ||
methods that than can be used to change the handle lifespan from | ||
the default. | ||
|
||
## Making handle lifespan shorter than that of the native method | ||
|
||
It is often necessary to make the lifespan of handles shorter than | ||
the lifespan of a native method. For example, consider a native method | ||
that has a loop which creates a number of values and does something | ||
with each of the values, one at a time: | ||
|
||
```C++ | ||
for (int i = 0; i < LOOP_MAX; i++) { | ||
std::string name = std::string("inner-scope") + std::to_string(i); | ||
Value newValue = String::New(info.Env(), name.c_str()); | ||
// do something with neValue | ||
}; | ||
``` | ||
|
||
This would result in a large number of handles being created, consuming | ||
substantial resources. In addition, even though the native code could only | ||
use the most recently created value, all of the previously created | ||
values would also be kept alive since they all share the same scope. | ||
|
||
To handle this case, node-addon-api provides the ability to establish | ||
a new 'scope' to which newly created handles will be associated. Once those | ||
handles are no longer required, the scope can be deleted and any handles | ||
associated with the scope are invalidated. The `HandleScope` | ||
and `EscapableHandleScope` classes are provided by node-addon-api for | ||
creating additional scopes. | ||
|
||
node-addon-api only supports a single nested hierarchy of scopes. There is | ||
only one active scope at any time, and all new handles will be associated | ||
with that scope while it is active. Scopes must be deleted in the reverse | ||
order from which they are opened. In addition, all scopes created within | ||
a native method must be deleted before returning from that method. Since | ||
HandleScopes are typically stack allocated the compiler will take care of | ||
deletion, however, care must be taken to create the scope in the right | ||
place such that you achieve the desired lifetime. | ||
|
||
Taking the earlier example, creating a HandleScope in the innner loop | ||
would ensure that at most a single new value is held alive throughout the | ||
execution of the loop: | ||
|
||
```C | ||
for (int i = 0; i < LOOP_MAX; i++) { | ||
HandleScope scope(info.Env()); | ||
std::string name = std::string("inner-scope") + std::to_string(i); | ||
Value newValue = String::New(info.Env(), name.c_str()); | ||
// do something with neValue | ||
}; | ||
``` | ||
|
||
When nesting scopes, there are cases where a handle from an | ||
inner scope needs to live beyond the lifespan of that scope. node-addon-api | ||
provides the `EscapableHandleScope` with the Escape method | ||
in order to support this case. An escapable scope | ||
allows one object to be 'promoted' so that it 'escapes' the | ||
current scope and the lifespan of the handle changes from the current | ||
scope to that of the outer scope. The Escape method can only be called | ||
once for a given EscapableHandleScope. |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
#include "napi.h" | ||
#include "string.h" | ||
|
||
using namespace Napi; | ||
|
||
Value createScope(const CallbackInfo& info) { | ||
{ | ||
HandleScope scope(info.Env()); | ||
String::New(info.Env(), "inner-scope"); | ||
} | ||
return String::New(info.Env(), "scope"); | ||
} | ||
|
||
Value escapeFromScope(const CallbackInfo& info) { | ||
Value result; | ||
{ | ||
EscapableHandleScope scope(info.Env()); | ||
result = scope.Escape(String::New(info.Env(), "inner-scope")); | ||
} | ||
return result; | ||
} | ||
|
||
#define LOOP_MAX 1000000 | ||
Value stressEscapeFromScope(const CallbackInfo& info) { | ||
Value result; | ||
for (int i = 0; i < LOOP_MAX; i++) { | ||
EscapableHandleScope scope(info.Env()); | ||
std::string name = std::string("inner-scope") + std::to_string(i); | ||
Value newValue = String::New(info.Env(), name.c_str()); | ||
if (i == (LOOP_MAX -1)) { | ||
result = scope.Escape(newValue); | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
Value doubleEscapeFromScope(const CallbackInfo& info) { | ||
Value result; | ||
{ | ||
EscapableHandleScope scope(info.Env()); | ||
result = scope.Escape(String::New(info.Env(), "inner-scope")); | ||
result = scope.Escape(String::New(info.Env(), "inner-scope")); | ||
} | ||
return result; | ||
} | ||
|
||
Object InitHandleScope(Env env) { | ||
Object exports = Object::New(env); | ||
|
||
exports["createScope"] = Function::New(env, createScope); | ||
exports["escapeFromScope"] = Function::New(env, escapeFromScope); | ||
exports["stressEscapeFromScope"] = Function::New(env, stressEscapeFromScope); | ||
exports["doubleEscapeFromScope"] = Function::New(env, doubleEscapeFromScope); | ||
|
||
return exports; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
'use strict'; | ||
const buildType = process.config.target_defaults.default_configuration; | ||
const assert = require('assert'); | ||
|
||
test(require(`./build/${buildType}/binding.node`)); | ||
test(require(`./build/${buildType}/binding_noexcept.node`)); | ||
|
||
function test(binding) { | ||
assert.strictEqual(binding.handlescope.createScope(), 'scope'); | ||
assert.strictEqual(binding.handlescope.escapeFromScope(), 'inner-scope'); | ||
assert.strictEqual(binding.handlescope.stressEscapeFromScope(), 'inner-scope999999'); | ||
assert.throws(() => binding.handlescope.doubleEscapeFromScope(), | ||
Error, | ||
' napi_escape_handle already called on scope'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters