Skip to content

Commit

Permalink
doc: update context-aware section of addon doc
Browse files Browse the repository at this point in the history
Replace the portion of the context-aware addon example that advocates
for managing the life cycle of the per-addon-instance data using a
weak reference with code that illustrates how to manage its life cycle
using an environment cleanup hook.

Signed-off-by: Gabriel Schulhof <gabriel.schulhof@intel.com>
PR-URL: #28659
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
  • Loading branch information
Gabriel Schulhof committed Mar 31, 2020
1 parent accc984 commit e158218
Showing 1 changed file with 32 additions and 29 deletions.
61 changes: 32 additions & 29 deletions doc/api/addons.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,18 +155,25 @@ they were created.
The context-aware addon can be structured to avoid global static data by
performing the following steps:
* defining a class which will hold per-addon-instance data. Such
a class should include a `v8::Global<v8::Object>` which will hold a weak
reference to the addon's `exports` object. The callback associated with the weak
reference will then destroy the instance of the class.
* constructing an instance of this class in the addon initializer such that the
`v8::Global<v8::Object>` is set to the `exports` object.
* storing the instance of the class in a `v8::External`, and
* passing the `v8::External` to all methods exposed to JavaScript by passing it
to the `v8::FunctionTemplate` constructor which creates the native-backed
JavaScript functions. The `v8::FunctionTemplate` constructor's third parameter
accepts the `v8::External`.
* Define a class which will hold per-addon-instance data and which has a static
member of the form
```C++
static void DeleteInstance(void* data) {
// Cast `data` to an instance of the class and delete it.
}
```
* Heap-allocate an instance of this class in the addon initializer. This can be
accomplished using the `new` keyword.
* Call `node::AddEnvironmentCleanupHook()`, passing it the above-created
instance and a pointer to `DeleteInstance()`. This will ensure the instance is
deleted when the environment is torn down.
* Store the instance of the class in a `v8::External`, and
* Pass the `v8::External` to all methods exposed to JavaScript by passing it
to `v8::FunctionTemplate::New()` or `v8::Function::New()` which creates the
native-backed JavaScript functions. The third parameter of
`v8::FunctionTemplate::New()` or `v8::Function::New()` accepts the
`v8::External` and makes it available in the native callback using the
`v8::FunctionCallbackInfo::Data()` method.

This will ensure that the per-addon-instance data reaches each binding that can
be called from JavaScript. The per-addon-instance data must also be passed into
Expand All @@ -181,25 +188,18 @@ using namespace v8;

class AddonData {
public:
AddonData(Isolate* isolate, Local<Object> exports):
explicit AddonData(Isolate* isolate):
call_count(0) {
// Link the existence of this object instance to the existence of exports.
exports_.Reset(isolate, exports);
exports_.SetWeak(this, DeleteMe, WeakCallbackType::kParameter);
// Ensure this per-addon-instance data is deleted at environment cleanup.
node::AddEnvironmentCleanupHook(isolate, DeleteInstance, this);
}

// Per-addon data.
int call_count;

private:
// Method to call when "exports" is about to be garbage-collected.
static void DeleteMe(const WeakCallbackInfo<AddonData>& info) {
delete info.GetParameter();
static void DeleteInstance(void* data) {
delete static_cast<AddonData*>(data);
}
// Weak handle to the "exports" object. An instance of this class will be
// destroyed along with the exports object to which it is weakly bound.
v8::Global<v8::Object> exports_;
};

static void Method(const v8::FunctionCallbackInfo<v8::Value>& info) {
Expand All @@ -214,14 +214,17 @@ static void Method(const v8::FunctionCallbackInfo<v8::Value>& info) {
NODE_MODULE_INIT(/* exports, module, context */) {
Isolate* isolate = context->GetIsolate();

// Create a new instance of AddonData for this instance of the addon.
AddonData* data = new AddonData(isolate, exports);
// Wrap the data in a v8::External so we can pass it to the method we expose.
// Create a new instance of `AddonData` for this instance of the addon and
// tie its life cycle to that of the Node.js environment.
AddonData* data = new AddonData(isolate);

// Wrap the data in a `v8::External` so we can pass it to the method we
// expose.
Local<External> external = External::New(isolate, data);

// Expose the method "Method" to JavaScript, and make sure it receives the
// Expose the method `Method` to JavaScript, and make sure it receives the
// per-addon-instance data we created above by passing `external` as the
// third parameter to the FunctionTemplate constructor.
// third parameter to the `FunctionTemplate` constructor.
exports->Set(context,
String::NewFromUtf8(isolate, "method", NewStringType::kNormal)
.ToLocalChecked(),
Expand Down

0 comments on commit e158218

Please sign in to comment.