Skip to content

Commit 239377b

Browse files
Gabriel SchulhofBethGriggs
authored andcommitted
n-api: correct instance data tests
When instance data was backported, some of the tests ended up in a location where they do not get run. This moves the tests into test/addons-napi, merging them with existing tests therein, thereby ensuring that they do get run. PR-URL: #32488 Reviewed-By: Beth Griggs <Bethany.Griggs@uk.ibm.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
1 parent 8a0ed8f commit 239377b

File tree

7 files changed

+215
-291
lines changed

7 files changed

+215
-291
lines changed

β€Žtest/addons-napi/test_instance_data/test.jsβ€Ž

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,20 @@ if (module.parent) {
1919
// Test that the instance data can be accessed from a finalizer.
2020
test_instance_data.objectWithFinalizer(common.mustCall());
2121
global.gc();
22+
23+
// Test that instance data can be used in an async work callback.
24+
new Promise((resolve) => test_instance_data.asyncWorkCallback(resolve))
25+
26+
// Test that the buffer finalizer can access the instance data.
27+
.then(() => new Promise((resolve) => {
28+
test_instance_data.testBufferFinalizer(resolve);
29+
global.gc();
30+
}))
31+
32+
// Test that the thread-safe function can access the instance data.
33+
.then(() => new Promise((resolve) =>
34+
test_instance_data.testThreadsafeFunction(common.mustCall(),
35+
common.mustCall(resolve))));
2236
} else {
2337
// When launched as a script, run tests in either a child process or in a
2438
// worker thread.

β€Žtest/addons-napi/test_instance_data/test_instance_data.cβ€Ž

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <stdio.h>
22
#include <stdlib.h>
3+
#include <uv.h>
34
#define NAPI_EXPERIMENTAL
45
#include <node_api.h>
56
#include "../common.h"
@@ -8,6 +9,9 @@ typedef struct {
89
size_t value;
910
bool print;
1011
napi_ref js_cb_ref;
12+
napi_ref js_tsfn_finalizer_ref;
13+
napi_threadsafe_function tsfn;
14+
uv_thread_t thread;
1115
} AddonData;
1216

1317
static napi_value Increment(napi_env env, napi_callback_info info) {
@@ -20,6 +24,194 @@ static napi_value Increment(napi_env env, napi_callback_info info) {
2024
return result;
2125
}
2226

27+
static void AsyncWorkCbExecute(napi_env env, void* data) {
28+
(void) env;
29+
(void) data;
30+
}
31+
32+
static void call_cb_and_delete_ref(napi_env env, napi_ref* optional_ref) {
33+
napi_value js_cb, undefined;
34+
35+
if (optional_ref == NULL) {
36+
AddonData* data;
37+
NAPI_CALL_RETURN_VOID(env, napi_get_instance_data(env, (void**)&data));
38+
optional_ref = &data->js_cb_ref;
39+
}
40+
41+
NAPI_CALL_RETURN_VOID(env, napi_get_reference_value(env,
42+
*optional_ref,
43+
&js_cb));
44+
NAPI_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
45+
NAPI_CALL_RETURN_VOID(env, napi_call_function(env,
46+
undefined,
47+
js_cb,
48+
0,
49+
NULL,
50+
NULL));
51+
NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, *optional_ref));
52+
53+
*optional_ref = NULL;
54+
}
55+
56+
static void AsyncWorkCbComplete(napi_env env,
57+
napi_status status,
58+
void* data) {
59+
(void) status;
60+
(void) data;
61+
call_cb_and_delete_ref(env, NULL);
62+
}
63+
64+
static bool establish_callback_ref(napi_env env, napi_callback_info info) {
65+
AddonData* data;
66+
size_t argc = 1;
67+
napi_value js_cb;
68+
69+
NAPI_CALL_BASE(env, napi_get_instance_data(env, (void**)&data), false);
70+
NAPI_ASSERT_BASE(env,
71+
data->js_cb_ref == NULL,
72+
"reference must be NULL",
73+
false);
74+
NAPI_CALL_BASE(env,
75+
napi_get_cb_info(env, info, &argc, &js_cb, NULL, NULL),
76+
false);
77+
NAPI_CALL_BASE(env,
78+
napi_create_reference(env, js_cb, 1, &data->js_cb_ref),
79+
false);
80+
81+
return true;
82+
}
83+
84+
static napi_value AsyncWorkCallback(napi_env env, napi_callback_info info) {
85+
if (establish_callback_ref(env, info)) {
86+
napi_value resource_name;
87+
napi_async_work work;
88+
89+
NAPI_CALL(env, napi_create_string_utf8(env,
90+
"AsyncIncrement",
91+
NAPI_AUTO_LENGTH,
92+
&resource_name));
93+
NAPI_CALL(env, napi_create_async_work(env,
94+
NULL,
95+
resource_name,
96+
AsyncWorkCbExecute,
97+
AsyncWorkCbComplete,
98+
NULL,
99+
&work));
100+
NAPI_CALL(env, napi_queue_async_work(env, work));
101+
}
102+
103+
return NULL;
104+
}
105+
106+
static void TestBufferFinalizerCallback(napi_env env, void* data, void* hint) {
107+
(void) data;
108+
(void) hint;
109+
call_cb_and_delete_ref(env, NULL);
110+
}
111+
112+
static napi_value TestBufferFinalizer(napi_env env, napi_callback_info info) {
113+
napi_value buffer = NULL;
114+
if (establish_callback_ref(env, info)) {
115+
NAPI_CALL(env, napi_create_external_buffer(env,
116+
sizeof(napi_callback),
117+
TestBufferFinalizer,
118+
TestBufferFinalizerCallback,
119+
NULL,
120+
&buffer));
121+
}
122+
return buffer;
123+
}
124+
125+
static void ThreadsafeFunctionCallJS(napi_env env,
126+
napi_value tsfn_cb,
127+
void* context,
128+
void* data) {
129+
(void) tsfn_cb;
130+
(void) context;
131+
(void) data;
132+
call_cb_and_delete_ref(env, NULL);
133+
}
134+
135+
static void ThreadsafeFunctionTestThread(void* raw_data) {
136+
AddonData* data = raw_data;
137+
napi_status status;
138+
139+
// No need to call `napi_acquire_threadsafe_function()` because the main
140+
// thread has set the refcount to 1 and there is only this one secondary
141+
// thread.
142+
status = napi_call_threadsafe_function(data->tsfn,
143+
ThreadsafeFunctionCallJS,
144+
napi_tsfn_nonblocking);
145+
if (status != napi_ok) {
146+
napi_fatal_error("ThreadSafeFunctionTestThread",
147+
NAPI_AUTO_LENGTH,
148+
"Failed to call TSFN",
149+
NAPI_AUTO_LENGTH);
150+
}
151+
152+
status = napi_release_threadsafe_function(data->tsfn, napi_tsfn_release);
153+
if (status != napi_ok) {
154+
napi_fatal_error("ThreadSafeFunctionTestThread",
155+
NAPI_AUTO_LENGTH,
156+
"Failed to release TSFN",
157+
NAPI_AUTO_LENGTH);
158+
}
159+
160+
}
161+
162+
static void FinalizeThreadsafeFunction(napi_env env, void* raw, void* hint) {
163+
AddonData* data;
164+
NAPI_CALL_RETURN_VOID(env, napi_get_instance_data(env, (void**)&data));
165+
NAPI_ASSERT_RETURN_VOID(env,
166+
uv_thread_join(&data->thread) == 0,
167+
"Failed to join the thread");
168+
call_cb_and_delete_ref(env, &data->js_tsfn_finalizer_ref);
169+
data->tsfn = NULL;
170+
}
171+
172+
// Ths function accepts two arguments: the JS callback, and the finalize
173+
// callback. The latter moves the test forward.
174+
static napi_value
175+
TestThreadsafeFunction(napi_env env, napi_callback_info info) {
176+
AddonData* data;
177+
size_t argc = 2;
178+
napi_value argv[2], resource_name;
179+
180+
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
181+
NAPI_CALL(env, napi_get_instance_data(env, (void**)&data));
182+
NAPI_ASSERT(env, data->js_cb_ref == NULL, "reference must be NULL");
183+
NAPI_ASSERT(env,
184+
data->js_tsfn_finalizer_ref == NULL,
185+
"tsfn finalizer reference must be NULL");
186+
NAPI_CALL(env, napi_create_reference(env, argv[0], 1, &data->js_cb_ref));
187+
NAPI_CALL(env, napi_create_reference(env,
188+
argv[1],
189+
1,
190+
&data->js_tsfn_finalizer_ref));
191+
NAPI_CALL(env, napi_create_string_utf8(env,
192+
"TSFN instance data test",
193+
NAPI_AUTO_LENGTH,
194+
&resource_name));
195+
NAPI_CALL(env, napi_create_threadsafe_function(env,
196+
NULL,
197+
NULL,
198+
resource_name,
199+
0,
200+
1,
201+
NULL,
202+
FinalizeThreadsafeFunction,
203+
NULL,
204+
ThreadsafeFunctionCallJS,
205+
&data->tsfn));
206+
NAPI_ASSERT(env,
207+
uv_thread_create(&data->thread,
208+
ThreadsafeFunctionTestThread,
209+
data) == 0,
210+
"uv_thread_create failed");
211+
212+
return NULL;
213+
}
214+
23215
static void DeleteAddonData(napi_env env, void* raw_data, void* hint) {
24216
AddonData* data = raw_data;
25217
if (data->print) {
@@ -28,6 +220,11 @@ static void DeleteAddonData(napi_env env, void* raw_data, void* hint) {
28220
if (data->js_cb_ref != NULL) {
29221
NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, data->js_cb_ref));
30222
}
223+
if (data->js_tsfn_finalizer_ref) {
224+
NAPI_CALL_RETURN_VOID(env,
225+
napi_delete_reference(env,
226+
data->js_tsfn_finalizer_ref));
227+
}
31228
free(data);
32229
}
33230

