emnapi 0.31.0 → 0.33.0

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.
@@ -0,0 +1,617 @@
1
+ #include "node_api.h"
2
+ #include "emnapi_internal.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
@@ -27,7 +27,11 @@
27
27
  #include <errno.h>
28
28
  #include <string.h>
29
29
  #include "uv-common.h"
30
- #include "common.h"
30
+ #include "emnapi_common.h"
31
+
32
+ #if defined(__wasi__) && defined(_REENTRANT)
33
+ #define __EMNAPI_WASI_THREADS__
34
+ #endif
31
35
 
32
36
  #define MAX_THREADPOOL_SIZE 1024
33
37
 
@@ -54,6 +58,15 @@ static void uv__cancelled(struct uv__work* w) {
54
58
 
55
59
  EMNAPI_INTERNAL_EXTERN void _emnapi_worker_unref(uv_thread_t pid);
56
60
 
61
+ #ifdef __EMNAPI_WASI_THREADS__
62
+ EMNAPI_INTERNAL_EXTERN
63
+ void _emnapi_after_uvthreadpool_ready(void (*callback)(QUEUE* w, enum uv__work_kind kind),
64
+ QUEUE* w,
65
+ enum uv__work_kind kind);
66
+ EMNAPI_INTERNAL_EXTERN void _emnapi_tell_js_uvthreadpool(uv_thread_t* threads, unsigned int n);
67
+ EMNAPI_INTERNAL_EXTERN void _emnapi_emit_async_thread_ready();
68
+ #endif
69
+
57
70
  /* To avoid deadlock with uv_cancel() it's crucial that the worker
58
71
  * never holds the global mutex and the loop-local mutex at the same time.
59
72
  */
@@ -61,8 +74,11 @@ static void* worker(void* arg) {
61
74
  struct uv__work* w;
62
75
  QUEUE* q;
63
76
  int is_slow_work;
64
-
77
+ #ifndef __EMNAPI_WASI_THREADS__
65
78
  uv_sem_post((uv_sem_t*) arg);
79
+ #else
80
+ _emnapi_emit_async_thread_ready();
81
+ #endif
66
82
  arg = NULL;
67
83
 
68
84
  uv_mutex_lock(&mutex);
@@ -199,7 +215,9 @@ static void init_threads(void) {
199
215
  #if !defined(EMNAPI_WORKER_POOL_SIZE) || !(EMNAPI_WORKER_POOL_SIZE > 0)
200
216
  const char* val;
201
217
  #endif
218
+ #ifndef __EMNAPI_WASI_THREADS__
202
219
  uv_sem_t sem;
220
+ #endif
203
221
 
204
222
  #if defined(EMNAPI_WORKER_POOL_SIZE) && EMNAPI_WORKER_POOL_SIZE > 0
205
223
  nthreads = EMNAPI_WORKER_POOL_SIZE;
@@ -233,24 +251,33 @@ static void init_threads(void) {
233
251
  QUEUE_INIT(&slow_io_pending_wq);
234
252
  QUEUE_INIT(&run_slow_work_message);
235
253
 
254
+ #ifndef __EMNAPI_WASI_THREADS__
236
255
  if (uv_sem_init(&sem, 0))
237
256
  abort();
257
+ #endif
238
258
 
239
259
  for (i = 0; i < nthreads; i++)
260
+ #ifndef __EMNAPI_WASI_THREADS__
240
261
  if (uv_thread_create(threads + i, (uv_thread_cb) worker, &sem))
262
+ #else
263
+ if (uv_thread_create(threads + i, (uv_thread_cb) worker, NULL))
264
+ #endif
241
265
  abort();
242
266
 
267
+ #ifndef __EMNAPI_WASI_THREADS__
243
268
  for (i = 0; i < nthreads; i++)
244
269
  uv_sem_wait(&sem);
245
270
 
246
271
  uv_sem_destroy(&sem);
247
-
248
272
  for (i = 0; i < nthreads; i++)
249
273
  _emnapi_worker_unref(*(threads + i));
274
+ #else
275
+ _emnapi_tell_js_uvthreadpool(threads, nthreads);
276
+ #endif
250
277
  }
251
278
 
252
279
 
253
- #ifndef _WIN32
280
+ #if !defined(_WIN32) && !defined(__wasi__)
254
281
  static void reset_once(void) {
255
282
  uv_once_t child_once = UV_ONCE_INIT;
256
283
  memcpy(&once, &child_once, sizeof(child_once));
@@ -259,7 +286,7 @@ static void reset_once(void) {
259
286
 
260
287
 
261
288
  static void init_once(void) {
262
- #ifndef _WIN32
289
+ #if !defined(_WIN32) && !defined(__wasi__)
263
290
  /* Re-initialize the threadpool after fork.
264
291
  * Note that this discards the global mutex and condition as well
265
292
  * as the work queue.
@@ -280,7 +307,11 @@ void uv__work_submit(uv_loop_t* loop,
280
307
  w->loop = loop;
281
308
  w->work = work;
282
309
  w->done = done;
310
+ // #ifdef __EMNAPI_WASI_THREADS__
311
+ // _emnapi_after_uvthreadpool_ready(post, &w->wq, kind);
312
+ // #else
283
313
  post(&w->wq, kind);
314
+ // #endif
284
315
  }
285
316
 
286
317