Skip to content

Commit

Permalink
node-api: add new napi_ref type for any napi_value
Browse files Browse the repository at this point in the history
  • Loading branch information
vmoroz committed Apr 26, 2022
1 parent 4f9bc41 commit efd23fd
Show file tree
Hide file tree
Showing 8 changed files with 447 additions and 55 deletions.
123 changes: 105 additions & 18 deletions doc/api/n-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,33 @@ minimum lifetimes explicitly.

For more details, review the [Object lifetime management][].

#### `node_api_reftype`

<!-- YAML
added: REPLACEME
-->

Type of the `napi_ref` reference.
There are two types of reference:

* `node_api_reftype_strong_or_weak` - a reference to an object (`napi_object`)
that can be a strong or a weak reference. References with ref count greater
than 0 are strong references and references with ref count equal to 0 are
weak references. The values referenced by weak references can be collected
at any time by GC if there are no other strong references to the
`napi_value`. To delete the reference we must use `napi_delete_reference`
function.
* `node_api_reftype_strong` - a strong reference to any type of `napi_value`.
When the ref count goes down to 0, the reference is deleted.
The `napi_delete_reference` function must not be used with the strong references.

```c
typedef enum {
node_api_reftype_strong_or_weak,
node_api_reftype_strong,
} node_api_reftype;
```

#### `napi_type_tag`

<!-- YAML
Expand Down Expand Up @@ -1613,9 +1640,9 @@ If it is called more than once an error will be returned.

This API can be called even if there is a pending JavaScript exception.

### References to objects with a lifespan longer than that of the native method
### References to values with a lifespan longer than that of the native method

In some cases an addon will need to be able to create and reference objects
In some cases an addon will need to be able to create and reference values
with a lifespan longer than that of a single native method invocation. For
example, to create a constructor and later use that constructor
in a request to creates instances, it must be possible to reference
Expand All @@ -1625,39 +1652,80 @@ described in the earlier section. The lifespan of a normal handle is
managed by scopes and all scopes must be closed before the end of a native
method.

Node-API provides methods to create persistent references to an object.
Each persistent reference has an associated count with a value of 0
or higher. The count determines if the reference will keep
Node-API provides methods to create persistent references to a `napi_value`.
There are two types of references:

* `node_api_reftype_strong_or_weak` type is only for `napi_object` values.
* `node_api_reftype_strong` type is for any value type.

Each persistent strong/weak reference has an associated count with a value
of 0 or higher. The count determines if the reference will keep
the corresponding object live. References with a count of 0 do not
prevent the object from being collected and are often called 'weak'
references. Any count greater than 0 will prevent the object
from being collected.

References can be created with an initial reference count. The count can
then be modified through [`napi_reference_ref`][] and
[`napi_reference_unref`][]. If an object is collected while the count
for a reference is 0, all subsequent calls to
get the object associated with the reference [`napi_get_reference_value`][]
The strong reference can have a reference count of 1 or higher.
If the count becomes 0, then the reference is deleted.

Strong/weak references can be created with an initial reference count, while
the strong references always use the initial reference count 1.
The count can then be modified through [`napi_reference_ref`][] and
[`napi_reference_unref`][]. If an object is collected while the strong/weak
references count is 0, all subsequent calls to get the object associated with
the reference [`napi_get_reference_value`][]
will return `NULL` for the returned `napi_value`. An attempt to call
[`napi_reference_ref`][] for a reference whose object has been collected
results in an error.

References must be deleted once they are no longer required by the addon. When
a reference is deleted, it will no longer prevent the corresponding object from
being collected. Failure to delete a persistent reference results in
a 'memory leak' with both the native memory for the persistent reference and
the corresponding object on the heap being retained forever.
Strong/weak references must be deleted once they are no longer required by
the addon. When a reference is deleted, it will no longer prevent the
corresponding object from being collected. Failure to delete a persistent
reference results in a 'memory leak' with both the native memory for the
persistent reference and the corresponding object on the heap being retained
forever.

There can be multiple persistent references created which refer to the same
object, each of which will either keep the object live or not based on its
individual count. Multiple persistent references to the same object
value, each of which will either keep the value alive or not based on its
individual count. Multiple persistent references to the same value
can result in unexpectedly keeping alive native memory. The native structures
for a persistent reference must be kept alive until finalizers for the
referenced object are executed. If a new persistent reference is created
for the same object, the finalizers for that object will not be
run and the native memory pointed by the earlier persistent reference
will not be freed. This can be avoided by calling
`napi_delete_reference` in addition to `napi_reference_unref` when possible.
`napi_delete_reference` in addition to `napi_reference_unref` for strong/weak
references when possible. The `napi_delete_reference` must not be called for
the `node_api_reftype_strong` references because the reference is deleted
automatically after `napi_reference_unref` call sets reference count to 0.

#### `node_api_create_reference`

<!-- YAML
added: REPLACEME
-->

