Skip to main content

luna_core/runtime/
coroutine.rs

1//! Coroutine (thread) objects. A coroutine owns a full execution context
2//! — value stack, call frames, open upvalues, to-be-closed slots and stack top
3//! — that is swapped into the running `Vm` while it is active and saved back
4//! here while it is suspended.
5
6use crate::runtime::Upvalue;
7use crate::runtime::function::{CallFrame, ContKind};
8use crate::runtime::heap::{Gc, GcHeader, Marker};
9use crate::runtime::table::Table;
10use crate::runtime::value::Value;
11
12/// Lua coroutine status (PUC `coroutine.status`).
13#[derive(Clone, Copy, PartialEq, Eq, Debug)]
14pub enum CoroStatus {
15    /// created or yielded — resumable
16    Suspended,
17    /// currently executing (the running thread)
18    Running,
19    /// resumed another coroutine and is waiting for it
20    Normal,
21    /// finished or errored — not resumable
22    Dead,
23}
24
25/// A Lua coroutine (`thread`) — one independent execution context plus its
26/// saved value/frame stacks and resume linkage.
27#[repr(C)]
28pub struct Coro {
29    pub(crate) hdr: GcHeader,
30    /// Resume state (suspended / running / normal / dead).
31    pub status: CoroStatus,
32    /// the body function, kept for the first resume (and as a GC root)
33    pub body: Value,
34    /// whether the body frame has been pushed yet (first resume vs. continue)
35    pub started: bool,
36    /// the coroutine that resumed this one (to restore on yield/return and for
37    /// `coroutine.running`); `None` once suspended/dead
38    pub resumer: Option<Gc<Coro>>,
39    /// where execution suspended on `yield`: the call slot and result count to
40    /// finish that call with the next resume's arguments
41    pub resume_at: Option<(u32, i32)>,
42    /// the error object a coroutine died with (when it errored rather than
43    /// returned); `coroutine.close` reports it once, then clears it
44    pub error_value: Option<Value>,
45    /// snapshot of the traceback at the error point — captured before the
46    /// dying coroutine's frames are unwound, so `debug.traceback(co)` on a
47    /// dead-with-error coroutine still shows the error site (PUC's
48    /// `luaG_errormsg` flow plus a per-thread `errfunc` snapshot).
49    pub error_traceback: Option<Vec<u8>>,
50    /// the same snapshot one line per stack level, so `debug.traceback(co,
51    /// msg, level)` can start at any level
52    pub(crate) error_levels: Option<Vec<Vec<u8>>>,
53    /// while it waits on a coroutine it resumed (status normal): its own
54    /// running natives, as indices into the Vm's `running_natives`
55    pub(crate) natives: std::ops::Range<usize>,
56    // ---- saved execution context (valid while suspended/normal) ----
57    /// Saved value stack.
58    pub stack: Vec<Value>,
59    /// Saved frame stack (Lua frames + native continuations).
60    pub frames: Vec<CallFrame>,
61    /// `frames`' `__call` counts, by frame index (see `Vm::frame_ccmt`)
62    pub(crate) frame_ccmt: Vec<u8>,
63    /// Open-upvalue list — `(stack slot, upvalue cell)` pairs.
64    pub open_upvals: Vec<(u32, Gc<Upvalue>)>,
65    /// Stack indices of registered `<close>` slots (5.4+).
66    pub tbc: Vec<u32>,
67    /// Saved stack top.
68    pub top: u32,
69    /// live pcall/xpcall continuation count (PUC nCcalls portion); see Vm
70    pub pcall_depth: u32,
71    /// this thread's debug hook state (PUC per-thread hook/hookmask)
72    pub hook: crate::vm::exec::HookState,
73    /// PUC `L->l_gt` — the thread's own globals table. Captured from the
74    /// resuming thread at create time, then swapped with `Vm.globals` on
75    /// every resume/yield boundary so a `setfenv(0, env)` inside the
76    /// coroutine only retunes *this* thread (5.1 closure.lua :177 pins
77    /// this — yielding `getfenv()` after the rewire must see the
78    /// coroutine's own per-closure env, not the caller's).
79    pub globals: Gc<Table>,
80}
81
82impl Coro {
83    pub(crate) fn trace(&self, m: &mut Marker) {
84        m.value(self.body);
85        for &v in self.stack.iter() {
86            m.value(v);
87        }
88        for cf in self.frames.iter() {
89            match cf {
90                CallFrame::Lua(f) => {
91                    m.header(f.closure.as_ptr() as *mut GcHeader);
92                }
93                CallFrame::Cont(nc) => {
94                    if let ContKind::Xpcall { handler } = nc.kind {
95                        m.value(handler);
96                    }
97                }
98            }
99        }
100        for &(_, uv) in self.open_upvals.iter() {
101            m.header(uv.as_ptr() as *mut GcHeader);
102        }
103        if let Some(r) = self.resumer {
104            m.header(r.as_ptr() as *mut GcHeader);
105        }
106        if let Some(e) = self.error_value {
107            m.value(e);
108        }
109        if let Some(h) = self.hook.func {
110            m.value(h);
111        }
112        m.value(Value::Table(self.globals));
113    }
114}