emnapi 0.31.0 → 0.32.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +22 -1
- package/README.md +19 -16
- package/dist/library_napi.js +217 -9
- package/index.js +6 -0
- package/lib/wasm32/libemnapi-basic.a +0 -0
- package/lib/wasm32/libemnapi.a +0 -0
- package/lib/wasm32-emscripten/libemnapi-basic.a +0 -0
- package/lib/wasm32-emscripten/libemnapi-mt.a +0 -0
- package/lib/wasm32-emscripten/libemnapi.a +0 -0
- package/lib/wasm32-emscripten.txt +2 -2
- package/lib/wasm32-wasi/libemnapi-basic.a +0 -0
- package/lib/wasm32-wasi/libemnapi.a +0 -0
- package/package.json +1 -1
- package/src/async_cleanup_hook.c +126 -0
- package/src/async_context.c +53 -0
- package/src/async_work.c +236 -0
- package/src/emnapi.c +1 -1324
- package/src/emnapi_common.h +171 -0
- package/src/js_native_api.c +106 -0
- package/src/node_api.c +73 -0
- package/src/threadsafe_function.c +617 -0
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
#include "node_api.h"
|
|
2
|
+
#include "emnapi_common.h"
|
|
3
|
+
|
|
4
|
+
#if NAPI_VERSION >= 4 && EMNAPI_HAVE_THREADS
|
|
5
|
+
#include <stdatomic.h>
|
|
6
|
+
#include <pthread.h>
|
|
7
|
+
#include <errno.h>
|
|
8
|
+
#include "uv/queue.h"
|
|
9
|
+
|
|
10
|
+
#include "uv.h"
|
|
11
|
+
|
|
12
|
+
EXTERN_C_START
|
|
13
|
+
|
|
14
|
+
EMNAPI_INTERNAL_EXTERN void _emnapi_call_finalizer(napi_env env, napi_finalize cb, void* data, void* hint);
|
|
15
|
+
|
|
16
|
+
static const unsigned char kDispatchIdle = 0;
|
|
17
|
+
static const unsigned char kDispatchRunning = 1 << 0;
|
|
18
|
+
static const unsigned char kDispatchPending = 1 << 1;
|
|
19
|
+
|
|
20
|
+
static const unsigned int kMaxIterationCount = 1000;
|
|
21
|
+
|
|
22
|
+
struct data_queue_node {
|
|
23
|
+
void* data;
|
|
24
|
+
void* q[2];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
struct napi_threadsafe_function__ {
|
|
28
|
+
ASYNC_RESOURCE_FIELD
|
|
29
|
+
// These are variables protected by the mutex.
|
|
30
|
+
pthread_mutex_t mutex;
|
|
31
|
+
pthread_cond_t* cond;
|
|
32
|
+
size_t queue_size;
|
|
33
|
+
void* queue[2];
|
|
34
|
+
uv_async_t async;
|
|
35
|
+
size_t thread_count;
|
|
36
|
+
bool is_closing;
|
|
37
|
+
atomic_uchar dispatch_state;
|
|
38
|
+
|
|
39
|
+
// These are variables set once, upon creation, and then never again, which
|
|
40
|
+
// means we don't need the mutex to read them.
|
|
41
|
+
void* context;
|
|
42
|
+
size_t max_queue_size;
|
|
43
|
+
|
|
44
|
+
// These are variables accessed only from the loop thread.
|
|
45
|
+
napi_ref ref;
|
|
46
|
+
napi_env env;
|
|
47
|
+
void* finalize_data;
|
|
48
|
+
napi_finalize finalize_cb;
|
|
49
|
+
napi_threadsafe_function_call_js call_js_cb;
|
|
50
|
+
bool handles_closing;
|
|
51
|
+
bool async_ref;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
static void _emnapi_tsfn_default_call_js(napi_env env, napi_value cb, void* context, void* data) {
|
|
55
|
+
if (!(env == NULL || cb == NULL)) {
|
|
56
|
+
napi_value recv;
|
|
57
|
+
napi_status status;
|
|
58
|
+
|
|
59
|
+
status = napi_get_undefined(env, &recv);
|
|
60
|
+
if (status != napi_ok) {
|
|
61
|
+
napi_throw_error(env, "ERR_NAPI_TSFN_GET_UNDEFINED",
|
|
62
|
+
"Failed to retrieve undefined value");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
status = napi_call_function(env, recv, cb, 0, NULL, NULL);
|
|
67
|
+
if (status != napi_ok && status != napi_pending_exception) {
|
|
68
|
+
napi_throw_error(env, "ERR_NAPI_TSFN_CALL_JS",
|
|
69
|
+
"Failed to call JS callback");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static void _emnapi_tsfn_cleanup(void* data);
|
|
76
|
+
|
|
77
|
+
static napi_threadsafe_function
|
|
78
|
+
_emnapi_tsfn_create(napi_env env,
|
|
79
|
+
napi_ref ref,
|
|
80
|
+
napi_value async_resource,
|
|
81
|
+
napi_value async_resource_name,
|
|
82
|
+
size_t max_queue_size,
|
|
83
|
+
size_t initial_thread_count,
|
|
84
|
+
void* thread_finalize_data,
|
|
85
|
+
napi_finalize thread_finalize_cb,
|
|
86
|
+
void* context,
|
|
87
|
+
napi_threadsafe_function_call_js call_js_cb) {
|
|
88
|
+
napi_threadsafe_function ts_fn =
|
|
89
|
+
(napi_threadsafe_function) calloc(1, sizeof(struct napi_threadsafe_function__));
|
|
90
|
+
if (ts_fn == NULL) return NULL;
|
|
91
|
+
EMNAPI_ASYNC_RESOURCE_CTOR(env, async_resource, async_resource_name, (emnapi_async_resource*) ts_fn);
|
|
92
|
+
pthread_mutex_init(&ts_fn->mutex, NULL);
|
|
93
|
+
ts_fn->cond = NULL;
|
|
94
|
+
ts_fn->queue_size = 0;
|
|
95
|
+
QUEUE_INIT(&ts_fn->queue);
|
|
96
|
+
ts_fn->thread_count = initial_thread_count;
|
|
97
|
+
ts_fn->is_closing = false;
|
|
98
|
+
ts_fn->dispatch_state = kDispatchIdle;
|
|
99
|
+
|
|
100
|
+
ts_fn->context = context;
|
|
101
|
+
ts_fn->max_queue_size = max_queue_size;
|
|
102
|
+
|
|
103
|
+
ts_fn->ref = ref;
|
|
104
|
+
ts_fn->env = env;
|
|
105
|
+
ts_fn->finalize_data = thread_finalize_data;
|
|
106
|
+
ts_fn->finalize_cb = thread_finalize_cb;
|
|
107
|
+
ts_fn->call_js_cb = call_js_cb;
|
|
108
|
+
ts_fn->handles_closing = false;
|
|
109
|
+
|
|
110
|
+
EMNAPI_ASSERT_CALL(napi_add_env_cleanup_hook(env, _emnapi_tsfn_cleanup, ts_fn));
|
|
111
|
+
_emnapi_env_ref(env);
|
|
112
|
+
|
|
113
|
+
EMNAPI_KEEPALIVE_PUSH();
|
|
114
|
+
_emnapi_ctx_increase_waiting_request_counter();
|
|
115
|
+
ts_fn->async_ref = true;
|
|
116
|
+
return ts_fn;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static void _emnapi_tsfn_destroy(napi_threadsafe_function func) {
|
|
120
|
+
if (func == NULL) return;
|
|
121
|
+
pthread_mutex_destroy(&func->mutex);
|
|
122
|
+
if (func->cond) {
|
|
123
|
+
pthread_cond_destroy(func->cond);
|
|
124
|
+
free(func->cond);
|
|
125
|
+
func->cond = NULL;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
QUEUE* tmp;
|
|
129
|
+
struct data_queue_node* node;
|
|
130
|
+
QUEUE_FOREACH(tmp, &func->queue) {
|
|
131
|
+
node = QUEUE_DATA(tmp, struct data_queue_node, q);
|
|
132
|
+
free(node);
|
|
133
|
+
}
|
|
134
|
+
QUEUE_INIT(&func->queue);
|
|
135
|
+
|
|
136
|
+
if (func->ref != NULL) {
|
|
137
|
+
EMNAPI_ASSERT_CALL(napi_delete_reference(func->env, func->ref));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
EMNAPI_ASSERT_CALL(napi_remove_env_cleanup_hook(func->env, _emnapi_tsfn_cleanup, func));
|
|
141
|
+
_emnapi_env_unref(func->env);
|
|
142
|
+
if (func->async_ref) {
|
|
143
|
+
EMNAPI_KEEPALIVE_POP();
|
|
144
|
+
_emnapi_ctx_decrease_waiting_request_counter();
|
|
145
|
+
func->async_ref = false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
EMNAPI_ASYNC_RESOURCE_DTOR(func->env, (emnapi_async_resource*) func);
|
|
149
|
+
free(func);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
static void _emnapi_tsfn_do_destroy(uv_handle_t* data) {
|
|
153
|
+
napi_threadsafe_function func = container_of(data, struct napi_threadsafe_function__, async);
|
|
154
|
+
_emnapi_tsfn_destroy(func);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
static void _emnapi_tsfn_dispatch(napi_threadsafe_function func);
|
|
158
|
+
|
|
159
|
+
// only main thread
|
|
160
|
+
static void _emnapi_tsfn_async_cb(uv_async_t* data) {
|
|
161
|
+
napi_threadsafe_function tsfn = container_of(data, struct napi_threadsafe_function__, async);
|
|
162
|
+
_emnapi_tsfn_dispatch(tsfn);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// only main thread
|
|
166
|
+
static napi_status _emnapi_tsfn_init(napi_threadsafe_function func) {
|
|
167
|
+
uv_loop_t* loop = uv_default_loop();
|
|
168
|
+
if (uv_async_init(loop, &func->async, _emnapi_tsfn_async_cb) == 0) {
|
|
169
|
+
int r;
|
|
170
|
+
if (func->max_queue_size > 0) {
|
|
171
|
+
func->cond = (pthread_cond_t*) malloc(sizeof(pthread_cond_t));
|
|
172
|
+
if (func->cond != NULL) {
|
|
173
|
+
r = pthread_cond_init(func->cond, NULL);
|
|
174
|
+
if (r != 0) {
|
|
175
|
+
free(func->cond);
|
|
176
|
+
func->cond = NULL;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (func->max_queue_size == 0 || func->cond) {
|
|
181
|
+
return napi_ok;
|
|
182
|
+
}
|
|
183
|
+
uv_close((uv_handle_t*) &func->async, _emnapi_tsfn_do_destroy);
|
|
184
|
+
}
|
|
185
|
+
_emnapi_tsfn_destroy(func);
|
|
186
|
+
return napi_generic_failure;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
static void _emnapi_tsfn_empty_queue_and_delete(napi_threadsafe_function func) {
|
|
190
|
+
while (!QUEUE_EMPTY(&func->queue)) {
|
|
191
|
+
QUEUE* q = QUEUE_HEAD(&func->queue);
|
|
192
|
+
struct data_queue_node* node = QUEUE_DATA(q, struct data_queue_node, q);
|
|
193
|
+
|
|
194
|
+
func->call_js_cb(NULL, NULL, func->context, node->data);
|
|
195
|
+
|
|
196
|
+
QUEUE_REMOVE(q);
|
|
197
|
+
QUEUE_INIT(q);
|
|
198
|
+
func->queue_size--;
|
|
199
|
+
free(node);
|
|
200
|
+
}
|
|
201
|
+
_emnapi_tsfn_destroy(func);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
static napi_value _emnapi_tsfn_finalize_in_callback_scope(napi_env env, napi_callback_info info) {
|
|
205
|
+
void* data = NULL;
|
|
206
|
+
EMNAPI_ASSERT_CALL(napi_get_cb_info(env, info, NULL, NULL, NULL, &data));
|
|
207
|
+
napi_threadsafe_function func = (napi_threadsafe_function) data;
|
|
208
|
+
_emnapi_call_finalizer(func->env, func->finalize_cb, func->finalize_data, func->context);
|
|
209
|
+
return NULL;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
static void _emnapi_tsfn_finalize(napi_threadsafe_function func) {
|
|
213
|
+
napi_handle_scope scope;
|
|
214
|
+
EMNAPI_ASSERT_CALL(napi_open_handle_scope(func->env, &scope));
|
|
215
|
+
if (func->finalize_cb) {
|
|
216
|
+
if (emnapi_is_node_binding_available()) {
|
|
217
|
+
napi_value resource, cb;
|
|
218
|
+
EMNAPI_ASSERT_CALL(napi_get_reference_value(func->env, func->resource_, &resource));
|
|
219
|
+
EMNAPI_ASSERT_CALL(napi_create_function(func->env, NULL, 0, _emnapi_tsfn_finalize_in_callback_scope, func, &cb));
|
|
220
|
+
_emnapi_node_make_callback(func->env,
|
|
221
|
+
resource,
|
|
222
|
+
cb,
|
|
223
|
+
NULL,
|
|
224
|
+
0,
|
|
225
|
+
func->async_context_.async_id,
|
|
226
|
+
func->async_context_.trigger_async_id,
|
|
227
|
+
NULL);
|
|
228
|
+
} else {
|
|
229
|
+
_emnapi_call_finalizer(func->env, func->finalize_cb, func->finalize_data, func->context);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
_emnapi_tsfn_empty_queue_and_delete(func);
|
|
233
|
+
EMNAPI_ASSERT_CALL(napi_close_handle_scope(func->env, scope));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
static void _emnapi_tsfn_do_finalize(uv_handle_t* data) {
|
|
237
|
+
napi_threadsafe_function func = container_of(data, struct napi_threadsafe_function__, async);
|
|
238
|
+
_emnapi_tsfn_finalize(func);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
static void _emnapi_tsfn_close_handles_and_maybe_delete(
|
|
242
|
+
napi_threadsafe_function func, bool set_closing) {
|
|
243
|
+
napi_handle_scope scope;
|
|
244
|
+
EMNAPI_ASSERT_CALL(napi_open_handle_scope(func->env, &scope));
|
|
245
|
+
|
|
246
|
+
if (set_closing) {
|
|
247
|
+
pthread_mutex_lock(&func->mutex);
|
|
248
|
+
func->is_closing = true;
|
|
249
|
+
if (func->max_queue_size > 0) {
|
|
250
|
+
pthread_cond_signal(func->cond);
|
|
251
|
+
}
|
|
252
|
+
pthread_mutex_unlock(&func->mutex);
|
|
253
|
+
}
|
|
254
|
+
if (func->handles_closing) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
func->handles_closing = true;
|
|
258
|
+
uv_close((uv_handle_t*)&func->async, _emnapi_tsfn_do_finalize);
|
|
259
|
+
|
|
260
|
+
EMNAPI_ASSERT_CALL(napi_close_handle_scope(func->env, scope));
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
static void _emnapi_tsfn_cleanup(void* data) {
|
|
264
|
+
_emnapi_tsfn_close_handles_and_maybe_delete((napi_threadsafe_function) data, true);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
static void _emnapi_tsfn_call_js_cb(napi_env env, void* arg) {
|
|
268
|
+
void** args = (void**) arg;
|
|
269
|
+
napi_threadsafe_function func = (napi_threadsafe_function) *args;
|
|
270
|
+
napi_value js_callback = (napi_value) *(args + 1);
|
|
271
|
+
void* data = *(args + 2);
|
|
272
|
+
func->call_js_cb(func->env, js_callback, func->context, data);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
static napi_value _emnapi_tsfn_call_js_cb_in_callback_scope(napi_env env, napi_callback_info info) {
|
|
276
|
+
void* data = NULL;
|
|
277
|
+
EMNAPI_ASSERT_CALL(napi_get_cb_info(env, info, NULL, NULL, NULL, &data));
|
|
278
|
+
_emnapi_call_into_module(env, _emnapi_tsfn_call_js_cb, data, 1);
|
|
279
|
+
return NULL;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// static void _emnapi_tsfn_
|
|
283
|
+
|
|
284
|
+
// only main thread
|
|
285
|
+
static bool _emnapi_tsfn_dispatch_one(napi_threadsafe_function func) {
|
|
286
|
+
void* data = NULL;
|
|
287
|
+
bool popped_value = false;
|
|
288
|
+
bool has_more = false;
|
|
289
|
+
|
|
290
|
+
{
|
|
291
|
+
pthread_mutex_lock(&func->mutex);
|
|
292
|
+
|
|
293
|
+
if (func->is_closing) {
|
|
294
|
+
_emnapi_tsfn_close_handles_and_maybe_delete(func, false);
|
|
295
|
+
} else {
|
|
296
|
+
size_t size = func->queue_size;
|
|
297
|
+
if (size > 0) {
|
|
298
|
+
QUEUE* q = QUEUE_HEAD(&func->queue);
|
|
299
|
+
struct data_queue_node* node = QUEUE_DATA(q, struct data_queue_node, q);
|
|
300
|
+
QUEUE_REMOVE(q);
|
|
301
|
+
QUEUE_INIT(q);
|
|
302
|
+
func->queue_size--;
|
|
303
|
+
data = node->data;
|
|
304
|
+
free(node);
|
|
305
|
+
popped_value = true;
|
|
306
|
+
if (size == func->max_queue_size && func->max_queue_size > 0) {
|
|
307
|
+
pthread_cond_signal(func->cond);
|
|
308
|
+
}
|
|
309
|
+
size--;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (size == 0) {
|
|
313
|
+
if (func->thread_count == 0) {
|
|
314
|
+
func->is_closing = true;
|
|
315
|
+
if (func->max_queue_size > 0) {
|
|
316
|
+
pthread_cond_signal(func->cond);
|
|
317
|
+
}
|
|
318
|
+
_emnapi_tsfn_close_handles_and_maybe_delete(func, false);
|
|
319
|
+
}
|
|
320
|
+
} else {
|
|
321
|
+
has_more = true;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
pthread_mutex_unlock(&func->mutex);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (popped_value) {
|
|
328
|
+
napi_handle_scope scope;
|
|
329
|
+
EMNAPI_ASSERT_CALL(napi_open_handle_scope(func->env, &scope));
|
|
330
|
+
napi_value js_callback = NULL;
|
|
331
|
+
void* jscb_data[3] = { (void*)(func), NULL, data };
|
|
332
|
+
if (func->ref != NULL) {
|
|
333
|
+
EMNAPI_ASSERT_CALL(napi_get_reference_value(func->env, func->ref, &js_callback));
|
|
334
|
+
jscb_data[1] = (void*)js_callback;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (emnapi_is_node_binding_available()) {
|
|
338
|
+
napi_value resource, cb;
|
|
339
|
+
EMNAPI_ASSERT_CALL(napi_get_reference_value(func->env, func->resource_, &resource));
|
|
340
|
+
EMNAPI_ASSERT_CALL(napi_create_function(func->env, NULL, 0, _emnapi_tsfn_call_js_cb_in_callback_scope, jscb_data, &cb));
|
|
341
|
+
_emnapi_node_make_callback(func->env,
|
|
342
|
+
resource,
|
|
343
|
+
cb,
|
|
344
|
+
NULL,
|
|
345
|
+
0,
|
|
346
|
+
func->async_context_.async_id,
|
|
347
|
+
func->async_context_.trigger_async_id,
|
|
348
|
+
NULL);
|
|
349
|
+
} else {
|
|
350
|
+
_emnapi_call_into_module(func->env, _emnapi_tsfn_call_js_cb, jscb_data, 1);
|
|
351
|
+
}
|
|
352
|
+
EMNAPI_ASSERT_CALL(napi_close_handle_scope(func->env, scope));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return has_more;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// all threads
|
|
359
|
+
static void _emnapi_tsfn_send(napi_threadsafe_function func) {
|
|
360
|
+
unsigned char current_state =
|
|
361
|
+
atomic_fetch_or(&func->dispatch_state, kDispatchPending);
|
|
362
|
+
if ((current_state & kDispatchRunning) == kDispatchRunning) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
uv_async_send(&func->async);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// only main thread
|
|
369
|
+
static void _emnapi_tsfn_dispatch(napi_threadsafe_function func) {
|
|
370
|
+
bool has_more = true;
|
|
371
|
+
|
|
372
|
+
// Limit maximum synchronous iteration count to prevent event loop
|
|
373
|
+
// starvation. See `src/node_messaging.cc` for an inspiration.
|
|
374
|
+
unsigned int iterations_left = kMaxIterationCount;
|
|
375
|
+
while (has_more && --iterations_left != 0) {
|
|
376
|
+
func->dispatch_state = kDispatchRunning;
|
|
377
|
+
has_more = _emnapi_tsfn_dispatch_one(func);
|
|
378
|
+
|
|
379
|
+
// Send() was called while we were executing the JS function
|
|
380
|
+
if (atomic_exchange(&func->dispatch_state, kDispatchIdle) != kDispatchRunning) {
|
|
381
|
+
has_more = true;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (has_more) {
|
|
386
|
+
_emnapi_tsfn_send(func);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
EXTERN_C_END
|
|
391
|
+
|
|
392
|
+
#endif
|
|
393
|
+
|
|
394
|
+
#if NAPI_VERSION >= 4
|
|
395
|
+
|
|
396
|
+
EXTERN_C_START
|
|
397
|
+
|
|
398
|
+
napi_status
|
|
399
|
+
napi_create_threadsafe_function(napi_env env,
|
|
400
|
+
napi_value func,
|
|
401
|
+
napi_value async_resource,
|
|
402
|
+
napi_value async_resource_name,
|
|
403
|
+
size_t max_queue_size,
|
|
404
|
+
size_t initial_thread_count,
|
|
405
|
+
void* thread_finalize_data,
|
|
406
|
+
napi_finalize thread_finalize_cb,
|
|
407
|
+
void* context,
|
|
408
|
+
napi_threadsafe_function_call_js call_js_cb,
|
|
409
|
+
napi_threadsafe_function* result) {
|
|
410
|
+
#if EMNAPI_HAVE_THREADS
|
|
411
|
+
CHECK_ENV(env);
|
|
412
|
+
// CHECK_ARG(env, async_resource_name);
|
|
413
|
+
RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg);
|
|
414
|
+
CHECK_ARG(env, result);
|
|
415
|
+
|
|
416
|
+
napi_status status = napi_ok;
|
|
417
|
+
napi_ref ref = NULL;
|
|
418
|
+
|
|
419
|
+
if (func == NULL) {
|
|
420
|
+
CHECK_ARG(env, call_js_cb);
|
|
421
|
+
} else {
|
|
422
|
+
napi_valuetype type;
|
|
423
|
+
status = napi_typeof(env, func, &type);
|
|
424
|
+
if (status != napi_ok) return napi_set_last_error(env, status, 0, NULL);
|
|
425
|
+
if (type != napi_function) {
|
|
426
|
+
return napi_set_last_error(env, napi_invalid_arg, 0, NULL);
|
|
427
|
+
}
|
|
428
|
+
status = napi_create_reference(env, func, 1, &ref);
|
|
429
|
+
if (status != napi_ok) return napi_set_last_error(env, status, 0, NULL);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
napi_value resource;
|
|
433
|
+
napi_value resource_name;
|
|
434
|
+
if (async_resource != NULL) {
|
|
435
|
+
status = napi_coerce_to_object(env, async_resource, &resource);
|
|
436
|
+
if (status != napi_ok) return status;
|
|
437
|
+
} else {
|
|
438
|
+
status = napi_create_object(env, &resource);
|
|
439
|
+
if (status != napi_ok) return status;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
CHECK_ARG(env, async_resource_name);
|
|
443
|
+
status = napi_coerce_to_string(env, async_resource_name, &resource_name);
|
|
444
|
+
if (status != napi_ok) return status;
|
|
445
|
+
|
|
446
|
+
napi_threadsafe_function ts_fn = _emnapi_tsfn_create(
|
|
447
|
+
env,
|
|
448
|
+
ref,
|
|
449
|
+
resource,
|
|
450
|
+
resource_name,
|
|
451
|
+
max_queue_size,
|
|
452
|
+
initial_thread_count,
|
|
453
|
+
thread_finalize_data,
|
|
454
|
+
thread_finalize_cb,
|
|
455
|
+
context,
|
|
456
|
+
call_js_cb != NULL ? call_js_cb : _emnapi_tsfn_default_call_js);
|
|
457
|
+
|
|
458
|
+
if (ts_fn == NULL) {
|
|
459
|
+
status = napi_generic_failure;
|
|
460
|
+
} else {
|
|
461
|
+
// Init deletes ts_fn upon failure.
|
|
462
|
+
status = _emnapi_tsfn_init(ts_fn);
|
|
463
|
+
if (status == napi_ok) {
|
|
464
|
+
*result = ts_fn;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return napi_set_last_error(env, status, 0, NULL);
|
|
469
|
+
#else
|
|
470
|
+
return napi_set_last_error(env, napi_generic_failure, 0, NULL);
|
|
471
|
+
#endif
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
napi_status
|
|
475
|
+
napi_get_threadsafe_function_context(napi_threadsafe_function func,
|
|
476
|
+
void** result) {
|
|
477
|
+
#if EMNAPI_HAVE_THREADS
|
|
478
|
+
CHECK_NOT_NULL(func);
|
|
479
|
+
CHECK_NOT_NULL(result);
|
|
480
|
+
|
|
481
|
+
*result = func->context;
|
|
482
|
+
|
|
483
|
+
return napi_ok;
|
|
484
|
+
#else
|
|
485
|
+
return napi_generic_failure;
|
|
486
|
+
#endif
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
napi_status
|
|
490
|
+
napi_call_threadsafe_function(napi_threadsafe_function func,
|
|
491
|
+
void* data,
|
|
492
|
+
napi_threadsafe_function_call_mode mode) {
|
|
493
|
+
#if EMNAPI_HAVE_THREADS
|
|
494
|
+
CHECK_NOT_NULL(func);
|
|
495
|
+
pthread_mutex_lock(&func->mutex);
|
|
496
|
+
|
|
497
|
+
while (func->queue_size >= func->max_queue_size &&
|
|
498
|
+
func->max_queue_size > 0 &&
|
|
499
|
+
!func->is_closing) {
|
|
500
|
+
if (mode == napi_tsfn_nonblocking) {
|
|
501
|
+
pthread_mutex_unlock(&func->mutex);
|
|
502
|
+
return napi_queue_full;
|
|
503
|
+
}
|
|
504
|
+
pthread_cond_wait(func->cond, &func->mutex);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (func->is_closing) {
|
|
508
|
+
if (func->thread_count == 0) {
|
|
509
|
+
pthread_mutex_unlock(&func->mutex);
|
|
510
|
+
return napi_invalid_arg;
|
|
511
|
+
} else {
|
|
512
|
+
func->thread_count--;
|
|
513
|
+
pthread_mutex_unlock(&func->mutex);
|
|
514
|
+
return napi_closing;
|
|
515
|
+
}
|
|
516
|
+
} else {
|
|
517
|
+
struct data_queue_node* queue_node = (struct data_queue_node*) malloc(sizeof(struct data_queue_node));
|
|
518
|
+
if (queue_node == NULL) {
|
|
519
|
+
pthread_mutex_unlock(&func->mutex);
|
|
520
|
+
return napi_generic_failure;
|
|
521
|
+
}
|
|
522
|
+
queue_node->data = data;
|
|
523
|
+
QUEUE_INSERT_TAIL(&func->queue, &queue_node->q);
|
|
524
|
+
func->queue_size++;
|
|
525
|
+
_emnapi_tsfn_send(func);
|
|
526
|
+
pthread_mutex_unlock(&func->mutex);
|
|
527
|
+
return napi_ok;
|
|
528
|
+
}
|
|
529
|
+
#else
|
|
530
|
+
return napi_generic_failure;
|
|
531
|
+
#endif
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
napi_status
|
|
535
|
+
napi_acquire_threadsafe_function(napi_threadsafe_function func) {
|
|
536
|
+
#if EMNAPI_HAVE_THREADS
|
|
537
|
+
CHECK_NOT_NULL(func);
|
|
538
|
+
pthread_mutex_lock(&func->mutex);
|
|
539
|
+
|
|
540
|
+
if (func->is_closing) {
|
|
541
|
+
pthread_mutex_unlock(&func->mutex);
|
|
542
|
+
return napi_closing;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
func->thread_count++;
|
|
546
|
+
|
|
547
|
+
pthread_mutex_unlock(&func->mutex);
|
|
548
|
+
return napi_ok;
|
|
549
|
+
#else
|
|
550
|
+
return napi_generic_failure;
|
|
551
|
+
#endif
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
napi_status
|
|
555
|
+
napi_release_threadsafe_function(napi_threadsafe_function func,
|
|
556
|
+
napi_threadsafe_function_release_mode mode) {
|
|
557
|
+
#if EMNAPI_HAVE_THREADS
|
|
558
|
+
CHECK_NOT_NULL(func);
|
|
559
|
+
pthread_mutex_lock(&func->mutex);
|
|
560
|
+
|
|
561
|
+
if (func->thread_count == 0) {
|
|
562
|
+
pthread_mutex_unlock(&func->mutex);
|
|
563
|
+
return napi_invalid_arg;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
func->thread_count--;
|
|
567
|
+
|
|
568
|
+
if (func->thread_count == 0 || mode == napi_tsfn_abort) {
|
|
569
|
+
if (!func->is_closing) {
|
|
570
|
+
func->is_closing = (mode == napi_tsfn_abort);
|
|
571
|
+
if (func->is_closing && func->max_queue_size > 0) {
|
|
572
|
+
pthread_cond_signal(func->cond);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
_emnapi_tsfn_send(func);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
pthread_mutex_unlock(&func->mutex);
|
|
580
|
+
|
|
581
|
+
return napi_ok;
|
|
582
|
+
#else
|
|
583
|
+
return napi_generic_failure;
|
|
584
|
+
#endif
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
napi_status
|
|
588
|
+
napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
|
|
589
|
+
#if EMNAPI_HAVE_THREADS
|
|
590
|
+
if (func->async_ref) {
|
|
591
|
+
EMNAPI_KEEPALIVE_POP();
|
|
592
|
+
_emnapi_ctx_decrease_waiting_request_counter();
|
|
593
|
+
func->async_ref = false;
|
|
594
|
+
}
|
|
595
|
+
return napi_ok;
|
|
596
|
+
#else
|
|
597
|
+
return napi_generic_failure;
|
|
598
|
+
#endif
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
napi_status
|
|
602
|
+
napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
|
|
603
|
+
#if EMNAPI_HAVE_THREADS
|
|
604
|
+
if (!func->async_ref) {
|
|
605
|
+
EMNAPI_KEEPALIVE_PUSH();
|
|
606
|
+
_emnapi_ctx_increase_waiting_request_counter();
|
|
607
|
+
func->async_ref = true;
|
|
608
|
+
}
|
|
609
|
+
return napi_ok;
|
|
610
|
+
#else
|
|
611
|
+
return napi_generic_failure;
|
|
612
|
+
#endif
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
EXTERN_C_END
|
|
616
|
+
|
|
617
|
+
#endif
|