You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// When a sync helper called from this coro hit `receive()`, the None placeholder lives in the innermost suspended frame's stack — not the outer's. Pick that frame's buffer when present.
236
+
let target_stack = ifletSome(frame) = sub_frames.last_mut(){&mut frame.stack_delta}else{ saved_stack };
Copy file name to clipboardExpand all lines: compiler/src/modules/vm/builtins/async_ops.rs
+77-30Lines changed: 77 additions & 30 deletions
Original file line number
Diff line number
Diff line change
@@ -5,41 +5,88 @@ use super::super::types::*;
5
5
6
6
impl<'a>VM<'a>{
7
7
8
-
/* Resume a coroutine. On yield: persist ip/slots/stack/iters back into it. On return: restore caller stack/iter state. */
8
+
// Resume coroutine: persist state on yield, restore caller on return. Suspended sync sub-frames run innermost-first, each pushing its result onto the next frame's stack at the Call site.
// Walk frames inside-out, then the outer. `outer_ran` tracks whether `outer_ip` should be overwritten by `resume_ip` on save — a re-yield inside a sync frame leaves the outer pristine.
// Event yields keep the None placeholder; `run_push_event` overwrites it before resume.
172
+
// Event yields keep the None placeholder (overwritten by `run_push_event` before resume). Sync sub-call yields pushed nothing — the helper's return lands on the stack when its frame completes — so don't pop and don't skip the next PopTop.
173
173
let event_yield = self.pending.event_wait_request;
174
-
let val = if event_yield {Val::none()}else{self.pop().unwrap_or(Val::none())};
175
-
self.resume_ip = if !event_yield && ip < n && matches!(insns.get(ip),Some(ins)if ins.opcode == OpCode::PopTop){ ip + 1}else{ ip };
174
+
let sub_call_yield = !self.pending_sync_frames.is_empty();
175
+
let val = if event_yield || sub_call_yield {Val::none()}else{self.pop().unwrap_or(Val::none())};
176
+
self.resume_ip = if !event_yield && !sub_call_yield && ip < n && matches!(insns.get(ip),Some(ins)if ins.opcode == OpCode::PopTop){ ip + 1}else{ ip };
// Sync helper suspended mid-execution (e.g. `sleep(0)` from inside a sync fn called by an async coro). Stage its frame on the VM-level buffer; `resume_coroutine` drains it onto the enclosing coro so the helper is re-entered from the right ip. Without this, the outer's resume_ip would skip past the unfinished helper and the next StoreName would underflow. A nested sync call inside this helper would already have pushed its own frame first, so the buffer ends up innermost-last.
168
+
let helper_resume_ip = self.resume_ip;
169
+
self.resume_ip = 0;
170
+
let helper_stack_delta = ifself.stack.len() > stack_base {self.stack.split_off(stack_base)}else{Vec::new()};
171
+
let helper_iter_delta:Vec<IterFrame> = ifself.iter_stack.len() > iter_base {self.iter_stack.drain(iter_base..).collect()}else{Vec::new()};
Copy file name to clipboardExpand all lines: compiler/src/modules/vm/mod.rs
+3Lines changed: 3 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -115,6 +115,8 @@ pub struct VM<'a> {
115
115
pub(crate)sandbox_off:bool,
116
116
pub(crate)with_stack:Vec<Val>,
117
117
pub(crate)pending:Pending,
118
+
/* Sync helpers that suspended during the current resume; drained into the active Coroutine on yield-save. Lives at VM scope (not `Pending`) because it propagates across dispatch frames, not within one. */
Copy file name to clipboardExpand all lines: compiler/src/modules/vm/types/coro.rs
+10Lines changed: 10 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -33,6 +33,16 @@ pub struct CoroutineHandle {
33
33
pubstate:CoroState,
34
34
}
35
35
36
+
// Suspended sync helper frame: a plain user fn called from a coroutine hit a yielding builtin mid-execution, so its state is snapshotted and parked on the enclosing Coroutine. Frames stack innermost-last; resume walks inside-out so each return value lands on the next frame's stack at the Call site.
37
+
#[derive(Clone,Debug)]
38
+
pubstructSyncFrame{
39
+
pubip:usize,
40
+
pubfi:usize,
41
+
pubslots:Vec<Val>,
42
+
pubstack_delta:Vec<Val>,
43
+
pubiter_delta:Vec<IterFrame>,
44
+
}
45
+
36
46
/* Call-site snapshot for traceback rendering; pushed by `exec_call`, popped on return/error. */
// Trailing `Vec<SyncFrame>` stacks suspended sync sub-calls (innermost-last): plain user fns called from this coro that hit a yielding builtin before returning. Resume walks inside-out so each return lands on the next frame's stack at the Call site.
0 commit comments