Skip to content

Commit bd9b6b1

Browse files
committed
fix: pump V8 message loop on background task completion to prevent worker stall
1 parent c1b4413 commit bd9b6b1

File tree

1 file changed

+35
-4
lines changed

1 file changed

+35
-4
lines changed

libs/core/runtime/jsruntime.rs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,12 @@ pub struct JsRuntimeState {
430430
pub(crate) function_templates: Rc<RefCell<FunctionTemplateData>>,
431431
pub(crate) callsite_prototype: RefCell<Option<v8::Global<v8::Object>>>,
432432
waker: Arc<AtomicWaker>,
433+
/// Tracks whether V8 had pending background tasks on the previous
434+
/// event loop iteration. Used to detect the transition from
435+
/// has_pending_background_tasks=true to false, which is when a
436+
/// background compilation may have just posted a foreground callback
437+
/// that needs an extra pump_message_loop call to process.
438+
had_pending_background_tasks: Cell<bool>,
433439
/// Accessed through [`JsRuntimeState::with_inspector`].
434440
inspector: RefCell<Option<Rc<JsRuntimeInspector>>>,
435441
has_inspector: Cell<bool>,
@@ -757,6 +763,7 @@ impl JsRuntime {
757763
eval_context_set_code_cache_cb,
758764
),
759765
waker,
766+
had_pending_background_tasks: false.into(),
760767
// Some fields are initialized later after isolate is created
761768
inspector: None.into(),
762769
has_inspector: false.into(),
@@ -1786,23 +1793,27 @@ impl JsRuntime {
17861793
}
17871794
}
17881795

1796+
/// Pump V8's foreground message loop, processing tasks posted by
1797+
/// background threads (e.g. module compilation callbacks).
1798+
/// Returns `true` if any tasks were processed.
17891799
fn pump_v8_message_loop(
17901800
&self,
17911801
scope: &mut v8::PinScope,
1792-
) -> Result<(), Box<JsError>> {
1802+
) -> Result<bool, Box<JsError>> {
1803+
let mut did_work = false;
17931804
while v8::Platform::pump_message_loop(
17941805
&v8::V8::get_current_platform(),
17951806
scope,
17961807
false, // don't block if there are no tasks
17971808
) {
1798-
// do nothing
1809+
did_work = true;
17991810
}
18001811

18011812
v8::tc_scope!(let tc_scope, scope);
18021813

18031814
tc_scope.perform_microtask_checkpoint();
18041815
match tc_scope.exception() {
1805-
None => Ok(()),
1816+
None => Ok(did_work),
18061817
Some(exception) => {
18071818
exception_to_err_result(tc_scope, exception, false, true)
18081819
}
@@ -2015,8 +2026,9 @@ impl JsRuntime {
20152026
if has_inspector {
20162027
self.inspector().poll_sessions_from_event_loop(cx);
20172028
}
2029+
let mut v8_tasks_processed = false;
20182030
if poll_options.pump_v8_message_loop {
2019-
self.pump_v8_message_loop(scope)?;
2031+
v8_tasks_processed = self.pump_v8_message_loop(scope)?;
20202032
}
20212033

20222034
let realm = &self.inner.main_realm;
@@ -2127,6 +2139,24 @@ impl JsRuntime {
21272139
let pending_state =
21282140
EventLoopPendingState::new(scope, context_state, modules);
21292141

2142+
// Second V8 message pump: only needed on the transition from
2143+
// had_pending_background_tasks=true to false. This is the exact
2144+
// moment a background compilation thread may have just posted a
2145+
// foreground callback that the first pump (pre-phase) missed.
2146+
// Avoids pumping on every iteration during module loading.
2147+
let had_bg_tasks = self.inner.state.had_pending_background_tasks.get();
2148+
self
2149+
.inner
2150+
.state
2151+
.had_pending_background_tasks
2152+
.set(pending_state.has_pending_background_tasks);
2153+
if poll_options.pump_v8_message_loop
2154+
&& had_bg_tasks
2155+
&& !pending_state.has_pending_background_tasks
2156+
{
2157+
v8_tasks_processed |= self.pump_v8_message_loop(scope)?;
2158+
}
2159+
21302160
if !pending_state.is_pending() {
21312161
if has_inspector {
21322162
let inspector = self.inspector();
@@ -2164,6 +2194,7 @@ impl JsRuntime {
21642194
|| pending_state.has_refed_immediates > 0
21652195
|| pending_state.has_pending_promise_events
21662196
|| uv_did_io
2197+
|| v8_tasks_processed
21672198
{
21682199
self.inner.state.waker.wake();
21692200
} else

0 commit comments

Comments
 (0)