| # 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); |
| Napi::Value newValue = Napi::String::New(info.Env(), name.c_str()); |
| // do something with newValue |
| }; |
| ``` |
| |
| 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 `Napi::HandleScope` |
| and `Napi::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 |
| `Napi::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 `Napi::HandleScope` in the inner 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++) { |
| Napi::HandleScope scope(info.Env()); |
| std::string name = std::string("inner-scope") + std::to_string(i); |
| Napi::Value newValue = Napi::String::New(info.Env(), name.c_str()); |
| // do something with newValue |
| }; |
| ``` |
| |
| 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 `Napi::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 `Napi::EscapableHandleScope`. |