Skip to content

Commit 53297e6

Browse files
legendecastargos
authored andcommitted
n-api: make func argument of napi_create_threadsafe_function optional
PR-URL: #27791 Refs: #27592 Reviewed-By: Gabriel Schulhof <[email protected]>
1 parent 4d12cef commit 53297e6

File tree

4 files changed

+68
-11
lines changed

4 files changed

+68
-11
lines changed

doc/api/n-api.md

+8-2
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,8 @@ typedef void (*napi_threadsafe_function_call_js)(napi_env env,
478478
- `[in] env`: The environment to use for API calls, or `NULL` if the thread-safe
479479
function is being torn down and `data` may need to be freed.
480480
- `[in] js_callback`: The JavaScript function to call, or `NULL` if the
481-
thread-safe function is being torn down and `data` may need to be freed.
481+
thread-safe function is being torn down and `data` may need to be freed. It may
482+
also be `NULL` if the thread-safe function was created without `js_callback`.
482483
- `[in] context`: The optional data with which the thread-safe function was
483484
created.
484485
- `[in] data`: Data created by the secondary thread. It is the responsibility of
@@ -4657,6 +4658,10 @@ prevent the event loop from exiting. The APIs `napi_ref_threadsafe_function` and
46574658
<!-- YAML
46584659
added: v10.6.0
46594660
napiVersion: 4
4661+
changes:
4662+
- version: v10.17.0
4663+
pr-url: https://github.com/nodejs/node/pull/27791
4664+
description: Made `func` parameter optional with custom `call_js_cb`.
46604665
-->
46614666
```C
46624667
NAPI_EXTERN napi_status
@@ -4674,7 +4679,8 @@ napi_create_threadsafe_function(napi_env env,
46744679
```
46754680

46764681
- `[in] env`: The environment that the API is invoked under.
4677-
- `[in] func`: The JavaScript function to call from another thread.
4682+
- `[in] func`: An optional JavaScript function to call from another thread.
4683+
It must be provided if `NULL` is passed to `call_js_cb`.
46784684
- `[in] async_resource`: An optional object associated with the async work that
46794685
will be passed to possible `async_hooks` [`init` hooks][].
46804686
- `[in] async_resource_name`: A JavaScript string to provide an identifier for

src/node_api.cc

+11-4
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,14 @@ class ThreadSafeFunction : public node::AsyncResource {
319319
"ERR_NAPI_TSFN_STOP_IDLE_LOOP",
320320
"Failed to stop the idle loop") == napi_ok);
321321
} else {
322-
v8::Local<v8::Function> js_cb =
322+
napi_value js_callback = nullptr;
323+
if (!ref.IsEmpty()) {
324+
v8::Local<v8::Function> js_cb =
323325
v8::Local<v8::Function>::New(env->isolate, ref);
326+
js_callback = v8impl::JsValueFromV8LocalValue(js_cb);
327+
}
324328
call_js_cb(env,
325-
v8impl::JsValueFromV8LocalValue(js_cb),
329+
js_callback,
326330
context,
327331
data);
328332
}
@@ -1014,15 +1018,18 @@ napi_create_threadsafe_function(napi_env env,
10141018
napi_threadsafe_function_call_js call_js_cb,
10151019
napi_threadsafe_function* result) {
10161020
CHECK_ENV(env);
1017-
CHECK_ARG(env, func);
10181021
CHECK_ARG(env, async_resource_name);
10191022
RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg);
10201023
CHECK_ARG(env, result);
10211024

10221025
napi_status status = napi_ok;
10231026

10241027
v8::Local<v8::Function> v8_func;
1025-
CHECK_TO_FUNCTION(env, v8_func, func);
1028+
if (func == nullptr) {
1029+
CHECK_ARG(env, call_js_cb);
1030+
} else {
1031+
CHECK_TO_FUNCTION(env, v8_func, func);
1032+
}
10261033

10271034
v8::Local<v8::Context> v8_context = env->context();
10281035

test/node-api/test_threadsafe_function/binding.c

+38-5
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,19 @@ static void call_js(napi_env env, napi_value cb, void* hint, void* data) {
129129
}
130130
}
131131

