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}