1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
|
# TypedThreadSafeFunction
The `Napi::TypedThreadSafeFunction` type provides APIs for threads to
communicate with the addon's main thread to invoke JavaScript functions on their
behalf. The type is a three-argument templated class, each argument representing
the type of:
- `ContextType = std::nullptr_t`: The thread-safe function's context. By
default, a TSFN has no context.
- `DataType = void*`: The data to use in the native callback. By default, a TSFN
can accept any data type.
- `Callback = void(*)(Napi::Env, Napi::Function jsCallback, ContextType*,
DataType*)`: The callback to run for each item added to the queue. If no
`Callback` is given, the API will call the function `jsCallback` with no
arguments.
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::TypedThreadSafeFunction`.
```cpp
Napi::Function::TypedThreadSafeFunction<ContextType, DataType, Callback>::TypedThreadSafeFunction();
```
### Constructor
Creates a new instance of the `Napi::TypedThreadSafeFunction` object.
```cpp
Napi::TypedThreadSafeFunction<ContextType, DataType, Callback>::TypedThreadSafeFunction(napi_threadsafe_function tsfn);
```
- `tsfn`: The `napi_threadsafe_function` which is a handle for an existing
thread-safe function.
Returns a non-empty `Napi::TypedThreadSafeFunction` instance. To ensure the API
statically handles the correct return type for `GetContext()` and
`[Non]BlockingCall()`, pass the proper template arguments to
`Napi::TypedThreadSafeFunction`.
### New
Creates a new instance of the `Napi::TypedThreadSafeFunction` 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,
CallbackType callback,
const Object& resource,
ResourceString resourceName,
size_t maxQueueSize,
size_t initialThreadCount,
ContextType* context,
Finalizer finalizeCallback,
FinalizerDataType* data = nullptr);
```
- `env`: The `napi_env` environment in which to construct the
`Napi::ThreadSafeFunction` object.
- `[optional] 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 retrieved via `GetContext()`.
- `[optional] finalizeCallback`: Function to call when the
`TypedThreadSafeFunction` 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,
FinalizerDataType* data, ContextType* hint)`.
- `[optional] data`: Data to be passed to `finalizeCallback`.
Returns a non-empty `Napi::TypedThreadSafeFunction` instance.
Depending on the targeted `NAPI_VERSION`, the API has different implementations
for `CallbackType callback`.
When targeting version 4, `callback` may be:
- of type `const Function&`
- not provided as a parameter, in which case the API creates a new no-op
`Function`
When targeting version 5+, `callback` may be:
- of type `const Function&`
- of type `std::nullptr_t`
- not provided as a parameter, in which case the API passes `std::nullptr`
### Acquire
Adds 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::TypedThreadSafeFunction<ContextType, DataType, Callback>::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
Indicates 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 the thread-safe function may
have been destroyed.
```cpp
napi_status Napi::TypedThreadSafeFunction<ContextType, DataType, Callback>::Release() const
```
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
"Aborts" 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::TypedThreadSafeFunction<ContextType, DataType, Callback>::Abort() const
```
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.
```cpp
napi_status Napi::TypedThreadSafeFunction<ContextType, DataType, Callback>::BlockingCall(DataType* data = nullptr) const
napi_status Napi::TypedThreadSafeFunction<ContextType, DataType, Callback>::NonBlockingCall(DataType* data = nullptr) const
```
- `[optional] data`: Data to pass to the callback which was passed to
`TypedThreadSafeFunction::New()`.
Returns one of:
- `napi_ok`: `data` 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 no further calls can
be made.
- `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 <chrono>
#include <napi.h>
#include <thread>
using namespace Napi;
using Context = Reference<Value>;
using DataType = int;
void CallJs(Napi::Env env, Function callback, Context *context, DataType *data);
using TSFN = TypedThreadSafeFunction<Context, DataType, CallJs>;
using FinalizerDataType = void;
std::thread nativeThread;
TSFN 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<Number>().Int32Value();
// Create a new context set to the receiver (ie, `this`) of the function call
Context *context = new Reference<Value>(Persistent(info.This()));
// Create a ThreadSafeFunction
tsfn = TSFN::New(
env,
info[0].As<Function>(), // JavaScript function called asynchronously
"Resource Name", // Name
0, // Unlimited queue
1, // Only one thread will use this initially
context,
[](Napi::Env, FinalizerDataType *,
Context *ctx) { // Finalizer used to clean threads up
nativeThread.join();
delete ctx;
});
// Create a native thread
nativeThread = std::thread([count] {
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);
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);
}
// Transform native data into JS data, passing it to the provided
// `callback` -- the TSFN's JavaScript function.
void CallJs(Napi::Env env, Function callback, Context *context,
DataType *data) {
// Is the JavaScript environment still available to call into, eg. the TSFN is
// not aborted
if (env != nullptr) {
// On Node-API 5+, the `callback` parameter is optional; however, this example
// does ensure a callback is provided.
if (callback != nullptr) {
callback.Call(context->Value(), {Number::New(env, *data)});
}
}
if (data != nullptr) {
// We're finished with the data.
delete data;
}
}
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.call(new Date(), function (clock) {
const context = this;
console.log(context, clock);
}, 5);
```
When executed, the output will show the value of `clock()` five times at one
second intervals, prefixed with the TSFN's context -- `start`'s receiver (ie,
`new Date()`):
```
2020-08-18T21:04:25.116Z 49824
2020-08-18T21:04:25.116Z 62493
2020-08-18T21:04:25.116Z 62919
2020-08-18T21:04:25.116Z 63228
2020-08-18T21:04:25.116Z 63531
```
|