node-addon-api 3.0.2 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.clang-format +111 -0
- package/.github/workflows/ci.yml +55 -0
- package/.github/workflows/linter.yml +24 -0
- package/.github/workflows/stale.yml +18 -0
- package/.travis.yml +0 -1
- package/CHANGELOG.md +76 -0
- package/README.md +23 -4
- package/doc/addon.md +5 -5
- package/doc/array_buffer.md +16 -0
- package/doc/async_context.md +1 -1
- package/doc/async_worker.md +1 -1
- package/doc/async_worker_variants.md +141 -40
- package/doc/boolean.md +1 -1
- package/doc/checker-tool.md +1 -1
- package/doc/creating_a_release.md +1 -1
- package/doc/error.md +1 -1
- package/doc/escapable_handle_scope.md +1 -1
- package/doc/function.md +2 -2
- package/doc/function_reference.md +1 -1
- package/doc/handle_scope.md +1 -1
- package/doc/hierarchy.md +1 -1
- package/doc/number.md +1 -1
- package/doc/object_lifetime_management.md +1 -1
- package/doc/object_reference.md +1 -1
- package/doc/object_wrap.md +1 -1
- package/doc/prebuild_tools.md +2 -2
- package/doc/property_descriptor.md +3 -3
- package/doc/threadsafe.md +121 -0
- package/doc/threadsafe_function.md +16 -46
- package/doc/typed_threadsafe_function.md +307 -0
- package/doc/version_management.md +2 -2
- package/napi-inl.h +550 -0
- package/napi.h +195 -0
- package/package-support.json +21 -0
- package/package.json +45 -3
- package/tools/README.md +8 -2
- package/tools/clang-format.js +47 -0
- package/doc/Doxyfile +0 -2450
|
@@ -243,7 +243,9 @@ be safely released.
|
|
|
243
243
|
Note that `Napi::AsyncProgressWorker::ExecutionProcess::Send` merely guarantees
|
|
244
244
|
**eventual** invocation of `Napi::AsyncProgressWorker::OnProgress`, which means
|
|
245
245
|
multiple send might be coalesced into single invocation of `Napi::AsyncProgressWorker::OnProgress`
|
|
246
|
-
with latest data.
|
|
246
|
+
with latest data. If you would like to guarantee that there is one invocation of
|
|
247
|
+
`OnProgress` for every `Send` call, you should use the `Napi::AsyncProgressQueueWorker`
|
|
248
|
+
class instead which is documented further down this page.
|
|
247
249
|
|
|
248
250
|
```cpp
|
|
249
251
|
void Napi::AsyncProgressWorker::ExecutionProcess::Send(const T* data, size_t count) const;
|
|
@@ -269,7 +271,8 @@ function runs in the background out of the **event loop** thread and at the end
|
|
|
269
271
|
the `Napi::AsyncProgressWorker::OnOK` or `Napi::AsyncProgressWorker::OnError` function will be
|
|
270
272
|
called and are executed as part of the event loop.
|
|
271
273
|
|
|
272
|
-
The code below shows a basic example of the `Napi::AsyncProgressWorker` implementation
|
|
274
|
+
The code below shows a basic example of the `Napi::AsyncProgressWorker` implementation along with an
|
|
275
|
+
example of how the counterpart in Javascript would appear:
|
|
273
276
|
|
|
274
277
|
```cpp
|
|
275
278
|
#include <napi.h>
|
|
@@ -281,28 +284,38 @@ using namespace Napi;
|
|
|
281
284
|
|
|
282
285
|
class EchoWorker : public AsyncProgressWorker<uint32_t> {
|
|
283
286
|
public:
|
|
284
|
-
EchoWorker(Function&
|
|
285
|
-
: AsyncProgressWorker(
|
|
287
|
+
EchoWorker(Function& okCallback, std::string& echo)
|
|
288
|
+
: AsyncProgressWorker(okCallback), echo(echo) {}
|
|
286
289
|
|
|
287
290
|
~EchoWorker() {}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
291
|
+
|
|
292
|
+
// This code will be executed on the worker thread
|
|
293
|
+
void Execute(const ExecutionProgress& progress) {
|
|
294
|
+
// Need to simulate cpu heavy task
|
|
295
|
+
// Note: This Send() call is not guaranteed to trigger an equal
|
|
296
|
+
// number of OnProgress calls (read documentation above for more info)
|
|
297
|
+
for (uint32_t i = 0; i < 100; ++i) {
|
|
298
|
+
progress.Send(&i, 1)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
void OnError(const Error &e) {
|
|
303
|
+
HandleScope scope(Env());
|
|
304
|
+
// Pass error onto JS, no data for other parameters
|
|
305
|
+
Callback().Call({String::New(Env(), e.Message())});
|
|
294
306
|
}
|
|
295
|
-
}
|
|
296
307
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
308
|
+
void OnOK() {
|
|
309
|
+
HandleScope scope(Env());
|
|
310
|
+
// Pass no error, give back original data
|
|
311
|
+
Callback().Call({Env().Null(), String::New(Env(), echo)});
|
|
312
|
+
}
|
|
301
313
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
314
|
+
void OnProgress(const uint32_t* data, size_t /* count */) {
|
|
315
|
+
HandleScope scope(Env());
|
|
316
|
+
// Pass no error, no echo data, but do pass on the progress data
|
|
317
|
+
Callback().Call({Env().Null(), Env().Null(), Number::New(Env(), *data)});
|
|
318
|
+
}
|
|
306
319
|
|
|
307
320
|
private:
|
|
308
321
|
std::string echo;
|
|
@@ -327,12 +340,23 @@ using namespace Napi;
|
|
|
327
340
|
|
|
328
341
|
Value Echo(const CallbackInfo& info) {
|
|
329
342
|
// We need to validate the arguments here
|
|
330
|
-
Function cb = info[1].As<Function>();
|
|
331
343
|
std::string in = info[0].As<String>();
|
|
344
|
+
Function cb = info[1].As<Function>();
|
|
332
345
|
EchoWorker* wk = new EchoWorker(cb, in);
|
|
333
346
|
wk->Queue();
|
|
334
347
|
return info.Env().Undefined();
|
|
335
348
|
}
|
|
349
|
+
|
|
350
|
+
// Register the native method for JS to access
|
|
351
|
+
Object Init(Env env, Object exports)
|
|
352
|
+
{
|
|
353
|
+
exports.Set(String::New(env, "echo"), Function::New(env, Echo));
|
|
354
|
+
|
|
355
|
+
return exports;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Register our native addon
|
|
359
|
+
NODE_API_MODULE(nativeAddon, Init)
|
|
336
360
|
```
|
|
337
361
|
|
|
338
362
|
The implementation of a `Napi::AsyncProgressWorker` can be used by creating a
|
|
@@ -341,6 +365,20 @@ asynchronous task ends and other data needed for the computation. Once created,
|
|
|
341
365
|
the only other action needed is to call the `Napi::AsyncProgressWorker::Queue`
|
|
342
366
|
method that will queue the created worker for execution.
|
|
343
367
|
|
|
368
|
+
Lastly, the following Javascript (ES6+) code would be associated the above example:
|
|
369
|
+
|
|
370
|
+
```js
|
|
371
|
+
const { nativeAddon } = require('binding.node');
|
|
372
|
+
|
|
373
|
+
const exampleCallback = (errorResponse, okResponse, progressData) => {
|
|
374
|
+
// Use the data accordingly
|
|
375
|
+
// ...
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// Call our native addon with the paramters of a string and a function
|
|
379
|
+
nativeAddon.echo("example", exampleCallback);
|
|
380
|
+
```
|
|
381
|
+
|
|
344
382
|
# AsyncProgressQueueWorker
|
|
345
383
|
|
|
346
384
|
`Napi::AsyncProgressQueueWorker` acts exactly like `Napi::AsyncProgressWorker`
|
|
@@ -379,7 +417,9 @@ void Napi::AsyncProgressQueueWorker::ExecutionProcess::Send(const T* data, size_
|
|
|
379
417
|
|
|
380
418
|
## Example
|
|
381
419
|
|
|
382
|
-
The code below
|
|
420
|
+
The code below show an example of the `Napi::AsyncProgressQueueWorker` implementation, but
|
|
421
|
+
also demonsrates how to use multiple `Napi::Function`'s if you wish to provide multiple
|
|
422
|
+
callback functions for more object oriented code:
|
|
383
423
|
|
|
384
424
|
```cpp
|
|
385
425
|
#include <napi.h>
|
|
@@ -391,31 +431,55 @@ using namespace Napi;
|
|
|
391
431
|
|
|
392
432
|
class EchoWorker : public AsyncProgressQueueWorker<uint32_t> {
|
|
393
433
|
public:
|
|
394
|
-
EchoWorker(Function&
|
|
395
|
-
: AsyncProgressQueueWorker(
|
|
434
|
+
EchoWorker(Function& okCallback, Function& errorCallback, Function& progressCallback, std::string& echo)
|
|
435
|
+
: AsyncProgressQueueWorker(okCallback), echo(echo) {
|
|
436
|
+
// Set our function references to use them below
|
|
437
|
+
this->errorCallback.Reset(errorCallback, 1);
|
|
438
|
+
this->progressCallback.Reset(progressCallback, 1);
|
|
439
|
+
}
|
|
396
440
|
|
|
397
441
|
~EchoWorker() {}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
442
|
+
|
|
443
|
+
// This code will be executed on the worker thread
|
|
444
|
+
void Execute(const ExecutionProgress& progress) {
|
|
445
|
+
// Need to simulate cpu heavy task to demonstrate that
|
|
446
|
+
// every call to Send() will trigger an OnProgress function call
|
|
447
|
+
for (uint32_t i = 0; i < 100; ++i) {
|
|
448
|
+
progress.Send(&i, 1);
|
|
449
|
+
}
|
|
404
450
|
}
|
|
405
|
-
}
|
|
406
451
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
452
|
+
void OnOK() {
|
|
453
|
+
HandleScope scope(Env());
|
|
454
|
+
// Call our onOkCallback in javascript with the data we were given originally
|
|
455
|
+
Callback().Call({String::New(Env(), echo)});
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
void OnError(const Error &e) {
|
|
459
|
+
HandleScope scope(Env());
|
|
460
|
+
|
|
461
|
+
// We call our callback provided in the constructor with 2 parameters
|
|
462
|
+
if (!this->errorCallback.IsEmpty()) {
|
|
463
|
+
// Call our onErrorCallback in javascript with the error message
|
|
464
|
+
this->errorCallback.Call(Receiver().Value(), {String::New(Env(), e.Message())});
|
|
465
|
+
}
|
|
466
|
+
}
|
|
411
467
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
468
|
+
void OnProgress(const uint32_t* data, size_t /* count */) {
|
|
469
|
+
HandleScope scope(Env());
|
|
470
|
+
|
|
471
|
+
if (!this->progressCallback.IsEmpty()) {
|
|
472
|
+
// Call our onProgressCallback in javascript with each integer from 0 to 99 (inclusive)
|
|
473
|
+
// as this function is triggered from the above Send() calls
|
|
474
|
+
this->progressCallback.Call(Receiver().Value(), {Number::New(Env(), *data)});
|
|
475
|
+
}
|
|
476
|
+
}
|
|
416
477
|
|
|
417
478
|
private:
|
|
418
479
|
std::string echo;
|
|
480
|
+
FunctionReference progressCallback;
|
|
481
|
+
FunctionReference errorCallback;
|
|
482
|
+
|
|
419
483
|
};
|
|
420
484
|
```
|
|
421
485
|
|
|
@@ -439,12 +503,25 @@ using namespace Napi;
|
|
|
439
503
|
|
|
440
504
|
Value Echo(const CallbackInfo& info) {
|
|
441
505
|
// We need to validate the arguments here.
|
|
442
|
-
Function cb = info[1].As<Function>();
|
|
443
506
|
std::string in = info[0].As<String>();
|
|
444
|
-
|
|
507
|
+
Function errorCb = info[1].As<Function>();
|
|
508
|
+
Function okCb = info[2].As<Function>();
|
|
509
|
+
Function progressCb = info[3].As<Function>();
|
|
510
|
+
EchoWorker* wk = new EchoWorker(okCb, errorCb, progressCb, in);
|
|
445
511
|
wk->Queue();
|
|
446
512
|
return info.Env().Undefined();
|
|
447
513
|
}
|
|
514
|
+
|
|
515
|
+
// Register the native method for JS to access
|
|
516
|
+
Object Init(Env env, Object exports)
|
|
517
|
+
{
|
|
518
|
+
exports.Set(String::New(env, "echo"), Function::New(env, Echo));
|
|
519
|
+
|
|
520
|
+
return exports;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Register our native addon
|
|
524
|
+
NODE_API_MODULE(nativeAddon, Init)
|
|
448
525
|
```
|
|
449
526
|
|
|
450
527
|
The implementation of a `Napi::AsyncProgressQueueWorker` can be used by creating a
|
|
@@ -453,4 +530,28 @@ asynchronous task ends and other data needed for the computation. Once created,
|
|
|
453
530
|
the only other action needed is to call the `Napi::AsyncProgressQueueWorker::Queue`
|
|
454
531
|
method that will queue the created worker for execution.
|
|
455
532
|
|
|
533
|
+
Lastly, the following Javascript (ES6+) code would be associated the above example:
|
|
534
|
+
|
|
535
|
+
```js
|
|
536
|
+
const { nativeAddon } = require('binding.node');
|
|
537
|
+
|
|
538
|
+
const onErrorCallback = (msg) => {
|
|
539
|
+
// Use the data accordingly
|
|
540
|
+
// ...
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
const onOkCallback = (echo) => {
|
|
544
|
+
// Use the data accordingly
|
|
545
|
+
// ...
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
const onProgressCallback = (num) => {
|
|
549
|
+
// Use the data accordingly
|
|
550
|
+
// ...
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
// Call our native addon with the paramters of a string and three callback functions
|
|
554
|
+
nativeAddon.echo("example", onErrorCallback, onOkCallback, onProgressCallback);
|
|
555
|
+
```
|
|
556
|
+
|
|
456
557
|
[`Napi::AsyncWorker`]: ./async_worker.md
|
package/doc/boolean.md
CHANGED
package/doc/checker-tool.md
CHANGED
|
@@ -26,7 +26,7 @@ indicating for each addon whether it is an N-API addon.
|
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
The tool accepts the root directory from which to start checking for Node.js
|
|
29
|
-
native addons as a single optional command line parameter. If
|
|
29
|
+
native addons as a single optional command line parameter. If omitted it will
|
|
30
30
|
start checking from the current directory (`.`).
|
|
31
31
|
|
|
32
32
|
[checker tool]: ../tools/check-napi.js
|
|
@@ -36,7 +36,7 @@ the route folder of the repo launch the following command:
|
|
|
36
36
|
```bash
|
|
37
37
|
> changelog-maker
|
|
38
38
|
```
|
|
39
|
-
* Use the output generated by **changelog maker** to
|
|
39
|
+
* Use the output generated by **changelog maker** to update the [CHANGELOG.md](https://github.com/nodejs/node-addon-api/blob/master/CHANGELOG.md)
|
|
40
40
|
following the style used in publishing the previous release.
|
|
41
41
|
|
|
42
42
|
* Add any new contributors to the "contributors" section in the package.json
|
package/doc/error.md
CHANGED
|
@@ -117,4 +117,4 @@ Returns a pointer to a null-terminated string that is used to identify the
|
|
|
117
117
|
exception. This method can be used only if the exception mechanism is enabled.
|
|
118
118
|
|
|
119
119
|
[`Napi::ObjectReference`]: ./object_reference.md
|
|
120
|
-
[`std::exception`]:
|
|
120
|
+
[`std::exception`]: https://cplusplus.com/reference/exception/exception/
|
|
@@ -59,7 +59,7 @@ Napi::EscapableHandleScope::~EscapableHandleScope();
|
|
|
59
59
|
|
|
60
60
|
Deletes the `Napi::EscapableHandleScope` instance and allows any objects/handles created
|
|
61
61
|
in the scope to be collected by the garbage collector. There is no
|
|
62
|
-
guarantee as to when the
|
|
62
|
+
guarantee as to when the garbage collector will do this.
|
|
63
63
|
|
|
64
64
|
### Escape
|
|
65
65
|
|
package/doc/function.md
CHANGED
|
@@ -218,7 +218,7 @@ Napi::Object Napi::Function::New(const std::initializer_list<napi_value>& args)
|
|
|
218
218
|
```
|
|
219
219
|
|
|
220
220
|
- `[in] args`: Initializer list of JavaScript values as `napi_value` representing
|
|
221
|
-
the arguments of the
|
|
221
|
+
the arguments of the constructor function.
|
|
222
222
|
|
|
223
223
|
Returns a new JavaScript object.
|
|
224
224
|
|
|
@@ -245,7 +245,7 @@ object.
|
|
|
245
245
|
Napi::Object Napi::Function::New(size_t argc, const napi_value* args) const;
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
-
- `[in] argc`: The number of the arguments passed to the
|
|
248
|
+
- `[in] argc`: The number of the arguments passed to the constructor function.
|
|
249
249
|
- `[in] args`: Array of JavaScript values as `napi_value` representing the
|
|
250
250
|
arguments of the constructor function.
|
|
251
251
|
|
|
@@ -73,7 +73,7 @@ Napi::Object Napi::FunctionReference::New(const std::initializer_list<napi_value
|
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
- `[in] args`: Initializer list of JavaScript values as `napi_value` representing
|
|
76
|
-
the arguments of the
|
|
76
|
+
the arguments of the constructor function.
|
|
77
77
|
|
|
78
78
|
Returns a new JavaScript object.
|
|
79
79
|
|
package/doc/handle_scope.md
CHANGED
|
@@ -54,7 +54,7 @@ Napi::HandleScope::~HandleScope();
|
|
|
54
54
|
|
|
55
55
|
Deletes the `Napi::HandleScope` instance and allows any objects/handles created
|
|
56
56
|
in the scope to be collected by the garbage collector. There is no
|
|
57
|
-
guarantee as to when the
|
|
57
|
+
guarantee as to when the garbage collector will do this.
|
|
58
58
|
|
|
59
59
|
### Env
|
|
60
60
|
|
package/doc/hierarchy.md
CHANGED
|
@@ -88,4 +88,4 @@
|
|
|
88
88
|
[`Napi::Uint8Array`]: ./typed_array_of.md
|
|
89
89
|
[`Napi::Value`]: ./value.md
|
|
90
90
|
[`Napi::VersionManagement`]: ./version_management.md
|
|
91
|
-
[`std::exception`]:
|
|
91
|
+
[`std::exception`]: https://cplusplus.com/reference/exception/exception/
|
package/doc/number.md
CHANGED
|
@@ -60,7 +60,7 @@ a native method must be deleted before returning from that method. Since
|
|
|
60
60
|
deletion, however, care must be taken to create the scope in the right
|
|
61
61
|
place such that you achieve the desired lifetime.
|
|
62
62
|
|
|
63
|
-
Taking the earlier example, creating a `Napi::HandleScope` in the
|
|
63
|
+
Taking the earlier example, creating a `Napi::HandleScope` in the inner loop
|
|
64
64
|
would ensure that at most a single new value is held alive throughout the
|
|
65
65
|
execution of the loop:
|
|
66
66
|
|
package/doc/object_reference.md
CHANGED
package/doc/object_wrap.md
CHANGED
|
@@ -43,7 +43,7 @@ Napi::Object Example::Init(Napi::Env env, Napi::Object exports) {
|
|
|
43
43
|
|
|
44
44
|
Napi::FunctionReference* constructor = new Napi::FunctionReference();
|
|
45
45
|
|
|
46
|
-
// Create a
|
|
46
|
+
// Create a persistent reference to the class constructor. This will allow
|
|
47
47
|
// a function called on a class prototype and a function
|
|
48
48
|
// called on instance of a class to be distinguished from each other.
|
|
49
49
|
*constructor = Napi::Persistent(func);
|
package/doc/prebuild_tools.md
CHANGED
|
@@ -5,8 +5,8 @@ In order to install a native add-on it's important to have all the necessary
|
|
|
5
5
|
dependencies installed and well configured (see the [setup](setup.md) section).
|
|
6
6
|
The end-user will need to compile the add-on when they will do an `npm install`
|
|
7
7
|
and in some cases this could create problems. To avoid the compilation process it's
|
|
8
|
-
possible to
|
|
9
|
-
and architectures. The prebuild tools help to create and
|
|
8
|
+
possible to distribute the native add-on in pre-built form for different platform
|
|
9
|
+
and architectures. The prebuild tools help to create and distribute the pre-built
|
|
10
10
|
form of a native add-on.
|
|
11
11
|
|
|
12
12
|
The following list report known tools that are compatible with **N-API**:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Property Descriptor
|
|
2
2
|
|
|
3
|
-
A [`Napi::Object`](object.md) can be assigned
|
|
3
|
+
A [`Napi::Object`](object.md) can be assigned properties via its [`DefineProperty`](object.md#defineproperty) and [`DefineProperties`](object.md#defineproperties) functions, which take PropertyDescriptor(s) as their parameters. The `Napi::PropertyDescriptor` can contain either values or functions, which are then assigned to the `Napi::Object`. Note that a single instance of a `Napi::PropertyDescriptor` class can only contain either one value, or at most two functions. PropertyDescriptors can only be created through the class methods [`Accessor`](#accessor), [`Function`](#function), or [`Value`](#value), each of which return a new static instance of a `Napi::PropertyDescriptor`.
|
|
4
4
|
|
|
5
5
|
## Example
|
|
6
6
|
|
|
@@ -150,7 +150,7 @@ static Napi::PropertyDescriptor Napi::PropertyDescriptor::Accessor (
|
|
|
150
150
|
void *data = nullptr);
|
|
151
151
|
```
|
|
152
152
|
|
|
153
|
-
* `[in] env`: The
|
|
153
|
+
* `[in] env`: The environment in which to create this accessor.
|
|
154
154
|
* `[in] object`: The object on which the accessor will be defined.
|
|
155
155
|
* `[in] name`: The name used for the getter function.
|
|
156
156
|
* `[in] getter`: A getter function.
|
|
@@ -199,7 +199,7 @@ static Napi::PropertyDescriptor Napi::PropertyDescriptor::Accessor (
|
|
|
199
199
|
void *data = nullptr);
|
|
200
200
|
```
|
|
201
201
|
|
|
202
|
-
* `[in] env`: The
|
|
202
|
+
* `[in] env`: The environment in which to create this accessor.
|
|
203
203
|
* `[in] object`: The object on which the accessor will be defined.
|
|
204
204
|
* `[in] name`: The name of the getter and setter function.
|
|
205
205
|
* `[in] getter`: The getter function.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Thread-safe Functions
|
|
2
|
+
|
|
3
|
+
JavaScript functions can normally only be called from a native addon's main
|
|
4
|
+
thread. If an addon creates additional threads, then node-addon-api functions
|
|
5
|
+
that require a `Napi::Env`, `Napi::Value`, or `Napi::Reference` must not be
|
|
6
|
+
called from those threads.
|
|
7
|
+
|
|
8
|
+
When an addon has additional threads and JavaScript functions need to be invoked
|
|
9
|
+
based on the processing completed by those threads, those threads must
|
|
10
|
+
communicate with the addon's main thread so that the main thread can invoke the
|
|
11
|
+
JavaScript function on their behalf. The thread-safe function APIs provide an
|
|
12
|
+
easy way to do this. These APIs provide two types --
|
|
13
|
+
[`Napi::ThreadSafeFunction`](threadsafe_function.md) and
|
|
14
|
+
[`Napi::TypedThreadSafeFunction`](typed_threadsafe_function.md) -- as well as
|
|
15
|
+
APIs to create, destroy, and call objects of this type. The differences between
|
|
16
|
+
the two are subtle and are [highlighted below](#implementation-differences).
|
|
17
|
+
Regardless of which type you choose, the APIs between the two are similar.
|
|
18
|
+
|
|
19
|
+
`Napi::[Typed]ThreadSafeFunction::New()` creates a persistent reference that
|
|
20
|
+
holds a JavaScript function which can be called from multiple threads. The calls
|
|
21
|
+
happen asynchronously. This means that values with which the JavaScript callback
|
|
22
|
+
is to be called will be placed in a queue, and, for each value in the queue, a
|
|
23
|
+
call will eventually be made to the JavaScript function.
|
|
24
|
+
|
|
25
|
+
`Napi::[Typed]ThreadSafeFunction` objects are destroyed when every thread which
|
|
26
|
+
uses the object has called `Release()` or has received a return status of
|
|
27
|
+
`napi_closing` in response to a call to `BlockingCall()` or `NonBlockingCall()`.
|
|
28
|
+
The queue is emptied before the `Napi::[Typed]ThreadSafeFunction` is destroyed.
|
|
29
|
+
It is important that `Release()` be the last API call made in conjunction with a
|
|
30
|
+
given `Napi::[Typed]ThreadSafeFunction`, because after the call completes, there
|
|
31
|
+
is no guarantee that the `Napi::[Typed]ThreadSafeFunction` is still allocated.
|
|
32
|
+
For the same reason it is also important that no more use be made of a
|
|
33
|
+
thread-safe function after receiving a return value of `napi_closing` in
|
|
34
|
+
response to a call to `BlockingCall()` or `NonBlockingCall()`. Data associated
|
|
35
|
+
with the `Napi::[Typed]ThreadSafeFunction` can be freed in its `Finalizer`
|
|
36
|
+
callback which was passed to `[Typed]ThreadSafeFunction::New()`.
|
|
37
|
+
|
|
38
|
+
Once the number of threads making use of a `Napi::[Typed]ThreadSafeFunction`
|
|
39
|
+
reaches zero, no further threads can start making use of it by calling
|
|
40
|
+
`Acquire()`. In fact, all subsequent API calls associated with it, except
|
|
41
|
+
`Release()`, will return an error value of `napi_closing`.
|
|
42
|
+
|
|
43
|
+
## Implementation Differences
|
|
44
|
+
|
|
45
|
+
The choice between `Napi::ThreadSafeFunction` and
|
|
46
|
+
`Napi::TypedThreadSafeFunction` depends largely on how you plan to execute your
|
|
47
|
+
native C++ code (the "callback") on the Node.js thread.
|
|
48
|
+
|
|
49
|
+
### [`Napi::ThreadSafeFunction`](threadsafe_function.md)
|
|
50
|
+
|
|
51
|
+
This API is designed without N-API 5 native support for [the optional JavaScript
|
|
52
|
+
function callback feature](https://github.com/nodejs/node/commit/53297e66cb).
|
|
53
|
+
|
|
54
|
+
This API has some dynamic functionality, in that:
|
|
55
|
+
- The `[Non]BlockingCall()` methods provide a `Napi::Function` parameter as the
|
|
56
|
+
callback to run when processing the data item on the main thread -- the
|
|
57
|
+
`CallJs` callback. Since the callback is a parameter, it can be changed for
|
|
58
|
+
every call.
|
|
59
|
+
- Different C++ data types may be passed with each call of `[Non]BlockingCall()`
|
|
60
|
+
to match the specific data type as specified in the `CallJs` callback.
|
|
61
|
+
|
|
62
|
+
Note that this functionality comes with some **additional overhead** and
|
|
63
|
+
situational **memory leaks**:
|
|
64
|
+
- The API acts as a "broker" between the underlying `napi_threadsafe_function`,
|
|
65
|
+
and dynamically constructs a wrapper for your callback on the heap for every
|
|
66
|
+
call to `[Non]BlockingCall()`.
|
|
67
|
+
- In acting in this "broker" fashion, the API will call the underlying "make
|
|
68
|
+
call" N-API method on this packaged item. If the API has determined the
|
|
69
|
+
thread-safe function is no longer accessible (eg. all threads have released
|
|
70
|
+
yet there are still items on the queue), **the callback passed to
|
|
71
|
+
[Non]BlockingCall will not execute**. This means it is impossible to perform
|
|
72
|
+
clean-up for calls that never execute their `CallJs` callback. **This may lead
|
|
73
|
+
to memory leaks** if you are dynamically allocating memory.
|
|
74
|
+
- The `CallJs` does not receive the thread-safe function's context as a
|
|
75
|
+
parameter. In order for the callback to access the context, it must have a
|
|
76
|
+
reference to either (1) the context directly, or (2) the thread-safe function
|
|
77
|
+
to call `GetContext()`. Furthermore, the `GetContext()` method is not
|
|
78
|
+
_type-safe_, as the method returns an object that can be "any-casted", instead
|
|
79
|
+
of having a static type.
|
|
80
|
+
|
|
81
|
+
### [`Napi::TypedThreadSafeFunction`](typed_threadsafe_function.md)
|
|
82
|
+
|
|
83
|
+
The `TypedThreadSafeFunction` class is a new implementation to address the
|
|
84
|
+
drawbacks listed above. The API is designed with N-API 5's support of an
|
|
85
|
+
optional function callback. The API will correctly allow developers to pass
|
|
86
|
+
`std::nullptr` instead of a `const Function&` for the callback function
|
|
87
|
+
specified in `::New`. It also provides helper APIs to _target_ N-API 4 and
|
|
88
|
+
construct a no-op `Function` **or** to target N-API 5 and "construct" a
|
|
89
|
+
`std::nullptr` callback. This allows a single codebase to use the same APIs,
|
|
90
|
+
with just a switch of the `NAPI_VERSION` compile-time constant.
|
|
91
|
+
|
|
92
|
+
The removal of the dynamic call functionality has the following implications:
|
|
93
|
+
- The API does _not_ act as a "broker" compared to the
|
|
94
|
+
`Napi::ThreadSafeFunction`. Once Node.js finalizes the thread-safe function,
|
|
95
|
+
the `CallJs` callback will execute with an empty `Napi::Env` for any remaining
|
|
96
|
+
items on the queue. This provides the ability to handle any necessary cleanup
|
|
97
|
+
of the item's data.
|
|
98
|
+
- The callback _does_ receive the context as a parameter, so a call to
|
|
99
|
+
`GetContext()` is _not_ necessary. This context type is specified as the
|
|
100
|
+
**first template argument** specified to `::New`, ensuring type safety.
|
|
101
|
+
- The `New()` constructor accepts the `CallJs` callback as the **second type
|
|
102
|
+
argument**. The callback must be statically defined for the API to access it.
|
|
103
|
+
This affords the ability to statically pass the context as the correct type
|
|
104
|
+
across all methods.
|
|
105
|
+
- Only one C++ data type may be specified to every call to `[Non]BlockingCall()`
|
|
106
|
+
-- the **third template argument** specified to `::New`. Any "dynamic call
|
|
107
|
+
data" must be implemented by the user.
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
### Usage Suggestions
|
|
111
|
+
|
|
112
|
+
In summary, it may be best to use `Napi::TypedThreadSafeFunction` if:
|
|
113
|
+
|
|
114
|
+
- static, compile-time support for targeting N-API 4 or 5+ with an optional
|
|
115
|
+
JavaScript callback feature is desired;
|
|
116
|
+
- the callback can have `static` storage class and will not change across calls
|
|
117
|
+
to `[Non]BlockingCall()`;
|
|
118
|
+
- cleanup of items' data is required (eg. deleting dynamically-allocated data
|
|
119
|
+
that is created at the caller level).
|
|
120
|
+
|
|
121
|
+
Otherwise, `Napi::ThreadSafeFunction` may be a better choice.
|