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 10typedef struct { 11 int32_t _input; 12 int32_t _output; 13 napi_ref _callback; 14 napi_async_work _request; 15} carrier; 16 17static carrier the_carrier; 18static carrier async_carrier[MAX_CANCEL_THREADS]; 19 20static 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 29static 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 60static 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 98static 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 103static 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 123static void CancelExecute(napi_env env, void* data) { 124 uv_sleep(1000); 125} 126 127static 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 162struct { 163 napi_ref ref; 164 napi_async_work work; 165} repeated_work_info = { NULL, NULL }; 166 167static void RepeatedWorkerThread(napi_env env, void* data) {} 168 169static 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 185static 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 203static 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 216NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) 217