Skip to main content

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}