@@ -98,6 +98,140 @@ the `.node` suffix).
9898In the ` hello.cc ` example, then, the initialization function is ` Initialize `
9999and the addon module name is ` addon ` .
100100
101+ When building addons with ` node-gyp ` , using the macro ` NODE_GYP_MODULE_NAME ` as
102+ the first parameter of ` NODE_MODULE() ` will ensure that the name of the final
103+ binary will be passed to ` NODE_MODULE() ` .
104+
105+ ### Context-aware addons
106+
107+ There are environments in which Node.js addons may need to be loaded multiple
108+ times in multiple contexts. For example, the [ Electron] [ ] runtime runs multiple
109+ instances of Node.js in a single process. Each instance will have its own
110+ ` require() ` cache, and thus each instance will need a native addon to behave
111+ correctly when loaded via ` require() ` . From the addon's perspective, this means
112+ that it must support multiple initializations.
113+
114+ A context-aware addon can be constructed by using the macro
115+ ` NODE_MODULE_INITIALIZER ` , which expands to the name of a function which Node.js
116+ will expect to find when it loads an addon. An addon can thus be initialized as
117+ in the following example:
118+
119+ ``` cpp
120+ using namespace v8 ;
121+
122+ extern "C" NODE_MODULE_EXPORT void
123+ NODE_MODULE_INITIALIZER (Local<Object > exports,
124+ Local<Value > module,
125+ Local<Context > context) {
126+ /* Perform addon initialization steps here. * /
127+ }
128+ ```
129+
130+ Another option is to use the macro `NODE_MODULE_INIT()`, which will also
131+ construct a context-aware addon. Unlike `NODE_MODULE()`, which is used to
132+ construct an addon around a given addon initializer function,
133+ `NODE_MODULE_INIT()` serves as the declaration of such an initializer to be
134+ followed by a function body.
135+
136+ The following three variables may be used inside the function body following an
137+ invocation of `NODE_MODULE_INIT()`:
138+ * `Local<Object> exports`,
139+ * `Local<Value> module`, and
140+ * `Local<Context> context`
141+
142+ The choice to build a context-aware addon carries with it the responsibility of
143+ carefully managing global static data. Since the addon may be loaded multiple
144+ times, potentially even from different threads, any global static data stored
145+ in the addon must be properly protected, and must not contain any persistent
146+ references to JavaScript objects. The reason for this is that JavaScript
147+ objects are only valid in one context, and will likely cause a crash when
148+ accessed from the wrong context or from a different thread than the one on which
149+ they were created.
150+
151+ The context-aware addon can be structured to avoid global static data by
152+ performing the following steps:
153+ * defining a class which will hold per-addon-instance data. Such
154+ a class should include a `v8::Persistent<v8::Object>` which will hold a weak
155+ reference to the addon's `exports` object. The callback associated with the weak
156+ reference will then destroy the instance of the class.
157+ * constructing an instance of this class in the addon initializer such that the
158+ `v8::Persistent<v8::Object>` is set to the `exports` object.
159+ * storing the instance of the class in a `v8::External`, and
160+ * passing the `v8::External` to all methods exposed to JavaScript by passing it
161+ to the `v8::FunctionTemplate` constructor which creates the native-backed
162+ JavaScript functions. The `v8::FunctionTemplate` constructor's third parameter
163+ accepts the `v8::External`.
164+
165+ This will ensure that the per-addon-instance data reaches each binding that can
166+ be called from JavaScript. The per-addon-instance data must also be passed into
167+ any asynchronous callbacks the addon may create.
168+
169+ The following example illustrates the implementation of a context-aware addon:
170+
171+ ```cpp
172+ #include <node.h>
173+
174+ using namespace v8;
175+
176+ class AddonData {
177+ public:
178+ AddonData(Isolate* isolate, Local<Object> exports):
179+ call_count(0) {
180+ // Link the existence of this object instance to the existence of exports.
181+ exports_.Reset(isolate, exports);
182+ exports_.SetWeak(this, DeleteMe, WeakCallbackType::kParameter);
183+ }
184+
185+ ~AddonData() {
186+ if (!exports_.IsEmpty()) {
187+ // Reset the reference to avoid leaking data.
188+ exports_.ClearWeak();
189+ exports_.Reset();
190+ }
191+ }
192+
193+ // Per-addon data.
194+ int call_count;
195+
196+ private:
197+ // Method to call when "exports" is about to be garbage-collected.
198+ static void DeleteMe(const WeakCallbackInfo<AddonData>& info) {
199+ delete info.GetParameter();
200+ }
201+
202+ // Weak handle to the "exports" object. An instance of this class will be
203+ // destroyed along with the exports object to which it is weakly bound.
204+ v8::Persistent<v8::Object> exports_;
205+ };
206+
207+ static void Method(const v8::FunctionCallbackInfo<v8::Value>& info) {
208+ // Retrieve the per-addon-instance data.
209+ AddonData* data =
210+ reinterpret_cast<AddonData*>(info.Data().As<External>()->Value());
211+ data->call_count++;
212+ info.GetReturnValue().Set((double)data->call_count);
213+ }
214+
215+ // Initialize this addon to be context-aware.
216+ NODE_MODULE_INIT(/* exports, module, context */) {
217+ Isolate* isolate = context->GetIsolate();
218+
219+ // Create a new instance of AddonData for this instance of the addon.
220+ AddonData* data = new AddonData(isolate, exports);
221+ // Wrap the data in a v8::External so we can pass it to the method we expose.
222+ Local<External> external = External::New(isolate, data);
223+
224+ // Expose the method "Method" to JavaScript, and make sure it receives the
225+ // per-addon-instance data we created above by passing `external` as the
226+ // third parameter to the FunctionTemplate constructor.
227+ exports->Set(context,
228+ String::NewFromUtf8(isolate, "method", NewStringType::kNormal)
229+ .ToLocalChecked(),
230+ FunctionTemplate::New(isolate, Method, external)
231+ ->GetFunction(context).ToLocalChecked()).FromJust();
232+ }
233+ ```
234+
101235### Building
102236
103237Once the source code has been written, it must be compiled into the binary
@@ -1162,6 +1296,7 @@ Test in JavaScript by running:
11621296require('./build/Release/addon');
11631297```
11641298
1299+ [ Electron]: https:// electronjs.org/
11651300[Embedder' s Guide]: https://github.com/v8/v8/wiki/Embedder' s%20Guide
11661301[Linking to Node.js' own dependencies]: #addons_linking_to_node_js_own_dependencies
11671302[Native Abstractions for Node.js]: https://github.com/nodejs/nan
0 commit comments