```c
NAPI_EXTERN napi_status node_api_create_reference(napi_env env,
napi_value value,
node_api_reftype reftype,
uint32_t initial_refcount,
napi_ref* result);
```

* `[in] env`: The environment that the API is invoked under.
* `[in] value`: `napi_value` to which we want to create a reference.
* `[in] reftype`: Type of the reference.
* `[in] initial_refcount`: Initial reference count for the new reference.
* `[out] result`: `napi_ref` pointing to the new reference.

Returns `napi_ok` if the API succeeded.

For the `node_api_reftype_strong_or_weak` `reftype` set `initial_refcount` to 0
for a weak reference, and value greater than 0 for a strong reference. It
accepts only `napi_object` values.
The `node_api_reftype_strong` ignores the `initial_refcount` and always uses 1.
It accept `napi_value` of any type.

#### `napi_create_reference`

Expand All @@ -1684,6 +1752,25 @@ Returns `napi_ok` if the API succeeded.
This API creates a new reference with the specified reference count
to the `Object` passed in.

#### `node_api_get_reference_type`

<!-- YAML
added: REPLACEME
-->

```c
NAPI_EXTERN napi_status node_api_get_reference_type(napi_env env,
napi_ref ref,
node_api_reftype* result);
```

* `[in] env`: The environment that the API is invoked under.
* `[in] ref`: `napi_ref` which type we want to get.
* `[out] result`: `node_api_reftype` type of the `ref`.

Returns `napi_ok` if the API succeeded and the `result` contains type of the
reference.

#### `napi_delete_reference`

<!-- YAML
Expand Down
39 changes: 29 additions & 10 deletions src/js_native_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -315,34 +315,53 @@ NAPI_EXTERN napi_status napi_get_value_external(napi_env env,

// Methods to control object lifespan

#ifdef NAPI_EXPERIMENTAL
// For node_api_reftype_strong_or_weak set initial_refcount to 0 for a weak
// reference, and value greater than 0 for a strong reference. The
// node_api_reftype_strong ignores the initial_refcount and always uses 1.
NAPI_EXTERN napi_status node_api_create_reference(napi_env env,
napi_value value,
node_api_reftype reftype,
uint32_t initial_refcount,
napi_ref* result);
// Get type of the reference.
NAPI_EXTERN napi_status node_api_get_reference_type(napi_env env,
napi_ref ref,
node_api_reftype* result);
#endif

// Create node_api_reftype_strong_or_weak reference.
// Set initial_refcount to 0 for a weak reference, >0 for a strong reference.
NAPI_EXTERN napi_status napi_create_reference(napi_env env,
napi_value value,
uint32_t initial_refcount,
napi_ref* result);

// Deletes a reference. The referenced value is released, and may
// be GC'd unless there are other references to it.
// Delete a node_api_reftype_strong_or_weak reference. The referenced value is
// released, and may be GC'd unless there are other references to it.
// This method cannot be used with node_api_reftype_strong references.
NAPI_EXTERN napi_status napi_delete_reference(napi_env env, napi_ref ref);

// Increments the reference count, optionally returning the resulting count.
// Increment the reference count, optionally returning the resulting count.
// After this call the reference will be a strong reference because its
// refcount is >0, and the referenced object is effectively "pinned".
// Calling this when the refcount is 0 and the object is unavailable
// results in an error.
NAPI_EXTERN napi_status napi_reference_ref(napi_env env,
napi_ref ref,
uint32_t* result);

// Decrements the reference count, optionally returning the resulting count.
// If the result is 0 the reference is now weak and the object may be GC'd
// at any time if there are no other references. Calling this when the
// refcount is already 0 results in an error.
// Decrement the reference count, optionally returning the resulting count.
// For node_api_reftype_strong_or_weak references if the result is 0, then the
// reference is now weak and the object may be GC'd at any time if there are no
// other references.
// For node_api_reftype_strong references if the result is 0, then the
// reference is deleted and the stored napi_value may be GC'd at any time if
// there are no other references.
// Calling this when the refcount is already 0 results in an error.
NAPI_EXTERN napi_status napi_reference_unref(napi_env env,
napi_ref ref,
uint32_t* result);

// Attempts to get a referenced value. If the reference is weak,
// Attempt to get a referenced value. If the reference is weak,
// the value might no longer be available, in that case the call
// is still successful but the result is NULL.
NAPI_EXTERN napi_status napi_get_reference_value(napi_env env,
Expand Down
7 changes: 7 additions & 0 deletions src/js_native_api_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,11 @@ typedef struct {
} napi_type_tag;
#endif // NAPI_VERSION >= 8

#ifdef NAPI_EXPERIMENTAL
typedef enum {
node_api_reftype_strong_or_weak,
node_api_reftype_strong,
} node_api_reftype;
#endif

#endif // SRC_JS_NATIVE_API_TYPES_H_
Loading

0 comments on commit efd23fd

Please sign in to comment.