1
1
#include <stdio.h>
2
2
#include <stdlib.h>
3
+ #include <uv.h>
3
4
#define NAPI_EXPERIMENTAL
4
5
#include <node_api.h>
5
6
#include "../common.h"
@@ -8,6 +9,9 @@ typedef struct {
8
9
size_t value ;
9
10
bool print ;
10
11
napi_ref js_cb_ref ;
12
+ napi_ref js_tsfn_finalizer_ref ;
13
+ napi_threadsafe_function tsfn ;
14
+ uv_thread_t thread ;
11
15
} AddonData ;
12
16
13
17
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) {
20
24
return result ;
21
25
}
22
26
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
+
23
215
static void DeleteAddonData (napi_env env , void * raw_data , void * hint ) {
24
216
AddonData * data = raw_data ;
25
217
if (data -> print ) {
@@ -28,6 +220,11 @@ static void DeleteAddonData(napi_env env, void* raw_data, void* hint) {
28
220
if (data -> js_cb_ref != NULL ) {
29
221
NAPI_CALL_RETURN_VOID (env , napi_delete_reference (env , data -> js_cb_ref ));
30
222
}
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
+ }
31
228
free (data );
32
229
}
33
230
@@ -77,13 +274,17 @@ napi_value Init(napi_env env, napi_value exports) {
77
274
data -> value = 41 ;
78
275
data -> print = false;
79
276
data -> js_cb_ref = NULL ;
277
+ data -> js_tsfn_finalizer_ref = NULL ;
80
278
81
279
NAPI_CALL (env , napi_set_instance_data (env , data , DeleteAddonData , NULL ));
82
280
83
281
napi_property_descriptor props [] = {
84
282
DECLARE_NAPI_PROPERTY ("increment" , Increment ),
85
283
DECLARE_NAPI_PROPERTY ("setPrintOnDelete" , SetPrintOnDelete ),
86
284
DECLARE_NAPI_PROPERTY ("objectWithFinalizer" , ObjectWithFinalizer ),
285
+ DECLARE_NAPI_PROPERTY ("asyncWorkCallback" , AsyncWorkCallback ),
286
+ DECLARE_NAPI_PROPERTY ("testBufferFinalizer" , TestBufferFinalizer ),
287
+ DECLARE_NAPI_PROPERTY ("testThreadsafeFunction" , TestThreadsafeFunction ),
87
288
};
88
289
89
290
NAPI_CALL (env , napi_define_properties (env ,
0 commit comments