1
- # TODO
2
- - Document new N-API 5+ only methods
3
- - Continue with examples
4
-
5
1
# ThreadSafeFunctionEx
6
2
7
3
The ` Napi::ThreadSafeFunctionEx ` type provides APIs for threads to communicate
8
4
with the addon's main thread to invoke JavaScript functions on their behalf. The
9
5
type is a three-argument templated class, each argument representing the type
10
6
of:
11
- - ` ContextType = std::nullptr_t ` : The threadsafe function's context. By default,
7
+ - ` ContextType = std::nullptr_t ` : The thread-safe function's context. By default,
12
8
a TSFN has no context.
13
9
- ` DataType = void* ` : The data to use in the native callback. By default, a TSFN
14
10
can accept * any* data type.
15
- - ` Callback = void*(Napi::Env, Napi::Function, ContextType*, DataType*) ` : The
16
- callback to run for each item added to the queue.
11
+ - `Callback = void* (Napi::Env, Napi::Function jsCallback, ContextType* ,
12
+ DataType* )`: The callback to run for each item added to the queue. If no
13
+ ` Callback ` is given, the API will call the function ` jsCallback ` with no
14
+ arguments.
17
15
18
16
Documentation can be found for an [ overview of the API] ( threadsafe.md ) , as well
19
17
as [ differences between the two thread-safe function
@@ -40,46 +38,20 @@ Napi::ThreadSafeFunctionEx<ContextType, DataType, Callback>::ThreadSafeFunctionE
40
38
- ` tsfn ` : The ` napi_threadsafe_function ` which is a handle for an existing
41
39
thread-safe function.
42
40
43
- Returns a non-empty ` Napi::ThreadSafeFunctionEx ` instance.
44
-
45
- ### New
46
-
47
- Creates a new instance of the ` Napi::ThreadSafeFunctionEx ` object.
48
-
49
- ``` cpp
50
- New (napi_env env,
51
- const Function& callback,
52
- const Object& resource,
53
- ResourceString resourceName,
54
- size_t maxQueueSize,
55
- size_t initialThreadCount,
56
- ContextType* context = nullptr);
57
- ```
58
-
59
- - `env`: The `napi_env` environment in which to construct the
60
- `Napi::ThreadSafeFunction` object.
61
- - `callback`: The `Function` to call from another thread.
62
- - `resource`: An object associated with the async work that will be passed to
63
- possible async_hooks init hooks.
64
- - `resourceName`: A JavaScript string to provide an identifier for the kind of
65
- resource that is being provided for diagnostic information exposed by the
66
- async_hooks API.
67
- - `maxQueueSize`: Maximum size of the queue. `0` for no limit.
68
- - `initialThreadCount`: The initial number of threads, including the main
69
- thread, which will be making use of this function.
70
- - `[optional] context`: Data to attach to the resulting `ThreadSafeFunction`.
71
- Can be retreived via `GetContext()`.
72
-
73
- Returns a non-empty `Napi::ThreadSafeFunction` instance.
41
+ Returns a non-empty ` Napi::ThreadSafeFunctionEx ` instance. To ensure the API
42
+ statically handles the correct return type for ` GetContext() ` and
43
+ ` [Non]BlockingCall() ` , pass the proper type arguments to
44
+ ` Napi::ThreadSafeFunctionEx ` .
74
45
75
46
### New
76
47
77
- Creates a new instance of the `Napi::ThreadSafeFunctionEx` object with a
78
- finalizer that runs when the object is being destroyed.
48
+ Creates a new instance of the ` Napi::ThreadSafeFunctionEx ` object. The ` New `
49
+ function has several overloads for the various optional parameters: skip the
50
+ optional parameter for that specific overload.
79
51
80
52
``` cpp
81
53
New (napi_env env,
82
- const Function& callback,
54
+ CallbackType callback,
83
55
const Object& resource,
84
56
ResourceString resourceName,
85
57
size_t maxQueueSize,
@@ -91,9 +63,9 @@ New(napi_env env,
91
63
92
64
- `env`: The `napi_env` environment in which to construct the
93
65
`Napi::ThreadSafeFunction` object.
94
- - ` callback ` : The ` Function ` to call from another thread.
95
- - ` resource ` : An object associated with the async work that will be passed to
96
- possible async_hooks init hooks.
66
+ - `[optional] callback`: The `Function` to call from another thread.
67
+ - `[optional] resource`: An object associated with the async work that will be
68
+ passed to possible async_hooks init hooks.
97
69
- `resourceName`: A JavaScript string to provide an identifier for the kind of
98
70
resource that is being provided for diagnostic information exposed by the
99
71
async_hooks API.
@@ -102,19 +74,31 @@ New(napi_env env,
102
74
thread, which will be making use of this function.
103
75
- `[optional] context`: Data to attach to the resulting `ThreadSafeFunction`.
104
76
Can be retreived via `GetContext()`.
105
- - ` finalizeCallback ` : Function to call when the ` ThreadSafeFunctionEx ` is being
106
- destroyed. This callback will be invoked on the main thread when the
107
- thread-safe function is about to be destroyed. It receives the context and the
108
- finalize data given during construction (if given), and provides an
109
- opportunity for cleaning up after the threads e.g. by calling
110
- ` uv_thread_join() ` . It is important that, aside from the main loop thread,
111
- there be no threads left using the thread-safe function after the finalize
112
- callback completes. Must implement `void operator()(Env env, DataType * data ,
113
- ContextType* hint)`.
77
+ - `[optional] finalizeCallback`: Function to call when the
78
+ `ThreadSafeFunctionEx` is being destroyed. This callback will be invoked on
79
+ the main thread when the thread -safe function is about to be destroyed. It
80
+ receives the context and the finalize data given during construction (if
81
+ given), and provides an opportunity for cleaning up after the threads e.g. by
82
+ calling `uv_thread_join()`. It is important that, aside from the main loop
83
+ thread, there be no threads left using the thread-safe function after the
84
+ finalize callback completes. Must implement `void operator()(Env env,
85
+ DataType* data, ContextType* hint)`.
114
86
- `[optional] data`: Data to be passed to `finalizeCallback`.
115
87
116
88
Returns a non-empty `Napi::ThreadSafeFunctionEx` instance.
117
89
90
+ Depending on the targetted `NAPI_VERSION`, the API has different implementations
91
+ for `CallbackType callback`.
92
+
93
+ When targetting version 4, `CallbackType` is:
94
+ - `const Function&`
95
+ - skipped, in which case the API creates a new no-op `Function`
96
+
97
+ When targetting version 5+, `CallbackType` is:
98
+ - `const Function&`
99
+ - `std::nullptr_t`
100
+ - skipped, in which case the API passes `std::nullptr`
101
+
118
102
### Acquire
119
103
120
104
Add a thread to this thread-safe function object, indicating that a new thread
@@ -206,113 +190,54 @@ Returns one of:
206
190
207
191
## Example
208
192
209
- ``` cpp
210
- #include < chrono>
211
- #include < thread>
212
- #include < napi.h>
213
-
214
- using namespace Napi ;
215
-
216
- std::thread nativeThread;
217
-
218
- struct ContextType {
219
- int threadId;
220
- };
221
-
222
- using DataType = int;
223
-
224
- using ThreadSafeFunctionEx = tsfn;
225
-
226
- Value Start( const CallbackInfo& info )
227
- {
228
- Napi::Env env = info.Env();
229
-
230
- if ( info.Length() < 2 )
231
- {
232
- throw TypeError::New( env, "Expected two arguments" );
233
- }
234
- else if ( !info[ 0] .IsFunction() )
235
- {
236
- throw TypeError::New( env, "Expected first arg to be function" );
237
- }
238
- else if ( !info[ 1] .IsNumber() )
239
- {
240
- throw TypeError::New( env, "Expected second arg to be number" );
241
- }
242
-
243
- int count = info[ 1] .As<Number >().Int32Value();
244
-
245
- // Create a ThreadSafeFunction
246
- tsfn = ThreadSafeFunction::New(
247
- env,
248
- info[ 0] .As<Function >(), // JavaScript function called asynchronously
249
- "Resource Name", // Name
250
- 0, // Unlimited queue
251
- 1, // Only one thread will use this initially
252
- [ ] ( Napi::Env ) { // Finalizer used to clean threads up
253
- nativeThread.join();
254
- } );
255
-
256
- // Create a native thread
257
- nativeThread = std::thread( [ count] {
258
- auto callback = [ ] ( Napi::Env env, Function jsCallback, int* value ) {
259
- // Transform native data into JS data, passing it to the provided
260
- // ` jsCallback ` -- the TSFN's JavaScript function.
261
- jsCallback.Call( {Number::New( env, * value )} );
262
-
263
- // We're finished with the data.
264
- delete value;
265
- };
266
-
267
- for ( int i = 0; i < count; i++ )
268
- {
269
- // Create new data
270
- int* value = new int( clock() );
271
-
272
- // Perform a blocking call
273
- napi_status status = tsfn.BlockingCall( value, callback );
274
- if ( status != napi_ok )
275
- {
276
- // Handle error
277
- break;
278
- }
279
-
280
- std::this_thread::sleep_for ( std::chrono::seconds( 1 ) );
281
- }
282
-
283
- // Release the thread-safe function
284
- tsfn.Release();
285
- } );
286
-
287
- return Boolean::New(env, true);
288
- }
289
-
290
- Napi::Object Init( Napi::Env env, Object exports )
291
- {
292
- exports.Set( "start", Function::New( env, Start ) );
293
- return exports;
294
- }
295
-
296
- NODE_API_MODULE( clock, Init )
297
- ```
298
-
299
- The above code can be used from JavaScript as follows:
193
+ For an in-line documented example, please see the ThreadSafeFunctionEx CI tests hosted here.
194
+ - [ test/threadsafe_function_ex/test/example.js] ( ../test/threadsafe_function_ex/test/example.js )
195
+ - [ test/threadsafe_function_ex/test/example.cc] ( ../test/threadsafe_function_ex/test/example.cc )
300
196
301
- ```js
302
- const { start } = require('bindings')('clock');
303
-
304
- start(function () {
305
- console.log("JavaScript callback called with arguments", Array.from(arguments));
306
- }, 5);
307
- ```
197
+ The example will create multiple set of threads. Each thread calls into
198
+ JavaScript with a numeric ` base ` value (deterministically calculated by the
199
+ thread id), with Node returning either a ` number ` or ` Promise<number> ` that
200
+ resolves to ` base * base ` .
308
201
309
- When executed, the output will show the value of ` clock() ` five times at one
310
- second intervals:
202
+ From the root of the ` node-addon-api ` repository:
311
203
312
204
```
313
- JavaScript callback called with arguments [ 84745 ]
314
- JavaScript callback called with arguments [ 103211 ]
315
- JavaScript callback called with arguments [ 104516 ]
316
- JavaScript callback called with arguments [ 105104 ]
317
- JavaScript callback called with arguments [ 105691 ]
205
+ Usage: node ./test/threadsafe_function_ex/test/example.js [options]
206
+
207
+ -c, --calls <calls> The number of calls each thread should make (number[]).
208
+ -a, --acquire [factor] Acquire a new set of `factor` call threads, using the
209
+ same `calls` definition.
210
+ -d, --call-delay <call-delays> The delay on callback resolution that each thread should
211
+ have (number[]). This is achieved via a delayed Promise
212
+ resolution in the JavaScript callback provided to the
213
+ TSFN. Using large delays here will cause all threads to
214
+ bottle-neck.
215
+ -D, --thread-delay <thread-delays> The delay that each thread should have prior to making a
216
+ call (number[]). Using large delays here will cause the
217
+ individual thread to bottle-neck.
218
+ -l, --log-call Display console.log-based logging messages.
219
+ -L, --log-thread Display std::cout-based logging messages.
220
+ -n, --no-callback Do not use a JavaScript callback.
221
+ -e, --callback-error [thread[.call]] Cause an error to occur in the JavaScript callback for
222
+ the given thread's call (if provided; first thread's
223
+ first call otherwise).
224
+
225
+ When not provided:
226
+ - <calls> defaults to [1,2,3,4,5]
227
+ - [factor] defaults to 1
228
+ - <call-delays> defaults to [400,200,100,50,0]
229
+ - <thread-delays> defaults to [400,200,100,50,0]
230
+
231
+
232
+ Examples:
233
+
234
+ -c [1,2,3] -l -L
235
+
236
+ Creates three threads that makes one, two, and three calls each, respectively.
237
+
238
+ -c [5,5] -d [5000,5000] -D [0,0] -l -L
239
+
240
+ Creates two threads that make five calls each. In this scenario, the threads will be
241
+ blocked primarily on waiting for the callback to resolve, as each thread's call takes
242
+ 5000 milliseconds.
318
243
```
0 commit comments