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
1317static 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+
23215static 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 ,
0 commit comments