132+
static napi_ref alt_ref;
133+
// Getting the data into JS with the alternative referece
134+
static void call_ref(napi_env env, napi_value _, void* hint, void* data) {
135+
if (!(env == NULL || alt_ref == NULL)) {
136+
napi_value fn, argv, undefined;
137+
NAPI_CALL_RETURN_VOID(env, napi_get_reference_value(env, alt_ref, &fn));
138+
NAPI_CALL_RETURN_VOID(env, napi_create_int32(env, *(int*)data, &argv));
139+
NAPI_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
140+
NAPI_CALL_RETURN_VOID(env, napi_call_function(env, undefined, fn, 1, &argv,
141+
NULL));
142+
}
143+
}
144+
132145
// Cleanup
133146
static napi_value StopThread(napi_env env, napi_callback_info info) {
134147
size_t argc = 2;
@@ -168,20 +181,31 @@ static void join_the_threads(napi_env env, void *data, void *hint) {
168181
napi_call_function(env, undefined, js_cb, 0, NULL, NULL));
169182
NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env,
170183
the_hint->js_finalize_cb));
184+
if (alt_ref != NULL) {
185+
NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, alt_ref));
186+
alt_ref = NULL;
187+
}
171188
}
172189

173190
static napi_value StartThreadInternal(napi_env env,
174191
napi_callback_info info,
175192
napi_threadsafe_function_call_js cb,
176-
bool block_on_full) {
193+
bool block_on_full,
194+
bool alt_ref_js_cb) {
195+
177196
size_t argc = 4;
178197
napi_value argv[4];
179198

199+
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
200+
if (alt_ref_js_cb) {
201+
NAPI_CALL(env, napi_create_reference(env, argv[0], 1, &alt_ref));
202+
argv[0] = NULL;
203+
}
204+
180205
ts_info.block_on_full =
181206
(block_on_full ? napi_tsfn_blocking : napi_tsfn_nonblocking);
182207

183208
NAPI_ASSERT(env, (ts_fn == NULL), "Existing thread-safe function");
184-
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
185209
napi_value async_name;
186210
NAPI_CALL(env, napi_create_string_utf8(env, "N-API Thread-safe Function Test",
187211
NAPI_AUTO_LENGTH, &async_name));
@@ -223,16 +247,24 @@ static napi_value Release(napi_env env, napi_callback_info info) {
223247

224248
// Startup
225249
static napi_value StartThread(napi_env env, napi_callback_info info) {
226-
return StartThreadInternal(env, info, call_js, true);
250+
return StartThreadInternal(env, info, call_js,
251+
/** block_on_full */true, /** alt_ref_js_cb */false);
227252
}
228253

229254
static napi_value StartThreadNonblocking(napi_env env,
230255
napi_callback_info info) {
231-
return StartThreadInternal(env, info, call_js, false);
256+
return StartThreadInternal(env, info, call_js,
257+
/** block_on_full */false, /** alt_ref_js_cb */false);
232258
}
233259

234260
static napi_value StartThreadNoNative(napi_env env, napi_callback_info info) {
235-
return StartThreadInternal(env, info, NULL, true);
261+
return StartThreadInternal(env, info, NULL,
262+
/** block_on_full */true, /** alt_ref_js_cb */false);
263+
}
264+
265+
static napi_value StartThreadNoJsFunc(napi_env env, napi_callback_info info) {
266+
return StartThreadInternal(env, info, call_ref,
267+
/** block_on_full */true, /** alt_ref_js_cb */true);
236268
}
237269

238270
// Module init
@@ -269,6 +301,7 @@ static napi_value Init(napi_env env, napi_value exports) {
269301
DECLARE_NAPI_PROPERTY("StartThread", StartThread),
270302
DECLARE_NAPI_PROPERTY("StartThreadNoNative", StartThreadNoNative),
271303
DECLARE_NAPI_PROPERTY("StartThreadNonblocking", StartThreadNonblocking),
304+
DECLARE_NAPI_PROPERTY("StartThreadNoJsFunc", StartThreadNoJsFunc),
272305
DECLARE_NAPI_PROPERTY("StopThread", StopThread),
273306
DECLARE_NAPI_PROPERTY("Unref", Unref),
274307
DECLARE_NAPI_PROPERTY("Release", Release),

test/node-api/test_threadsafe_function/test.js

+11
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,17 @@ new Promise(function testWithoutJSMarshaller(resolve) {
102102
}))
103103
.then((result) => assert.deepStrictEqual(result, expectedArray))
104104

105+
// Start the thread in blocking mode, and assert that all values are passed.
106+
// Quit after it's done.
107+
// Doesn't pass the callback js function to napi_create_threadsafe_function.
108+
// Instead, use an alternative reference to get js function called.
109+
.then(() => testWithJSMarshaller({
110+
threadStarter: 'StartThreadNoJsFunc',
111+
maxQueueSize: binding.MAX_QUEUE_SIZE,
112+
quitAfter: binding.ARRAY_LENGTH
113+
}))
114+
.then((result) => assert.deepStrictEqual(result, expectedArray))
115+
105116
// Start the thread in blocking mode with an infinite queue, and assert that all
106117
// values are passed. Quit after it's done.
107118
.then(() => testWithJSMarshaller({

0 commit comments

Comments
 (0)