1# ThreadSafeFunction
2
3The `Napi::ThreadSafeFunction` type provides APIs for threads to communicate
4with the addon's main thread to invoke JavaScript functions on their behalf.
5Documentation can be found for an [overview of the API](threadsafe.md), as well
6as [differences between the two thread-safe function
7APIs](threadsafe.md#implementation-differences).
8
9## Methods
10
11### Constructor
12
13Creates a new empty instance of `Napi::ThreadSafeFunction`.
14
15```cpp
16Napi::Function::ThreadSafeFunction();
17```
18
19### Constructor
20
21Creates a new instance of the `Napi::ThreadSafeFunction` object.
22
23```cpp
24Napi::ThreadSafeFunction::ThreadSafeFunction(napi_threadsafe_function tsfn);
25```
26
27- `tsfn`: The `napi_threadsafe_function` which is a handle for an existing
28  thread-safe function.
29
30Returns a non-empty `Napi::ThreadSafeFunction` instance. When using this
31constructor, only use the `Blocking(void*)` / `NonBlocking(void*)` overloads;
32the `Callback` and templated `data*` overloads should _not_ be used. See below
33for additional details.
34
35### New
36
37Creates a new instance of the `Napi::ThreadSafeFunction` object. The `New`
38function has several overloads for the various optional parameters: skip the
39optional parameter for that specific overload.
40
41```cpp
42New(napi_env env,
43    const Function& callback,
44    const Object& resource,
45    ResourceString resourceName,
46    size_t maxQueueSize,
47    size_t initialThreadCount,
48    ContextType* context,
49    Finalizer finalizeCallback,
50    FinalizerDataType* data);
51```
52
53- `env`: The `napi_env` environment in which to construct the
54  `Napi::ThreadSafeFunction` object.
55- `callback`: The `Function` to call from another thread.
56- `[optional] resource`: An object associated with the async work that will be
57  passed to possible async_hooks init hooks.
58- `resourceName`: A JavaScript string to provide an identifier for the kind of
59  resource that is being provided for diagnostic information exposed by the
60  async_hooks API.
61- `maxQueueSize`: Maximum size of the queue. `0` for no limit.
62- `initialThreadCount`: The initial number of threads, including the main
63  thread, which will be making use of this function.
64- `[optional] context`: Data to attach to the resulting `ThreadSafeFunction`. It
65  can be retreived by calling `GetContext()`.
66- `[optional] finalizeCallback`: Function to call when the `ThreadSafeFunction`
67  is being destroyed.  This callback will be invoked on the main thread when the
68  thread-safe function is about to be destroyed. It receives the context and the
69  finalize data given during construction (if given), and provides an
70  opportunity for cleaning up after the threads e.g. by calling
71  `uv_thread_join()`. It is important that, aside from the main loop thread,
72  there be no threads left using the thread-safe function after the finalize
73  callback completes. Must implement `void operator()(Env env, DataType* data,
74  ContextType* hint)`, skipping `data` or `hint` if they are not provided. Can
75  be retrieved via `GetContext()`.
76- `[optional] data`: Data to be passed to `finalizeCallback`.
77
78Returns a non-empty `Napi::ThreadSafeFunction` instance.
79
80### Acquire
81
82Add a thread to this thread-safe function object, indicating that a new thread
83will start making use of the thread-safe function.
84
85```cpp
86napi_status Napi::ThreadSafeFunction::Acquire() const
87```
88
89Returns one of:
90- `napi_ok`: The thread has successfully acquired the thread-safe function
91for its use.
92- `napi_closing`: The thread-safe function has been marked as closing via a
93previous call to `Abort()`.
94
95### Release
96
97Indicate that an existing thread will stop making use of the thread-safe
98function. A thread should call this API when it stops making use of this
99thread-safe function. Using any thread-safe APIs after having called this API
100has undefined results in the current thread, as it may have been destroyed.
101
102```cpp
103napi_status Napi::ThreadSafeFunction::Release() const
104```
105
106Returns one of:
107- `napi_ok`: The thread-safe function has been successfully released.
108- `napi_invalid_arg`: The thread-safe function's thread-count is zero.
109- `napi_generic_failure`: A generic error occurred when attempting to release
110the thread-safe function.
111
112### Abort
113
114"Abort" the thread-safe function. This will cause all subsequent APIs associated
115with the thread-safe function except `Release()` to return `napi_closing` even
116before its reference count reaches zero. In particular, `BlockingCall` and
117`NonBlockingCall()` will return `napi_closing`, thus informing the threads that
118it is no longer possible to make asynchronous calls to the thread-safe function.
119This can be used as a criterion for terminating the thread. Upon receiving a
120return value of `napi_closing` from a thread-safe function call a thread must
121make no further use of the thread-safe function because it is no longer
122guaranteed to be allocated.
123
124```cpp
125napi_status Napi::ThreadSafeFunction::Abort() const
126```
127
128Returns one of:
129- `napi_ok`: The thread-safe function has been successfully aborted.
130- `napi_invalid_arg`: The thread-safe function's thread-count is zero.
131- `napi_generic_failure`: A generic error occurred when attempting to abort
132the thread-safe function.
133
134### BlockingCall / NonBlockingCall
135
136Calls the Javascript function in either a blocking or non-blocking fashion.
137- `BlockingCall()`: the API blocks until space becomes available in the queue.
138  Will never block if the thread-safe function was created with a maximum queue
139  size of `0`.
140- `NonBlockingCall()`: will return `napi_queue_full` if the queue was full,
141  preventing data from being successfully added to the queue.
142
143There are several overloaded implementations of `BlockingCall()` and
144`NonBlockingCall()` for use with optional parameters: skip the optional
145parameter for that specific overload.
146
147**These specific function overloads should only be used on a `ThreadSafeFunction`
148created via `ThreadSafeFunction::New`.**
149
150```cpp
151napi_status Napi::ThreadSafeFunction::BlockingCall(DataType* data, Callback callback) const
152
153napi_status Napi::ThreadSafeFunction::NonBlockingCall(DataType* data, Callback callback) const
154```
155
156- `[optional] data`: Data to pass to `callback`.
157- `[optional] callback`: C++ function that is invoked on the main thread. The
158  callback receives the `ThreadSafeFunction`'s JavaScript callback function to
159  call as an `Napi::Function` in its parameters and the `DataType*` data pointer
160  (if provided). Must implement `void operator()(Napi::Env env, Function
161  jsCallback, DataType* data)`, skipping `data` if not provided. It is not
162  necessary to call into JavaScript via `MakeCallback()` because Node-API runs
163  `callback` in a context appropriate for callbacks.
164
165**These specific function overloads should only be used on a `ThreadSafeFunction`
166created via `ThreadSafeFunction(napi_threadsafe_function)`.**
167
168```cpp
169napi_status Napi::ThreadSafeFunction::BlockingCall(void* data) const
170
171napi_status Napi::ThreadSafeFunction::NonBlockingCall(void* data) const
172```
173- `data`: Data to pass to `call_js_cb` specified when creating the thread-safe
174  function via `napi_create_threadsafe_function`.
175
176Returns one of:
177- `napi_ok`: The call was successfully added to the queue.
178- `napi_queue_full`: The queue was full when trying to call in a non-blocking
179  method.
180- `napi_closing`: The thread-safe function is aborted and cannot accept more
181  calls.
182- `napi_invalid_arg`: The thread-safe function is closed.
183- `napi_generic_failure`: A generic error occurred when attempting to add to the
184  queue.
185
186## Example
187
188```cpp
189#include <chrono>
190#include <thread>
191#include <napi.h>
192
193using namespace Napi;
194
195std::thread nativeThread;
196ThreadSafeFunction tsfn;
197
198Value Start( const CallbackInfo& info )
199{
200  Napi::Env env = info.Env();
201
202  if ( info.Length() < 2 )
203  {
204    throw TypeError::New( env, "Expected two arguments" );
205  }
206  else if ( !info[0].IsFunction() )
207  {
208    throw TypeError::New( env, "Expected first arg to be function" );
209  }
210  else if ( !info[1].IsNumber() )
211  {
212    throw TypeError::New( env, "Expected second arg to be number" );
213  }
214
215  int count = info[1].As<Number>().Int32Value();
216
217  // Create a ThreadSafeFunction
218  tsfn = ThreadSafeFunction::New(
219      env,
220      info[0].As<Function>(),  // JavaScript function called asynchronously
221      "Resource Name",         // Name
222      0,                       // Unlimited queue
223      1,                       // Only one thread will use this initially
224      []( Napi::Env ) {        // Finalizer used to clean threads up
225        nativeThread.join();
226      } );
227
228  // Create a native thread
229  nativeThread = std::thread( [count] {
230    auto callback = []( Napi::Env env, Function jsCallback, int* value ) {
231      // Transform native data into JS data, passing it to the provided
232      // `jsCallback` -- the TSFN's JavaScript function.
233      jsCallback.Call( {Number::New( env, *value )} );
234
235      // We're finished with the data.
236      delete value;
237    };
238
239    for ( int i = 0; i < count; i++ )
240    {
241      // Create new data
242      int* value = new int( clock() );
243
244      // Perform a blocking call
245      napi_status status = tsfn.BlockingCall( value, callback );
246      if ( status != napi_ok )
247      {
248        // Handle error
249        break;
250      }
251
252      std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
253    }
254
255    // Release the thread-safe function
256    tsfn.Release();
257  } );
258
259  return Boolean::New(env, true);
260}
261
262Napi::Object Init( Napi::Env env, Object exports )
263{
264  exports.Set( "start", Function::New( env, Start ) );
265  return exports;
266}
267
268NODE_API_MODULE( clock, Init )
269```
270
271The above code can be used from JavaScript as follows:
272
273```js
274const { start } = require('bindings')('clock');
275
276start(function () {
277    console.log("JavaScript callback called with arguments", Array.from(arguments));
278}, 5);
279```
280
281When executed, the output will show the value of `clock()` five times at one
282second intervals:
283
284```
285JavaScript callback called with arguments [ 84745 ]
286JavaScript callback called with arguments [ 103211 ]
287JavaScript callback called with arguments [ 104516 ]
288JavaScript callback called with arguments [ 105104 ]
289JavaScript callback called with arguments [ 105691 ]
290```
291