1# Thread-safe Functions 2 3JavaScript functions can normally only be called from a native addon's main 4thread. If an addon creates additional threads, then node-addon-api functions 5that require a `Napi::Env`, `Napi::Value`, or `Napi::Reference` must not be 6called from those threads. 7 8When an addon has additional threads and JavaScript functions need to be invoked 9based on the processing completed by those threads, those threads must 10communicate with the addon's main thread so that the main thread can invoke the 11JavaScript function on their behalf. The thread-safe function APIs provide an 12easy 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 15APIs to create, destroy, and call objects of this type. The differences between 16the two are subtle and are [highlighted below](#implementation-differences). 17Regardless of which type you choose, the APIs between the two are similar. 18 19`Napi::[Typed]ThreadSafeFunction::New()` creates a persistent reference that 20holds a JavaScript function which can be called from multiple threads. The calls 21happen asynchronously. This means that values with which the JavaScript callback 22is to be called will be placed in a queue, and, for each value in the queue, a 23call will eventually be made to the JavaScript function. 24 25`Napi::[Typed]ThreadSafeFunction` objects are destroyed when every thread which 26uses the object has called `Release()` or has received a return status of 27`napi_closing` in response to a call to `BlockingCall()` or `NonBlockingCall()`. 28The queue is emptied before the `Napi::[Typed]ThreadSafeFunction` is destroyed. 29It is important that `Release()` be the last API call made in conjunction with a 30given `Napi::[Typed]ThreadSafeFunction`, because after the call completes, there 31is no guarantee that the `Napi::[Typed]ThreadSafeFunction` is still allocated. 32For the same reason it is also important that no more use be made of a 33thread-safe function after receiving a return value of `napi_closing` in 34response to a call to `BlockingCall()` or `NonBlockingCall()`. Data associated 35with the `Napi::[Typed]ThreadSafeFunction` can be freed in its `Finalizer` 36callback which was passed to `[Typed]ThreadSafeFunction::New()`. 37 38Once the number of threads making use of a `Napi::[Typed]ThreadSafeFunction` 39reaches 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 45The choice between `Napi::ThreadSafeFunction` and 46`Napi::TypedThreadSafeFunction` depends largely on how you plan to execute your 47native C++ code (the "callback") on the Node.js thread. 48 49### [`Napi::ThreadSafeFunction`](threadsafe_function.md) 50 51This API is designed without Node-API 5 native support for [the optional JavaScript 52 function callback feature](/node/commit/53297e66cb). 53 54This 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 62Note that this functionality comes with some **additional overhead** and 63situational **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" Node-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 83The `TypedThreadSafeFunction` class is a new implementation to address the 84drawbacks listed above. The API is designed with Node-API 5's support of an 85optional function callback. The API will correctly allow developers to pass 86`std::nullptr` instead of a `const Function&` for the callback function 87specified in `::New`. It also provides helper APIs to _target_ Node-API 4 and 88construct a no-op `Function` **or** to target Node-API 5 and "construct" a 89`std::nullptr` callback. This allows a single codebase to use the same APIs, 90with just a switch of the `NAPI_VERSION` compile-time constant. 91 92The 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 112In summary, it may be best to use `Napi::TypedThreadSafeFunction` if: 113 114- static, compile-time support for targeting Node-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 121Otherwise, `Napi::ThreadSafeFunction` may be a better choice. 122