• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <assert.h>
2 #include <stdio.h>
3 #include <node_api.h>
4 #include "../../js-native-api/common.h"
5 
6 #if defined _WIN32
7 #include <windows.h>
8 #else
9 #include <unistd.h>
10 #endif
11 
12 // this needs to be greater than the thread pool size
13 #define MAX_CANCEL_THREADS 6
14 
15 typedef struct {
16   int32_t _input;
17   int32_t _output;
18   napi_ref _callback;
19   napi_async_work _request;
20 } carrier;
21 
22 static carrier the_carrier;
23 static carrier async_carrier[MAX_CANCEL_THREADS];
24 
Execute(napi_env env,void * data)25 static void Execute(napi_env env, void* data) {
26 #if defined _WIN32
27   Sleep(1000);
28 #else
29   sleep(1);
30 #endif
31   carrier* c = (carrier*)(data);
32 
33   assert(c == &the_carrier);
34 
35   c->_output = c->_input * 2;
36 }
37 
Complete(napi_env env,napi_status status,void * data)38 static void Complete(napi_env env, napi_status status, void* data) {
39   carrier* c = (carrier*)(data);
40 
41   if (c != &the_carrier) {
42     napi_throw_type_error(env, NULL, "Wrong data parameter to Complete.");
43     return;
44   }
45 
46   if (status != napi_ok) {
47     napi_throw_type_error(env, NULL, "Execute callback failed.");
48     return;
49   }
50 
51   napi_value argv[2];
52 
53   NAPI_CALL_RETURN_VOID(env, napi_get_null(env, &argv[0]));
54   NAPI_CALL_RETURN_VOID(env, napi_create_int32(env, c->_output, &argv[1]));
55   napi_value callback;
56   NAPI_CALL_RETURN_VOID(env,
57       napi_get_reference_value(env, c->_callback, &callback));
58   napi_value global;
59   NAPI_CALL_RETURN_VOID(env, napi_get_global(env, &global));
60 
61   napi_value result;
62   NAPI_CALL_RETURN_VOID(env,
63     napi_call_function(env, global, callback, 2, argv, &result));
64 
65   NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, c->_callback));
66   NAPI_CALL_RETURN_VOID(env, napi_delete_async_work(env, c->_request));
67 }
68 
Test(napi_env env,napi_callback_info info)69 static napi_value Test(napi_env env, napi_callback_info info) {
70   size_t argc = 3;
71   napi_value argv[3];
72   napi_value _this;
73   napi_value resource_name;
74   void* data;
75   NAPI_CALL(env,
76     napi_get_cb_info(env, info, &argc, argv, &_this, &data));
77   NAPI_ASSERT(env, argc >= 3, "Not enough arguments, expected 2.");
78 
79   napi_valuetype t;
80   NAPI_CALL(env, napi_typeof(env, argv[0], &t));
81   NAPI_ASSERT(env, t == napi_number,
82       "Wrong first argument, integer expected.");
83   NAPI_CALL(env, napi_typeof(env, argv[1], &t));
84   NAPI_ASSERT(env, t == napi_object,
85     "Wrong second argument, object expected.");
86   NAPI_CALL(env, napi_typeof(env, argv[2], &t));
87   NAPI_ASSERT(env, t == napi_function,
88     "Wrong third argument, function expected.");
89 
90   the_carrier._output = 0;
91 
92   NAPI_CALL(env,
93       napi_get_value_int32(env, argv[0], &the_carrier._input));
94   NAPI_CALL(env,
95     napi_create_reference(env, argv[2], 1, &the_carrier._callback));
96 
97   NAPI_CALL(env, napi_create_string_utf8(
98       env, "TestResource", NAPI_AUTO_LENGTH, &resource_name));
99   NAPI_CALL(env, napi_create_async_work(env, argv[1], resource_name,
100     Execute, Complete, &the_carrier, &the_carrier._request));
101   NAPI_CALL(env,
102       napi_queue_async_work(env, the_carrier._request));
103 
104   return NULL;
105 }
106 
BusyCancelComplete(napi_env env,napi_status status,void * data)107 static void BusyCancelComplete(napi_env env, napi_status status, void* data) {
108   carrier* c = (carrier*)(data);
109   NAPI_CALL_RETURN_VOID(env, napi_delete_async_work(env, c->_request));
110 }
111 
CancelComplete(napi_env env,napi_status status,void * data)112 static void CancelComplete(napi_env env, napi_status status, void* data) {
113   carrier* c = (carrier*)(data);
114 
115   if (status == napi_cancelled) {
116     // ok we got the status we expected so make the callback to
117     // indicate the cancel succeeded.
118     napi_value callback;
119     NAPI_CALL_RETURN_VOID(env,
120         napi_get_reference_value(env, c->_callback, &callback));
121     napi_value global;
122     NAPI_CALL_RETURN_VOID(env, napi_get_global(env, &global));
123     napi_value result;
124     NAPI_CALL_RETURN_VOID(env,
125       napi_call_function(env, global, callback, 0, NULL, &result));
126   }
127 
128   NAPI_CALL_RETURN_VOID(env, napi_delete_async_work(env, c->_request));
129   NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, c->_callback));
130 }
131 
CancelExecute(napi_env env,void * data)132 static void CancelExecute(napi_env env, void* data) {
133 #if defined _WIN32
134   Sleep(1000);
135 #else
136   sleep(1);
137 #endif
138 }
139 
TestCancel(napi_env env,napi_callback_info info)140 static napi_value TestCancel(napi_env env, napi_callback_info info) {
141   size_t argc = 1;
142   napi_value argv[1];
143   napi_value _this;
144   napi_value resource_name;
145   void* data;
146 
147   NAPI_CALL(env, napi_create_string_utf8(
148       env, "TestResource", NAPI_AUTO_LENGTH, &resource_name));
149 
150   // make sure the work we are going to cancel will not be
151   // able to start by using all the threads in the pool
152   for (int i = 1; i < MAX_CANCEL_THREADS; i++) {
153     NAPI_CALL(env, napi_create_async_work(env, NULL, resource_name,
154       CancelExecute, BusyCancelComplete,
155       &async_carrier[i], &async_carrier[i]._request));
156     NAPI_CALL(env, napi_queue_async_work(env, async_carrier[i]._request));
157   }
158 
159   // now queue the work we are going to cancel and then cancel it.
160   // cancel will fail if the work has already started, but
161   // we have prevented it from starting by consuming all of the
162   // workers above.
163   NAPI_CALL(env,
164     napi_get_cb_info(env, info, &argc, argv, &_this, &data));
165   NAPI_CALL(env, napi_create_async_work(env, NULL, resource_name,
166     CancelExecute, CancelComplete,
167     &async_carrier[0], &async_carrier[0]._request));
168   NAPI_CALL(env,
169       napi_create_reference(env, argv[0], 1, &async_carrier[0]._callback));
170   NAPI_CALL(env, napi_queue_async_work(env, async_carrier[0]._request));
171   NAPI_CALL(env, napi_cancel_async_work(env, async_carrier[0]._request));
172   return NULL;
173 }
174 
175 struct {
176   napi_ref ref;
177   napi_async_work work;
178 } repeated_work_info = { NULL, NULL };
179 
RepeatedWorkerThread(napi_env env,void * data)180 static void RepeatedWorkerThread(napi_env env, void* data) {}
181 
RepeatedWorkComplete(napi_env env,napi_status status,void * data)182 static void RepeatedWorkComplete(napi_env env, napi_status status, void* data) {
183   napi_value cb, js_status;
184   NAPI_CALL_RETURN_VOID(env,
185       napi_get_reference_value(env, repeated_work_info.ref, &cb));
186   NAPI_CALL_RETURN_VOID(env,
187       napi_delete_async_work(env, repeated_work_info.work));
188   NAPI_CALL_RETURN_VOID(env,
189       napi_delete_reference(env, repeated_work_info.ref));
190   repeated_work_info.work = NULL;
191   repeated_work_info.ref = NULL;
192   NAPI_CALL_RETURN_VOID(env,
193       napi_create_uint32(env, (uint32_t)status, &js_status));
194   NAPI_CALL_RETURN_VOID(env,
195       napi_call_function(env, cb, cb, 1, &js_status, NULL));
196 }
197 
DoRepeatedWork(napi_env env,napi_callback_info info)198 static napi_value DoRepeatedWork(napi_env env, napi_callback_info info) {
199   size_t argc = 1;
200   napi_value cb, name;
201   NAPI_ASSERT(env, repeated_work_info.ref == NULL,
202       "Reference left over from previous work");
203   NAPI_ASSERT(env, repeated_work_info.work == NULL,
204       "Work pointer left over from previous work");
205   NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &cb, NULL, NULL));
206   NAPI_CALL(env, napi_create_reference(env, cb, 1, &repeated_work_info.ref));
207   NAPI_CALL(env,
208       napi_create_string_utf8(env, "Repeated Work", NAPI_AUTO_LENGTH, &name));
209   NAPI_CALL(env,
210       napi_create_async_work(env, NULL, name, RepeatedWorkerThread,
211           RepeatedWorkComplete, &repeated_work_info, &repeated_work_info.work));
212   NAPI_CALL(env, napi_queue_async_work(env, repeated_work_info.work));
213   return NULL;
214 }
215 
Init(napi_env env,napi_value exports)216 static napi_value Init(napi_env env, napi_value exports) {
217   napi_property_descriptor properties[] = {
218     DECLARE_NAPI_PROPERTY("Test", Test),
219     DECLARE_NAPI_PROPERTY("TestCancel", TestCancel),
220     DECLARE_NAPI_PROPERTY("DoRepeatedWork", DoRepeatedWork),
221   };
222 
223   NAPI_CALL(env, napi_define_properties(
224       env, exports, sizeof(properties) / sizeof(*properties), properties));
225 
226   return exports;
227 }
228 
229 NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
230