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.
- package/CMakeLists.txt +23 -2
- package/README.md +213 -94
- package/dist/library_napi.js +258 -33
- package/include/emnapi.h +1 -1
- package/include/js_native_api.h +1 -1
- 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_internal.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
- package/src/uv/threadpool.c +36 -5
- package/src/uv/unix/async.c +1 -1
- package/src/uv/unix/thread.c +10 -1
- /package/include/{common.h → emnapi_common.h} +0 -0
|
@@ -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
|
package/src/uv/threadpool.c
CHANGED
|
@@ -27,7 +27,11 @@
|
|
|
27
27
|
#include <errno.h>
|
|
28
28
|
#include <string.h>
|
|
29
29
|
#include "uv-common.h"
|
|
30
|
-
#include "
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
|