From 15c557a5d4ec6cb23344bceda71273ee5672a239 Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Thu, 26 Apr 2018 17:47:45 -0400 Subject: [PATCH] test: add basic tests and doc for scopes --- README.md | 2 +- doc/escapable_handle_sope.md | 82 +++++++++++++++++++++++++++++-- doc/object_lifetime_management.md | 80 ++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 doc/object_lifetime_management.md diff --git a/README.md b/README.md index ebf6fec2d..2516ec0b5 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ values. Concepts and operations generally map to ideas specified in the - [PropertyDescriptor](doc/property_descriptor.md) - [Error Handling](doc/error_handling.md) - [Error](doc/error.md) - - [Object Lifettime Management](doc/object_lifetime_management.md) + - [Object Lifetime Management](doc/object_lifetime_management.md) - [HandleScope](doc/handle_scope.md) - [EscapableHandleScope](doc/escapable_handle_scope.md) - [Working with JavaScript Values](doc/working_with_javascript_values.md) diff --git a/doc/escapable_handle_sope.md b/doc/escapable_handle_sope.md index 37d6ddd66..351f6a1de 100644 --- a/doc/escapable_handle_sope.md +++ b/doc/escapable_handle_sope.md @@ -1,5 +1,79 @@ -# Escapable handle scope +# EscapableHandleScope -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/) \ No newline at end of file +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 while native code is using them. A handle may be +created any time a node-addon-api Value(and subclasses) are 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 title "Object lifetime management". + +## Methods + +### Constructor + +Creates a new escapable handle scope. + +```cpp +EscapableHandleScope::New(Napi:Env env); +``` + +- `[in] Env`: The environment in which to construct the EscapableHandleScope object. + +Creates a new EscapableHandleScope + +### Constructor + +Creates a new escapable handle scope. + +```cpp +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. + +Creates 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 calls 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 calls 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 +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 and exception will be thrown. + +### Env + +```cpp +Napi:Env Env() const; +``` + +Returns the Napi:Env associated with the EscapableHandleScope. diff --git a/doc/object_lifetime_management.md b/doc/object_lifetime_management.md new file mode 100644 index 000000000..10574d103 --- /dev/null +++ b/doc/object_lifetime_management.md @@ -0,0 +1,80 @@ +# Object lifetime management + +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 for any new node-addon-api +Value(and subclasses) 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 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. +