@@ -77,13 +274,17 @@ napi_value Init(napi_env env, napi_value exports) {
77274
data->value = 41;
78275
data->print = false;
79276
data->js_cb_ref = NULL;
277+
data->js_tsfn_finalizer_ref = NULL;
80278

81279
NAPI_CALL(env, napi_set_instance_data(env, data, DeleteAddonData, NULL));
82280

83281
napi_property_descriptor props[] = {
84282
DECLARE_NAPI_PROPERTY("increment", Increment),
85283
DECLARE_NAPI_PROPERTY("setPrintOnDelete", SetPrintOnDelete),
86284
DECLARE_NAPI_PROPERTY("objectWithFinalizer", ObjectWithFinalizer),
285+
DECLARE_NAPI_PROPERTY("asyncWorkCallback", AsyncWorkCallback),
286+
DECLARE_NAPI_PROPERTY("testBufferFinalizer", TestBufferFinalizer),
287+
DECLARE_NAPI_PROPERTY("testThreadsafeFunction", TestThreadsafeFunction),
87288
};
88289

89290
NAPI_CALL(env, napi_define_properties(env,

β€Žtest/node-api/.gitignoreβ€Ž

Lines changed: 0 additions & 1 deletion
This file was deleted.

β€Žtest/node-api/node-api.statusβ€Ž

Lines changed: 0 additions & 9 deletions
This file was deleted.

β€Žtest/node-api/test_instance_data/binding.gypβ€Ž

Lines changed: 0 additions & 10 deletions
This file was deleted.

β€Žtest/node-api/test_instance_data/test.jsβ€Ž

Lines changed: 0 additions & 35 deletions
This file was deleted.

0 commit comments

Comments
Β (0)