Skip to content

Commit ec2a726

Browse files
committed
Revert "no more time threads"
This reverts commit b247994.
1 parent b247994 commit ec2a726

File tree

2 files changed

+42
-51
lines changed

2 files changed

+42
-51
lines changed

libs/core/runtime/jsruntime.rs

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -436,10 +436,8 @@ pub struct JsRuntimeState {
436436
pub(crate) function_templates: Rc<RefCell<FunctionTemplateData>>,
437437
pub(crate) callsite_prototype: RefCell<Option<v8::Global<v8::Object>>>,
438438
waker: Arc<AtomicWaker>,
439-
/// Level-triggered flag set by the custom V8 platform when a foreground
440-
/// task is posted. Checked and cleared each poll to ensure pumping even
441-
/// if the edge-triggered AtomicWaker notification was lost.
442-
has_foreground_task: Arc<std::sync::atomic::AtomicBool>,
439+
/// Guards the 100ms safety-net timer so at most one is active at a time.
440+
safety_net_active: Arc<std::sync::atomic::AtomicBool>,
443441
/// Accessed through [`JsRuntimeState::with_inspector`].
444442
inspector: RefCell<Option<Rc<JsRuntimeInspector>>>,
445443
has_inspector: Cell<bool>,
@@ -745,8 +743,6 @@ impl JsRuntime {
745743
// ...now let's set up ` JsRuntimeState`, we'll need to set some fields
746744
// later, after `JsRuntime` is all set up...
747745
let waker = op_state.waker.clone();
748-
let has_foreground_task =
749-
Arc::new(std::sync::atomic::AtomicBool::new(false));
750746
let op_state = Rc::new(RefCell::new(op_state));
751747
let (eval_context_get_code_cache_cb, eval_context_set_code_cache_cb) =
752748
options
@@ -769,7 +765,7 @@ impl JsRuntime {
769765
eval_context_set_code_cache_cb,
770766
),
771767
waker: waker.clone(),
772-
has_foreground_task: has_foreground_task.clone(),
768+
safety_net_active: Arc::new(std::sync::atomic::AtomicBool::new(false)),
773769
// Some fields are initialized later after isolate is created
774770
inspector: None.into(),
775771
has_inspector: false.into(),
@@ -851,7 +847,6 @@ impl JsRuntime {
851847
setup::register_isolate_waker(
852848
setup::isolate_ptr_to_key(isolate_ptr),
853849
waker.clone(),
854-
has_foreground_task,
855850
);
856851

857852
// ...isolate is fully set up, we can forward its pointer to the ops to finish
@@ -2113,14 +2108,7 @@ impl JsRuntime {
21132108
if has_inspector {
21142109
self.inspector().poll_sessions_from_event_loop(cx);
21152110
}
2116-
// Clear the foreground-task flag (set by the custom V8 platform callback).
2117-
// If it was set, we must pump the message loop regardless of other state.
2118-
let had_foreground_task = self
2119-
.inner
2120-
.state
2121-
.has_foreground_task
2122-
.swap(false, std::sync::atomic::Ordering::Relaxed);
2123-
if poll_options.pump_v8_message_loop || had_foreground_task {
2111+
if poll_options.pump_v8_message_loop {
21242112
self.pump_v8_message_loop(scope)?;
21252113
}
21262114

@@ -2277,6 +2265,27 @@ impl JsRuntime {
22772265
scope.perform_microtask_checkpoint();
22782266
}
22792267

2268+
// Safety net: if V8 has pending background tasks (e.g. module compilation),
2269+
// schedule a delayed wake to pump the message loop in case the platform
2270+
// callback was missed due to a race condition. Uses an OS thread (not
2271+
// tokio) to avoid depending on the async runtime being cooperative.
2272+
// The AtomicBool guard ensures at most one safety-net timer is active.
2273+
if pending_state.has_pending_background_tasks
2274+
&& !self
2275+
.inner
2276+
.state
2277+
.safety_net_active
2278+
.swap(true, std::sync::atomic::Ordering::Relaxed)
2279+
{
2280+
let waker = cx.waker().clone();
2281+
let flag = self.inner.state.safety_net_active.clone();
2282+
std::thread::spawn(move || {
2283+
std::thread::sleep(std::time::Duration::from_millis(100));
2284+
flag.store(false, std::sync::atomic::Ordering::Relaxed);
2285+
waker.wake_by_ref();
2286+
});
2287+
}
2288+
22802289
// Re-wake logic for next iteration
22812290
#[allow(clippy::suspicious_else_formatting, clippy::if_same_then_else)]
22822291
{

libs/core/runtime/setup.rs

Lines changed: 17 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -32,69 +32,51 @@ pub(crate) fn isolate_ptr_to_key(ptr: v8::UnsafeRawIsolatePtr) -> usize {
3232
unsafe { std::mem::transmute::<v8::UnsafeRawIsolatePtr, usize>(ptr) }
3333
}
3434

35-
/// Per-isolate waker state: an `AtomicWaker` for edge-triggered wakeups
36-
/// and an `AtomicBool` flag for level-triggered detection.
37-
///
38-
/// The `AtomicWaker` can lose wakes if `wake()` is called while the stored
39-
/// waker was already consumed (e.g. by the re-wake logic in the poll loop).
40-
/// The `AtomicBool` flag ensures the poll loop always sees that a foreground
41-
/// task was posted, even if the waker-based notification was lost.
42-
struct IsolateWakerEntry {
43-
waker: std::sync::Arc<AtomicWaker>,
44-
has_foreground_task: std::sync::Arc<AtomicBool>,
45-
}
46-
47-
/// Global registry mapping isolate pointers to their waker state.
35+
/// Global registry mapping isolate pointers to their event loop wakers.
36+
/// When V8 posts a foreground task for an isolate, the callback looks up
37+
/// the waker here and wakes the event loop. Isolates that received a
38+
/// notification before their waker was registered are tracked in
39+
/// `pending_wakes` so `register_isolate_waker` can wake them immediately.
4840
struct IsolateWakerRegistry {
49-
entries: HashMap<usize, IsolateWakerEntry>,
41+
wakers: HashMap<usize, std::sync::Arc<AtomicWaker>>,
5042
pending_wakes: std::collections::HashSet<usize>,
5143
}
5244

5345
static ISOLATE_WAKERS: std::sync::LazyLock<Mutex<IsolateWakerRegistry>> =
5446
std::sync::LazyLock::new(|| {
5547
Mutex::new(IsolateWakerRegistry {
56-
entries: HashMap::new(),
48+
wakers: HashMap::new(),
5749
pending_wakes: std::collections::HashSet::new(),
5850
})
5951
});
6052

61-
/// Register a waker and foreground-task flag for an isolate.
62-
/// If a notification arrived before registration, the flag is set
63-
/// and the waker is triggered immediately.
53+
/// Register a waker for an isolate so foreground task notifications
54+
/// wake the correct event loop. If a notification arrived before
55+
/// registration, the waker is triggered immediately.
6456
pub fn register_isolate_waker(
6557
isolate_ptr: usize,
6658
waker: std::sync::Arc<AtomicWaker>,
67-
has_foreground_task: std::sync::Arc<AtomicBool>,
6859
) {
6960
let mut reg = ISOLATE_WAKERS.lock().unwrap();
7061
if reg.pending_wakes.remove(&isolate_ptr) {
71-
has_foreground_task.store(true, Ordering::Relaxed);
7262
waker.wake();
7363
}
74-
reg.entries.insert(
75-
isolate_ptr,
76-
IsolateWakerEntry {
77-
waker,
78-
has_foreground_task,
79-
},
80-
);
64+
reg.wakers.insert(isolate_ptr, waker);
8165
}
8266

83-
/// Unregister an isolate's waker state (called on isolate drop).
67+
/// Unregister an isolate's waker (called on isolate drop).
8468
pub fn unregister_isolate_waker(isolate_ptr: usize) {
8569
let mut reg = ISOLATE_WAKERS.lock().unwrap();
86-
reg.entries.remove(&isolate_ptr);
70+
reg.wakers.remove(&isolate_ptr);
8771
reg.pending_wakes.remove(&isolate_ptr);
8872
}
8973

90-
/// Notify that a foreground task was posted for the given isolate.
91-
/// Sets the level-triggered flag and wakes the event loop.
92-
/// If the isolate's state is not yet registered, marks it as pending.
74+
/// Wake the event loop for a given isolate. If the isolate's waker
75+
/// is not yet registered, marks it as pending so registration wakes it.
9376
fn wake_isolate(key: usize) {
9477
let mut reg = ISOLATE_WAKERS.lock().unwrap();
95-
if let Some(entry) = reg.entries.get(&key) {
96-
entry.has_foreground_task.store(true, Ordering::Relaxed);
97-
entry.waker.wake();
78+
if let Some(waker) = reg.wakers.get(&key) {
79+
waker.wake();
9880
} else {
9981
reg.pending_wakes.insert(key);
10082
}

0 commit comments

Comments
 (0)