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