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