# ThreadSafeFunction The `Napi::ThreadSafeFunction` type provides APIs for threads to communicate with the addon's main thread to invoke JavaScript functions on their behalf. Documentation can be found for an [overview of the API](threadsafe.md), as well as [differences between the two thread-safe function APIs](threadsafe.md#implementation-differences). ## Methods ### Constructor Creates a new empty instance of `Napi::ThreadSafeFunction`. ```cpp Napi::Function::ThreadSafeFunction(); ``` ### Constructor Creates a new instance of the `Napi::ThreadSafeFunction` object. ```cpp Napi::ThreadSafeFunction::ThreadSafeFunction(napi_threadsafe_function tsfn); ``` - `tsfn`: The `napi_threadsafe_function` which is a handle for an existing thread-safe function. Returns a non-empty `Napi::ThreadSafeFunction` instance. When using this constructor, only use the `Blocking(void*)` / `NonBlocking(void*)` overloads; the `Callback` and templated `data*` overloads should _not_ be used. See below for additional details. ### New Creates a new instance of the `Napi::ThreadSafeFunction` object. The `New` function has several overloads for the various optional parameters: skip the optional parameter for that specific overload. ```cpp New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data); ``` - `env`: The `napi_env` environment in which to construct the `Napi::ThreadSafeFunction` object. - `callback`: The `Function` to call from another thread. - `[optional] resource`: An object associated with the async work that will be passed to possible async_hooks init hooks. - `resourceName`: A JavaScript string to provide an identifier for the kind of resource that is being provided for diagnostic information exposed by the async_hooks API. - `maxQueueSize`: Maximum size of the queue. `0` for no limit. - `initialThreadCount`: The initial number of threads, including the main thread, which will be making use of this function. - `[optional] context`: Data to attach to the resulting `ThreadSafeFunction`. It can be retreived by calling `GetContext()`. - `[optional] finalizeCallback`: Function to call when the `ThreadSafeFunction` is being destroyed. This callback will be invoked on the main thread when the thread-safe function is about to be destroyed. It receives the context and the finalize data given during construction (if given), and provides an opportunity for cleaning up after the threads e.g. by calling `uv_thread_join()`. It is important that, aside from the main loop thread, there be no threads left using the thread-safe function after the finalize callback completes. Must implement `void operator()(Env env, DataType* data, ContextType* hint)`, skipping `data` or `hint` if they are not provided. Can be retrieved via `GetContext()`. - `[optional] data`: Data to be passed to `finalizeCallback`. Returns a non-empty `Napi::ThreadSafeFunction` instance. ### Acquire Add a thread to this thread-safe function object, indicating that a new thread will start making use of the thread-safe function. ```cpp napi_status Napi::ThreadSafeFunction::Acquire() ``` Returns one of: - `napi_ok`: The thread has successfully acquired the thread-safe function for its use. - `napi_closing`: The thread-safe function has been marked as closing via a previous call to `Abort()`. ### Release Indicate that an existing thread will stop making use of the thread-safe function. A thread should call this API when it stops making use of this thread-safe function. Using any thread-safe APIs after having called this API has undefined results in the current thread, as it may have been destroyed. ```cpp napi_status Napi::ThreadSafeFunction::Release() ``` Returns one of: - `napi_ok`: The thread-safe function has been successfully released. - `napi_invalid_arg`: The thread-safe function's thread-count is zero. - `napi_generic_failure`: A generic error occurred when attempting to release the thread-safe function. ### Abort "Abort" the thread-safe function. This will cause all subsequent APIs associated with the thread-safe function except `Release()` to return `napi_closing` even before its reference count reaches zero. In particular, `BlockingCall` and `NonBlockingCall()` will return `napi_closing`, thus informing the threads that it is no longer possible to make asynchronous calls to the thread-safe function. This can be used as a criterion for terminating the thread. Upon receiving a return value of `napi_closing` from a thread-safe function call a thread must make no further use of the thread-safe function because it is no longer guaranteed to be allocated. ```cpp napi_status Napi::ThreadSafeFunction::Abort() ``` Returns one of: - `napi_ok`: The thread-safe function has been successfully aborted. - `napi_invalid_arg`: The thread-safe function's thread-count is zero. - `napi_generic_failure`: A generic error occurred when attempting to abort the thread-safe function. ### BlockingCall / NonBlockingCall Calls the Javascript function in either a blocking or non-blocking fashion. - `BlockingCall()`: the API blocks until space becomes available in the queue. Will never block if the thread-safe function was created with a maximum queue size of `0`. - `NonBlockingCall()`: will return `napi_queue_full` if the queue was full, preventing data from being successfully added to the queue. There are several overloaded implementations of `BlockingCall()` and `NonBlockingCall()` for use with optional parameters: skip the optional parameter for that specific overload. **These specific function overloads should only be used on a `ThreadSafeFunction` created via `ThreadSafeFunction::New`.** ```cpp napi_status Napi::ThreadSafeFunction::BlockingCall(DataType* data, Callback callback) const napi_status Napi::ThreadSafeFunction::NonBlockingCall(DataType* data, Callback callback) const ``` - `[optional] data`: Data to pass to `callback`. - `[optional] callback`: C++ function that is invoked on the main thread. The callback receives the `ThreadSafeFunction`'s JavaScript callback function to call as an `Napi::Function` in its parameters and the `DataType*` data pointer (if provided). Must implement `void operator()(Napi::Env env, Function jsCallback, DataType* data)`, skipping `data` if not provided. It is not necessary to call into JavaScript via `MakeCallback()` because N-API runs `callback` in a context appropriate for callbacks. **These specific function overloads should only be used on a `ThreadSafeFunction` created via `ThreadSafeFunction(napi_threadsafe_function)`.** ```cpp napi_status Napi::ThreadSafeFunction::BlockingCall(void* data) const napi_status Napi::ThreadSafeFunction::NonBlockingCall(void* data) const ``` - `data`: Data to pass to `call_js_cb` specified when creating the thread-safe function via `napi_create_threadsafe_function`. Returns one of: - `napi_ok`: The call was successfully added to the queue. - `napi_queue_full`: The queue was full when trying to call in a non-blocking method. - `napi_closing`: The thread-safe function is aborted and cannot accept more calls. - `napi_invalid_arg`: The thread-safe function is closed. - `napi_generic_failure`: A generic error occurred when attempting to add to the queue. ## Example ```cpp #include #include #include using namespace Napi; std::thread nativeThread; ThreadSafeFunction tsfn; Value Start( const CallbackInfo& info ) { Napi::Env env = info.Env(); if ( info.Length() < 2 ) { throw TypeError::New( env, "Expected two arguments" ); } else if ( !info[0].IsFunction() ) { throw TypeError::New( env, "Expected first arg to be function" ); } else if ( !info[1].IsNumber() ) { throw TypeError::New( env, "Expected second arg to be number" ); } int count = info[1].As().Int32Value(); // Create a ThreadSafeFunction tsfn = ThreadSafeFunction::New( env, info[0].As(), // JavaScript function called asynchronously "Resource Name", // Name 0, // Unlimited queue 1, // Only one thread will use this initially []( Napi::Env ) { // Finalizer used to clean threads up nativeThread.join(); } ); // Create a native thread nativeThread = std::thread( [count] { auto callback = []( Napi::Env env, Function jsCallback, int* value ) { // Transform native data into JS data, passing it to the provided // `jsCallback` -- the TSFN's JavaScript function. jsCallback.Call( {Number::New( env, *value )} ); // We're finished with the data. delete value; }; for ( int i = 0; i < count; i++ ) { // Create new data int* value = new int( clock() ); // Perform a blocking call napi_status status = tsfn.BlockingCall( value, callback ); if ( status != napi_ok ) { // Handle error break; } std::this_thread::sleep_for( std::chrono::seconds( 1 ) ); } // Release the thread-safe function tsfn.Release(); } ); return Boolean::New(env, true); } Napi::Object Init( Napi::Env env, Object exports ) { exports.Set( "start", Function::New( env, Start ) ); return exports; } NODE_API_MODULE( clock, Init ) ``` The above code can be used from JavaScript as follows: ```js const { start } = require('bindings')('clock'); start(function () { console.log("JavaScript callback called with arguments", Array.from(arguments)); }, 5); ``` When executed, the output will show the value of `clock()` five times at one second intervals: ``` JavaScript callback called with arguments [ 84745 ] JavaScript callback called with arguments [ 103211 ] JavaScript callback called with arguments [ 104516 ] JavaScript callback called with arguments [ 105104 ] JavaScript callback called with arguments [ 105691 ] ```