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