From 189c25283ee16a2cdbee99733075a3a82c6fbace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 6 Aug 2025 09:40:14 +0200 Subject: [PATCH 1/2] feat: support immediates --- core/01_core.js | 16 ++++++++++++++-- core/ops_builtin.rs | 2 +- core/ops_builtin_v8.rs | 19 +++++++------------ core/runtime/jsrealm.rs | 2 ++ core/runtime/jsruntime.rs | 8 ++++++++ 5 files changed, 32 insertions(+), 15 deletions(-) diff --git a/core/01_core.js b/core/01_core.js index 3993010d1..3784ab715 100755 --- a/core/01_core.js +++ b/core/01_core.js @@ -67,13 +67,13 @@ op_add_main_module_handler, op_set_handled_promise_rejection_handler, op_set_has_tick_scheduled, + op_set_has_immediate_scheduled, op_set_promise_hooks, op_set_wasm_streaming_callback, op_str_byte_length, op_timer_cancel, op_timer_queue, op_timer_queue_system, - op_timer_queue_immediate, op_timer_ref, op_timer_unref, op_unref_op, @@ -148,6 +148,7 @@ const macrotaskCallbacks = []; const nextTickCallbacks = []; + const immediateCallbacks = []; function setMacrotaskCallback(cb) { ArrayPrototypePush(macrotaskCallbacks, cb); @@ -157,6 +158,10 @@ ArrayPrototypePush(nextTickCallbacks, cb); } + function setImmediateCallback(cb) { + ArrayPrototypePush(immediateCallbacks, cb); + } + // This function has variable number of arguments. The last argument describes // if there's a "next tick" scheduled by the Node.js compat layer. Arguments // before last are alternating integers and any values that describe the @@ -179,6 +184,12 @@ op_run_microtasks(); } + // Drain immediates queue. + // TODO: might do it recursively + for (let i = 0; i < immediateCallbacks.length; i++) { + immediateCallbacks[i](); + } + // Finally drain macrotask queue. for (let i = 0; i < macrotaskCallbacks.length; i++) { const cb = macrotaskCallbacks[i]; @@ -690,9 +701,11 @@ op_leak_tracing_get(0, promise[promiseIdSymbol]), setMacrotaskCallback, setNextTickCallback, + setImmediateCallback, runMicrotasks: () => op_run_microtasks(), hasTickScheduled: () => op_has_tick_scheduled(), setHasTickScheduled: (bool) => op_set_has_tick_scheduled(bool), + setHasImmediateScheduled: (bool) => op_set_has_immediate_scheduled(bool), evalContext: ( source, specifier, @@ -799,7 +812,6 @@ // TODO(mmastrac): Hook up associatedOp to tracing queueSystemTimer: (_associatedOp, repeat, timeout, task) => op_timer_queue_system(repeat, timeout, task), - queueImmediate: (task) => op_timer_queue_immediate(task), cancelTimer: (id) => { if (timersRunning) { cancelledTimers.add(id); diff --git a/core/ops_builtin.rs b/core/ops_builtin.rs index 4ed29de65..684032d36 100644 --- a/core/ops_builtin.rs +++ b/core/ops_builtin.rs @@ -101,7 +101,6 @@ builtin_ops! { ops_builtin_v8::op_set_handled_promise_rejection_handler, ops_builtin_v8::op_timer_queue, ops_builtin_v8::op_timer_queue_system, - ops_builtin_v8::op_timer_queue_immediate, ops_builtin_v8::op_timer_cancel, ops_builtin_v8::op_timer_ref, ops_builtin_v8::op_timer_unref, @@ -111,6 +110,7 @@ builtin_ops! { ops_builtin_v8::op_run_microtasks, ops_builtin_v8::op_has_tick_scheduled, ops_builtin_v8::op_set_has_tick_scheduled, + ops_builtin_v8::op_set_has_immediate_scheduled, ops_builtin_v8::op_eval_context, ops_builtin_v8::op_queue_microtask, ops_builtin_v8::op_encode, diff --git a/core/ops_builtin_v8.rs b/core/ops_builtin_v8.rs index 4d07db3fe..4573bd9aa 100644 --- a/core/ops_builtin_v8.rs +++ b/core/ops_builtin_v8.rs @@ -157,18 +157,6 @@ pub fn op_timer_queue_system( .queue_system_timer(repeat, timeout_ms as _, (task, 0)) as _ } -/// Queue a timer. We return a "large integer" timer ID in an f64 which allows for up -/// to `MAX_SAFE_INTEGER` (2^53) timers to exist, versus 2^32 timers if we used -/// `u32`. -#[op2] -pub fn op_timer_queue_immediate( - scope: &mut v8::HandleScope, - #[global] task: v8::Global, -) -> f64 { - let context_state = JsRealm::state_from_scope(scope); - context_state.timers.queue_timer(0, (task, 0)) as _ -} - #[op2(fast)] pub fn op_timer_cancel(scope: &mut v8::HandleScope, id: f64) { let context_state = JsRealm::state_from_scope(scope); @@ -240,6 +228,13 @@ pub fn op_set_has_tick_scheduled(scope: &mut v8::HandleScope, v: bool) { .set(v); } +#[op2(fast)] +pub fn op_set_has_immediate_scheduled(scope: &mut v8::HandleScope, v: bool) { + JsRealm::state_from_scope(scope) + .has_immediate_scheduled + .set(v); +} + pub struct EvalContextError<'s> { thrown: v8::Local<'s, v8::Value>, is_native_error: bool, diff --git a/core/runtime/jsrealm.rs b/core/runtime/jsrealm.rs index 830834e02..a07fe90b9 100644 --- a/core/runtime/jsrealm.rs +++ b/core/runtime/jsrealm.rs @@ -79,6 +79,7 @@ pub struct ContextState { pub(crate) isolate: Option<*mut v8::Isolate>, pub(crate) exception_state: Rc, pub(crate) has_next_tick_scheduled: Cell, + pub(crate) has_immediate_scheduled: Cell, pub(crate) external_ops_tracker: ExternalOpsTracker, pub(crate) ext_import_meta_proto: RefCell>>, } @@ -97,6 +98,7 @@ impl ContextState { isolate: Some(isolate_ptr), exception_state: Default::default(), has_next_tick_scheduled: Default::default(), + has_immediate_scheduled: Default::default(), js_event_loop_tick_cb: Default::default(), js_wasm_streaming_cb: Default::default(), wasm_instance_fn: Default::default(), diff --git a/core/runtime/jsruntime.rs b/core/runtime/jsruntime.rs index 9ecf54d95..ddab1ad2f 100644 --- a/core/runtime/jsruntime.rs +++ b/core/runtime/jsruntime.rs @@ -2074,6 +2074,7 @@ impl JsRuntime { { if pending_state.has_pending_background_tasks || pending_state.has_tick_scheduled + || pending_state.has_immediate_scheduled || pending_state.has_pending_promise_events { self.inner.state.waker.wake(); @@ -2094,6 +2095,7 @@ impl JsRuntime { || pending_state.has_pending_background_tasks || pending_state.has_pending_external_ops || pending_state.has_tick_scheduled + || pending_state.has_immediate_scheduled { // pass, will be polled again } else { @@ -2112,6 +2114,7 @@ impl JsRuntime { || pending_state.has_pending_background_tasks || pending_state.has_pending_external_ops || pending_state.has_tick_scheduled + || pending_state.has_immediate_scheduled { // pass, will be polled again } else if realm.modules_idle() { @@ -2331,6 +2334,7 @@ pub(crate) struct EventLoopPendingState { has_pending_module_evaluation: bool, has_pending_background_tasks: bool, has_tick_scheduled: bool, + has_immediate_scheduled: bool, has_pending_promise_events: bool, has_pending_external_ops: bool, } @@ -2374,6 +2378,7 @@ impl EventLoopPendingState { has_pending_module_evaluation, has_pending_background_tasks: scope.has_pending_background_tasks(), has_tick_scheduled: state.has_next_tick_scheduled.get(), + has_immediate_scheduled: state.has_immediate_scheduled.get(), has_pending_promise_events, has_pending_external_ops: state.external_ops_tracker.has_pending_ops(), } @@ -2393,6 +2398,7 @@ impl EventLoopPendingState { || self.has_pending_module_evaluation || self.has_pending_background_tasks || self.has_tick_scheduled + || self.has_immediate_scheduled || self.has_pending_promise_events || self.has_pending_external_ops } @@ -2683,7 +2689,9 @@ impl JsRuntime { let undefined: v8::Local = v8::undefined(scope).into(); let has_tick_scheduled = context_state.has_next_tick_scheduled.get(); + let has_immediate_scheduled = context_state.has_immediate_scheduled.get(); dispatched_ops |= has_tick_scheduled; + dispatched_ops |= has_immediate_scheduled; while let Some((promise, result)) = exception_state .pending_handled_promise_rejections From 87682886d23892e611c084e275870f5e1d2f7dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Thu, 7 Aug 2025 13:46:49 +0200 Subject: [PATCH 2/2] keep debugging --- core/01_core.js | 19 +++++++++++++++++-- core/ops_builtin.rs | 1 + core/ops_builtin_v8.rs | 7 +++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/core/01_core.js b/core/01_core.js index 3784ab715..6817aa5bf 100755 --- a/core/01_core.js +++ b/core/01_core.js @@ -55,6 +55,7 @@ op_get_proxy_details, op_get_ext_import_meta_proto, op_has_tick_scheduled, + op_has_immediate_scheduled, op_lazy_load_esm, op_memory_usage, op_op_names, @@ -186,8 +187,21 @@ // Drain immediates queue. // TODO: might do it recursively - for (let i = 0; i < immediateCallbacks.length; i++) { - immediateCallbacks[i](); + if (op_has_immediate_scheduled()) { + for (let i = 0; i < immediateCallbacks.length; i++) { + inner: while (true) { + console.log("tick in immediateCallbacks"); + try { + immediateCallbacks[i](); + break inner; + } catch (e) { + console.log("run reportExceptionCallback"); + reportExceptionCallback(e); + console.log("continue immediateCallbacks"); + continue inner; + } + } + } } // Finally drain macrotask queue. @@ -705,6 +719,7 @@ runMicrotasks: () => op_run_microtasks(), hasTickScheduled: () => op_has_tick_scheduled(), setHasTickScheduled: (bool) => op_set_has_tick_scheduled(bool), + hasImmediateScheduled: () => op_has_immediate_scheduled(), setHasImmediateScheduled: (bool) => op_set_has_immediate_scheduled(bool), evalContext: ( source, diff --git a/core/ops_builtin.rs b/core/ops_builtin.rs index 684032d36..ce5e464e5 100644 --- a/core/ops_builtin.rs +++ b/core/ops_builtin.rs @@ -110,6 +110,7 @@ builtin_ops! { ops_builtin_v8::op_run_microtasks, ops_builtin_v8::op_has_tick_scheduled, ops_builtin_v8::op_set_has_tick_scheduled, + ops_builtin_v8::op_has_immediate_scheduled, ops_builtin_v8::op_set_has_immediate_scheduled, ops_builtin_v8::op_eval_context, ops_builtin_v8::op_queue_microtask, diff --git a/core/ops_builtin_v8.rs b/core/ops_builtin_v8.rs index 4573bd9aa..92aa35842 100644 --- a/core/ops_builtin_v8.rs +++ b/core/ops_builtin_v8.rs @@ -228,6 +228,13 @@ pub fn op_set_has_tick_scheduled(scope: &mut v8::HandleScope, v: bool) { .set(v); } +#[op2(fast)] +pub fn op_has_immediate_scheduled(scope: &mut v8::HandleScope) -> bool { + JsRealm::state_from_scope(scope) + .has_immediate_scheduled + .get() +} + #[op2(fast)] pub fn op_set_has_immediate_scheduled(scope: &mut v8::HandleScope, v: bool) { JsRealm::state_from_scope(scope)