Skip to content

Commit d0a05d0

Browse files
legendecasGabriel Schulhof
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 7bef222 commit d0a05d0

File tree

4 files changed

+69
-11
lines changed

4 files changed

+69
-11
lines changed

doc/api/n-api.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,8 @@ typedef void (*napi_threadsafe_function_call_js)(napi_env env,
311311
- `[in] env`: The environment to use for API calls, or `NULL` if the thread-safe
312312
function is being torn down and `data` may need to be freed.
313313
- `[in] js_callback`: The JavaScript function to call, or `NULL` if the
314-
thread-safe function is being torn down and `data` may need to be freed.
314+
thread-safe function is being torn down and `data` may need to be freed. It may
315+
also be `NULL` if the thread-safe function was created without `js_callback`.
315316
- `[in] context`: The optional data with which the thread-safe function was
316317
created.
317318
- `[in] data`: Data created by the secondary thread. It is the responsibility of
@@ -4188,6 +4189,11 @@ prevent the event loop from exiting. The APIs `napi_ref_threadsafe_function` and
41884189

41894190
<!-- YAML
41904191
added: v8.16.0
4192+
napiVersion: 4
4193+
changes:
4194+
- version: REPLACEME
4195+
pr-url: https://github.com/nodejs/node/pull/27791
4196+
description: Made `func` parameter optional with custom `call_js_cb`.
41914197
-->
41924198
```C
41934199
NAPI_EXTERN napi_status
@@ -4205,7 +4211,8 @@ napi_create_threadsafe_function(napi_env env,
42054211
```
42064212

42074213
- `[in] env`: The environment that the API is invoked under.
4208-
- `[in] func`: The JavaScript function to call from another thread.
4214+
- `[in] func`: An optional JavaScript function to call from another thread.
4215+
It must be provided if `NULL` is passed to `call_js_cb`.
42094216
- `[in] async_resource`: An optional object associated with the async work that
42104217
will be passed to possible `async_hooks` [`init` hooks][].
42114218
- `[in] async_resource_name`: A javaScript string to provide an identifier for

src/node_api.cc

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,10 +1072,14 @@ class ThreadSafeFunction : public node::AsyncResource {
10721072
"ERR_NAPI_TSFN_STOP_IDLE_LOOP",
10731073
"Failed to stop the idle loop") == napi_ok);
10741074
} else {
1075-
v8::Local<v8::Function> js_cb =
1075+
napi_value js_callback = nullptr;
1076+
if (!ref.IsEmpty()) {
1077+
v8::Local<v8::Function> js_cb =
10761078
v8::Local<v8::Function>::New(env->isolate, ref);
1079+
js_callback = v8impl::JsValueFromV8LocalValue(js_cb);
1080+
}
10771081
call_js_cb(env,
1078-
v8impl::JsValueFromV8LocalValue(js_cb),
1082+
js_callback,
10791083
context,
10801084
data);
10811085
}
@@ -3968,15 +3972,18 @@ napi_create_threadsafe_function(napi_env env,
39683972
napi_threadsafe_function_call_js call_js_cb,
39693973
napi_threadsafe_function* result) {
39703974
CHECK_ENV(env);
3971-
CHECK_ARG(env, func);
39723975
CHECK_ARG(env, async_resource_name);
39733976
RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg);
39743977
CHECK_ARG(env, result);
39753978

39763979
napi_status status = napi_ok;
39773980

39783981
v8::Local<v8::Function> v8_func;
3979-
CHECK_TO_FUNCTION(env, v8_func, func);
3982+
if (func == nullptr) {
3983+
CHECK_ARG(env, call_js_cb);
3984+
} else {
3985+
CHECK_TO_FUNCTION(env, v8_func, func);
3986+
}
39803987

39813988
v8::Local<v8::Context> v8_context = env->isolate->GetCurrentContext();
39823989

test/addons-napi/test_threadsafe_function/binding.c

Lines changed: 38 additions & 5 deletions
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/addons-napi/test_threadsafe_function/test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,17 @@ new Promise(function testWithoutJSMarshaller(resolve) {
104104
}))
105105
.then((result) => assert.deepStrictEqual(result, expectedArray))
106106

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

0 commit comments

Comments
 (0)