12
12
#include " tracing/traced_value.h"
13
13
#include " util-inl.h"
14
14
15
+ #include < atomic>
15
16
#include < memory>
16
17
17
18
struct node_napi_env__ : public napi_env__ {
@@ -136,6 +137,7 @@ class ThreadSafeFunction : public node::AsyncResource {
136
137
*v8::String::Utf8Value (env_->isolate, name)),
137
138
thread_count(thread_count_),
138
139
is_closing(false ),
140
+ dispatch_state(kDispatchIdle ),
139
141
context(context_),
140
142
max_queue_size(max_queue_size_),
141
143
env(env_),
@@ -175,10 +177,8 @@ class ThreadSafeFunction : public node::AsyncResource {
175
177
return napi_closing;
176
178
}
177
179
} else {
178
- if (uv_async_send (&async) != 0 ) {
179
- return napi_generic_failure;
180
- }
181
180
queue.push (data);
181
+ Send ();
182
182
return napi_ok;
183
183
}
184
184
}
@@ -210,9 +210,7 @@ class ThreadSafeFunction : public node::AsyncResource {
210
210
if (is_closing && max_queue_size > 0 ) {
211
211
cond->Signal (lock);
212
212
}
213
- if (uv_async_send (&async) != 0 ) {
214
- return napi_generic_failure;
215
- }
213
+ Send ();
216
214
}
217
215
}
218
216
@@ -237,7 +235,6 @@ class ThreadSafeFunction : public node::AsyncResource {
237
235
cond = std::make_unique<node::ConditionVariable>();
238
236
}
239
237
if (max_queue_size == 0 || cond) {
240
- CHECK_EQ (0 , uv_idle_init (loop, &idle));
241
238
return napi_ok;
242
239
}
243
240
@@ -262,21 +259,46 @@ class ThreadSafeFunction : public node::AsyncResource {
262
259
263
260
napi_status Unref () {
264
261
uv_unref (reinterpret_cast <uv_handle_t *>(&async));
265
- uv_unref (reinterpret_cast <uv_handle_t *>(&idle));
266
262
267
263
return napi_ok;
268
264
}
269
265
270
266
napi_status Ref () {
271
267
uv_ref (reinterpret_cast <uv_handle_t *>(&async));
272
- uv_ref (reinterpret_cast <uv_handle_t *>(&idle));
273
268
274
269
return napi_ok;
275
270
}
276
271
277
- void DispatchOne () {
272
+ inline void * Context () {
273
+ return context;
274
+ }
275
+
276
+ protected:
277
+ void Dispatch () {
278
+ bool has_more = true ;
279
+
280
+ // Limit maximum synchronous iteration count to prevent event loop
281
+ // starvation. See `src/node_messaging.cc` for an inspiration.
282
+ unsigned int iterations_left = kMaxIterationCount ;
283
+ while (has_more && --iterations_left != 0 ) {
284
+ dispatch_state = kDispatchRunning ;
285
+ has_more = DispatchOne ();
286
+
287
+ // Send() was called while we were executing the JS function
288
+ if (dispatch_state.exchange (kDispatchIdle ) != kDispatchRunning ) {
289
+ has_more = true ;
290
+ }
291
+ }
292
+
293
+ if (has_more) {
294
+ Send ();
295
+ }
296
+ }
297
+
298
+ bool DispatchOne () {
278
299
void * data = nullptr ;
279
300
bool popped_value = false ;
301
+ bool has_more = false ;
280
302
281
303
{
282
304
node::Mutex::ScopedLock lock (this ->mutex );
@@ -301,9 +323,9 @@ class ThreadSafeFunction : public node::AsyncResource {
301
323
cond->Signal (lock);
302
324
}
303
325
CloseHandlesAndMaybeDelete ();
304
- } else {
305
- CHECK_EQ (0 , uv_idle_stop (&idle));
306
326
}
327
+ } else {
328
+ has_more = true ;
307
329
}
308
330
}
309
331
}
@@ -321,6 +343,8 @@ class ThreadSafeFunction : public node::AsyncResource {
321
343
call_js_cb (env, js_callback, context, data);
322
344
});
323
345
}
346
+
347
+ return has_more;
324
348
}
325
349
326
350
void Finalize () {
@@ -334,10 +358,6 @@ class ThreadSafeFunction : public node::AsyncResource {
334
358
EmptyQueueAndDelete ();
335
359
}
336
360
337
- inline void * Context () {
338
- return context;
339
- }
340
-
341
361
void CloseHandlesAndMaybeDelete (bool set_closing = false ) {
342
362
v8::HandleScope scope (env->isolate );
343
363
if (set_closing) {
@@ -357,18 +377,20 @@ class ThreadSafeFunction : public node::AsyncResource {
357
377
ThreadSafeFunction* ts_fn =
358
378
node::ContainerOf (&ThreadSafeFunction::async,
359
379
reinterpret_cast <uv_async_t *>(handle));
360
- v8::HandleScope scope (ts_fn->env ->isolate );
361
- ts_fn->env ->node_env ()->CloseHandle (
362
- reinterpret_cast <uv_handle_t *>(&ts_fn->idle ),
363
- [](uv_handle_t * handle) -> void {
364
- ThreadSafeFunction* ts_fn =
365
- node::ContainerOf (&ThreadSafeFunction::idle,
366
- reinterpret_cast <uv_idle_t *>(handle));
367
- ts_fn->Finalize ();
368
- });
380
+ ts_fn->Finalize ();
369
381
});
370
382
}
371
383
384
+ void Send () {
385
+ // Ask currently running Dispatch() to make one more iteration
386
+ unsigned char current_state = dispatch_state.fetch_or (kDispatchPending );
387
+ if ((current_state & kDispatchRunning ) == kDispatchRunning ) {
388
+ return ;
389
+ }
390
+
391
+ CHECK_EQ (0 , uv_async_send (&async));
392
+ }
393
+
372
394
// Default way of calling into JavaScript. Used when ThreadSafeFunction is
373
395
// without a call_js_cb_.
374
396
static void CallJs (napi_env env, napi_value cb, void * context, void * data) {
@@ -392,16 +414,10 @@ class ThreadSafeFunction : public node::AsyncResource {
392
414
}
393
415
}
394
416
395
- static void IdleCb (uv_idle_t * idle) {
396
- ThreadSafeFunction* ts_fn =
397
- node::ContainerOf (&ThreadSafeFunction::idle, idle);
398
- ts_fn->DispatchOne ();
399
- }
400
-
401
417
static void AsyncCb (uv_async_t * async) {
402
418
ThreadSafeFunction* ts_fn =
403
419
node::ContainerOf (&ThreadSafeFunction::async, async);
404
- CHECK_EQ ( 0 , uv_idle_start (& ts_fn->idle , IdleCb) );
420
+ ts_fn->Dispatch ( );
405
421
}
406
422
407
423
static void Cleanup (void * data) {
@@ -410,14 +426,20 @@ class ThreadSafeFunction : public node::AsyncResource {
410
426
}
411
427
412
428
private:
429
+ static const unsigned char kDispatchIdle = 0 ;
430
+ static const unsigned char kDispatchRunning = 1 << 0 ;
431
+ static const unsigned char kDispatchPending = 1 << 1 ;
432
+
433
+ static const unsigned int kMaxIterationCount = 1000 ;
434
+
413
435
// These are variables protected by the mutex.
414
436
node::Mutex mutex;
415
437
std::unique_ptr<node::ConditionVariable> cond;
416
438
std::queue<void *> queue;
417
439
uv_async_t async;
418
- uv_idle_t idle;
419
440
size_t thread_count;
420
441
bool is_closing;
442
+ std::atomic_uchar dispatch_state;
421
443
422
444
// These are variables set once, upon creation, and then never again, which
423
445
// means we don't need the mutex to read them.
0 commit comments