seq_runtime/weave/resume.rs
1//! Caller-side weave operations: `patch_seq_resume` and `patch_seq_weave_cancel`.
2//!
3//! Both operate on a `WeaveHandle` (a `Value::WeaveCtx`) received from
4//! `strand.weave`, sending on the shared `resume_chan` to drive the woven
5//! coroutine forward or tear it down.
6
7use crate::stack::{Stack, pop, push};
8use crate::value::{Value, WeaveMessage};
9use std::sync::Arc;
10
11/// Resume a woven strand with a value
12///
13/// Stack effect: ( WeaveHandle a -- WeaveHandle a Bool )
14///
15/// Sends value `a` to the weave and waits for it to yield.
16/// Returns (handle, yielded_value, has_more).
17/// - has_more = true: weave yielded a value
18/// - has_more = false: weave completed
19///
20/// # Error Handling
21///
22/// This function never panics (panicking in extern "C" is UB). On fatal error
23/// (null stack, type mismatch), it prints an error and aborts the process.
24///
25/// # Safety
26/// Stack must have a value on top and WeaveHandle below it
27#[unsafe(no_mangle)]
28pub unsafe extern "C" fn patch_seq_resume(stack: Stack) -> Stack {
29 // Note: We can't use assert! here (it panics). Use abort() for fatal errors.
30 if stack.is_null() {
31 eprintln!("strand.resume: stack is null (fatal programming error)");
32 std::process::abort();
33 }
34
35 // Pop the value to send
36 let (stack, value) = unsafe { pop(stack) };
37
38 // Pop the WeaveHandle
39 let (stack, handle) = unsafe { pop(stack) };
40
41 let (yield_chan, resume_chan) = match &handle {
42 Value::WeaveCtx {
43 yield_chan,
44 resume_chan,
45 } => (Arc::clone(yield_chan), Arc::clone(resume_chan)),
46 _ => {
47 eprintln!("strand.resume: expected WeaveHandle, got {:?}", handle);
48 std::process::abort();
49 }
50 };
51
52 // Wrap value in WeaveMessage for sending
53 let msg_to_send = WeaveMessage::Value(value.clone());
54
55 // Send resume value to the weave
56 if resume_chan.sender.send(msg_to_send).is_err() {
57 // Channel closed - weave is done
58 let stack = unsafe { push(stack, handle) };
59 let stack = unsafe { push(stack, Value::Int(0)) };
60 return unsafe { push(stack, Value::Bool(false)) };
61 }
62
63 // Wait for yielded value
64 match yield_chan.receiver.recv() {
65 Ok(msg) => match msg {
66 WeaveMessage::Done => {
67 // Weave completed
68 let stack = unsafe { push(stack, handle) };
69 let stack = unsafe { push(stack, Value::Int(0)) };
70 unsafe { push(stack, Value::Bool(false)) }
71 }
72 WeaveMessage::Value(yielded) => {
73 // Normal yield
74 let stack = unsafe { push(stack, handle) };
75 let stack = unsafe { push(stack, yielded) };
76 unsafe { push(stack, Value::Bool(true)) }
77 }
78 WeaveMessage::Cancel => {
79 // Shouldn't happen - Cancel is sent on resume_chan
80 let stack = unsafe { push(stack, handle) };
81 let stack = unsafe { push(stack, Value::Int(0)) };
82 unsafe { push(stack, Value::Bool(false)) }
83 }
84 },
85 Err(_) => {
86 // Channel closed unexpectedly
87 let stack = unsafe { push(stack, handle) };
88 let stack = unsafe { push(stack, Value::Int(0)) };
89 unsafe { push(stack, Value::Bool(false)) }
90 }
91 }
92}
93
94/// Cancel a weave, releasing its resources
95///
96/// Stack effect: ( WeaveHandle -- )
97///
98/// Sends a cancellation signal to the weave, causing it to exit cleanly.
99/// This is necessary to avoid resource leaks when abandoning a weave
100/// before it completes naturally.
101///
102/// If the weave is:
103/// - Waiting for first resume: exits immediately
104/// - Waiting inside yield: receives cancel signal and can exit
105/// - Already completed: no effect (signal is ignored)
106///
107/// # Error Handling
108///
109/// This function never panics (panicking in extern "C" is UB). On fatal error
110/// (null stack, type mismatch), it prints an error and aborts the process.
111///
112/// # Safety
113/// Stack must have a WeaveHandle (WeaveCtx) on top
114#[unsafe(no_mangle)]
115pub unsafe extern "C" fn patch_seq_weave_cancel(stack: Stack) -> Stack {
116 // Note: We can't use assert! here (it panics). Use abort() for fatal errors.
117 if stack.is_null() {
118 eprintln!("strand.weave-cancel: stack is null (fatal programming error)");
119 std::process::abort();
120 }
121
122 // Pop the WeaveHandle
123 let (stack, handle) = unsafe { pop(stack) };
124
125 // Extract the resume channel to send cancel signal
126 match handle {
127 Value::WeaveCtx { resume_chan, .. } => {
128 // Send cancel signal - if this fails, weave is already done (fine)
129 let _ = resume_chan.sender.send(WeaveMessage::Cancel);
130 }
131 _ => {
132 eprintln!(
133 "strand.weave-cancel: expected WeaveHandle, got {:?}",
134 handle
135 );
136 std::process::abort();
137 }
138 }
139
140 // Handle is consumed (dropped), stack returned without it
141 stack
142}