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