1- # TODO
2- - Document new N-API 5+ only methods
3- - Continue with examples
4-
51# ThreadSafeFunctionEx
62
73The ` Napi::ThreadSafeFunctionEx ` type provides APIs for threads to communicate
84with the addon's main thread to invoke JavaScript functions on their behalf. The
95type is a three-argument templated class, each argument representing the type
106of:
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,
128 a TSFN has no context.
139- ` DataType = void* ` : The data to use in the native callback. By default, a TSFN
1410 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.
1715
1816Documentation can be found for an [ overview of the API] ( threadsafe.md ) , as well
1917as [ differences between the two thread-safe function
@@ -40,46 +38,20 @@ Napi::ThreadSafeFunctionEx<ContextType, DataType, Callback>::ThreadSafeFunctionE
4038- ` tsfn ` : The ` napi_threadsafe_function ` which is a handle for an existing
4139 thread-safe function.
4240
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 ` .
7445
7546### New
7647
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.
7951
8052``` cpp
8153New (napi_env env,
82- const Function& callback,
54+ CallbackType callback,
8355 const Object& resource,
8456 ResourceString resourceName,
8557 size_t maxQueueSize,
@@ -91,9 +63,9 @@ New(napi_env env,
9163
9264- `env`: The `napi_env` environment in which to construct the
9365 `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.
9769- `resourceName`: A JavaScript string to provide an identifier for the kind of
9870 resource that is being provided for diagnostic information exposed by the
9971 async_hooks API.
@@ -102,19 +74,31 @@ New(napi_env env,
10274 thread, which will be making use of this function.
10375- `[optional] context`: Data to attach to the resulting `ThreadSafeFunction`.
10476 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)`.
11486- `[optional] data`: Data to be passed to `finalizeCallback`.
11587
11688Returns a non-empty `Napi::ThreadSafeFunctionEx` instance.
11789
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+
118102### Acquire
119103
120104Add a thread to this thread-safe function object, indicating that a new thread
@@ -206,113 +190,54 @@ Returns one of:
206190
207191## Example
208192
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 )
300196
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 ` .
308201
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:
311203
312204```
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.
318243```
0 commit comments