| # Thread-safe Functions | |
| JavaScript functions can normally only be called from a native addon's main | |
| thread. If an addon creates additional threads, then node-addon-api functions | |
| that require a `Napi::Env`, `Napi::Value`, or `Napi::Reference` must not be | |
| called from those threads. | |
| When an addon has additional threads and JavaScript functions need to be invoked | |
| based on the processing completed by those threads, those threads must | |
| communicate with the addon's main thread so that the main thread can invoke the | |
| JavaScript function on their behalf. The thread-safe function APIs provide an | |
| easy way to do this. These APIs provide two types -- | |
| [`Napi::ThreadSafeFunction`](threadsafe_function.md) and | |
| [`Napi::TypedThreadSafeFunction`](typed_threadsafe_function.md) -- as well as | |
| APIs to create, destroy, and call objects of this type. The differences between | |
| the two are subtle and are [highlighted below](#implementation-differences). | |
| Regardless of which type you choose, the APIs between the two are similar. | |
| `Napi::[Typed]ThreadSafeFunction::New()` creates a persistent reference that | |
| holds a JavaScript function which can be called from multiple threads. The calls | |
| happen asynchronously. This means that values with which the JavaScript callback | |
| is to be called will be placed in a queue, and, for each value in the queue, a | |
| call will eventually be made to the JavaScript function. | |
| `Napi::[Typed]ThreadSafeFunction` objects are destroyed when every thread which | |
| uses the object has called `Release()` or has received a return status of | |
| `napi_closing` in response to a call to `BlockingCall()` or `NonBlockingCall()`. | |
| The queue is emptied before the `Napi::[Typed]ThreadSafeFunction` is destroyed. | |
| It is important that `Release()` be the last API call made in conjunction with a | |
| given `Napi::[Typed]ThreadSafeFunction`, because after the call completes, there | |
| is no guarantee that the `Napi::[Typed]ThreadSafeFunction` is still allocated. | |
| For the same reason it is also important that no more use be made of a | |
| thread-safe function after receiving a return value of `napi_closing` in | |
| response to a call to `BlockingCall()` or `NonBlockingCall()`. Data associated | |
| with the `Napi::[Typed]ThreadSafeFunction` can be freed in its `Finalizer` | |
| callback which was passed to `[Typed]ThreadSafeFunction::New()`. | |
| Once the number of threads making use of a `Napi::[Typed]ThreadSafeFunction` | |
| reaches zero, no further threads can start making use of it by calling | |
| `Acquire()`. In fact, all subsequent API calls associated with it, except | |
| `Release()`, will return an error value of `napi_closing`. | |
| ## Implementation Differences | |
| The choice between `Napi::ThreadSafeFunction` and | |
| `Napi::TypedThreadSafeFunction` depends largely on how you plan to execute your | |
| native C++ code (the "callback") on the Node.js thread. | |
| ### [`Napi::ThreadSafeFunction`](threadsafe_function.md) | |
| This API is designed without Node-API 5 native support for [the optional JavaScript | |
| function callback feature](https://github.com/nodejs/node/commit/53297e66cb). | |
| This API has some dynamic functionality, in that: | |
| - The `[Non]BlockingCall()` methods provide a `Napi::Function` parameter as the | |
| callback to run when processing the data item on the main thread -- the | |
| `CallJs` callback. Since the callback is a parameter, it can be changed for | |
| every call. | |
| - Different C++ data types may be passed with each call of `[Non]BlockingCall()` | |
| to match the specific data type as specified in the `CallJs` callback. | |
| Note that this functionality comes with some **additional overhead** and | |
| situational **memory leaks**: | |
| - The API acts as a "broker" between the underlying `napi_threadsafe_function`, | |
| and dynamically constructs a wrapper for your callback on the heap for every | |
| call to `[Non]BlockingCall()`. | |
| - In acting in this "broker" fashion, the API will call the underlying "make | |
| call" Node-API method on this packaged item. If the API has determined the | |
| thread-safe function is no longer accessible (eg. all threads have released | |
| yet there are still items on the queue), **the callback passed to | |
| [Non]BlockingCall will not execute**. This means it is impossible to perform | |
| clean-up for calls that never execute their `CallJs` callback. **This may lead | |
| to memory leaks** if you are dynamically allocating memory. | |
| - The `CallJs` does not receive the thread-safe function's context as a | |
| parameter. In order for the callback to access the context, it must have a | |
| reference to either (1) the context directly, or (2) the thread-safe function | |
| to call `GetContext()`. Furthermore, the `GetContext()` method is not | |
| _type-safe_, as the method returns an object that can be "any-casted", instead | |
| of having a static type. | |
| ### [`Napi::TypedThreadSafeFunction`](typed_threadsafe_function.md) | |
| The `TypedThreadSafeFunction` class is a new implementation to address the | |
| drawbacks listed above. The API is designed with Node-API 5's support of an | |
| optional function callback. The API will correctly allow developers to pass | |
| `std::nullptr` instead of a `const Function&` for the callback function | |
| specified in `::New`. It also provides helper APIs to _target_ Node-API 4 and | |
| construct a no-op `Function` **or** to target Node-API 5 and "construct" a | |
| `std::nullptr` callback. This allows a single codebase to use the same APIs, | |
| with just a switch of the `NAPI_VERSION` compile-time constant. | |
| The removal of the dynamic call functionality has the following implications: | |
| - The API does _not_ act as a "broker" compared to the | |
| `Napi::ThreadSafeFunction`. Once Node.js finalizes the thread-safe function, | |
| the `CallJs` callback will execute with an empty `Napi::Env` for any remaining | |
| items on the queue. This provides the ability to handle any necessary cleanup | |
| of the item's data. | |
| - The callback _does_ receive the context as a parameter, so a call to | |
| `GetContext()` is _not_ necessary. This context type is specified as the | |
| **first template argument** specified to `::New`, ensuring type safety. | |
| - The `New()` constructor accepts the `CallJs` callback as the **second type | |
| argument**. The callback must be statically defined for the API to access it. | |
| This affords the ability to statically pass the context as the correct type | |
| across all methods. | |
| - Only one C++ data type may be specified to every call to `[Non]BlockingCall()` | |
| -- the **third template argument** specified to `::New`. Any "dynamic call | |
| data" must be implemented by the user. | |
| ### Usage Suggestions | |
| In summary, it may be best to use `Napi::TypedThreadSafeFunction` if: | |
| - static, compile-time support for targeting Node-API 4 or 5+ with an optional | |
| JavaScript callback feature is desired; | |
| - the callback can have `static` storage class and will not change across calls | |
| to `[Non]BlockingCall()`; | |
| - cleanup of items' data is required (eg. deleting dynamically-allocated data | |
| that is created at the caller level). | |
| Otherwise, `Napi::ThreadSafeFunction` may be a better choice. |