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.
@@ -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