Add Lua coroutine support#7
Conversation
Wrap the core Lua 5.4 coroutine C API: new-thread, resume, yield, coroutine-status, is-yieldable?, to-thread, plus YIELD and TYPE_THREAD constants. Add Luax convenience helpers: resume-coroutine (Result-based error handling), coroutine-suspended?, and coroutine-finished?. 25 new tests covering Lua-defined coroutines, C function coroutines (tail-call yield), value passing through yield/resume, error propagation, and multiple independent coroutines.
There was a problem hiding this comment.
Build & Tests
CI: Ubuntu pass, macOS pending (still running)
Local build: no Carp compiler on this machine — relied on CI
Tests: cannot verify — see finding #1
Findings
1. New test file is not included in CI (blocking)
test/coroutine.carp (331 lines, 25 tests) is not in the CI workflow. The workflow at .github/workflows/ci.yml lines 40-43 runs:
carp -x test/lua.carp
carp -x test/midlevel.carp
carp -x test/cfunction.carp
carp -x test/metatable.carp
The new test/coroutine.carp is missing. This means:
- CI passing does not validate any of the new coroutine code
- The test file could have compilation errors or failing tests and CI would still pass
- There is no automated verification that the bindings work
Fix needed: add carp -x test/coroutine.carp to the CI workflow's test step.
2. resume discards nres (non-blocking, design note)
lua.carp:355-356 — the resume template declares a local int nres and passes its address to lua_resume, but the value is never returned to the caller:
int $NAME(lua_State* co, lua_State* from, int nargs)
$DECL { int nres; return lua_resume(co, from, nargs, &nres); }In Lua 5.4, nres tells you how many values the coroutine left on the stack. Without this, callers must know the expected number of results in advance. This is fine for the common case (all the tests know how many values to expect), but limits variable-result coroutine patterns. A later extension could return a pair (status, nres).
Not blocking — matches the simplicity of the existing API style.
3. Code quality is good ✓
Reviewed all new bindings in lua.carp:330-369 and Luax helpers at lines 899-921:
new-thread→lua_newthread: correct signature(Fn [&Lua] &Lua), thread is GC-managed by parentcoroutine-status→lua_status: correctis-yieldable?→lua_isyieldable: template returns Bool, correctyield→lua_yield: correct wrapping for Lua 5.4to-thread→lua_tothread: correctresume-coroutine: good error handling withString.from-cstr-orfallbackcoroutine-suspended?/coroutine-finished?: clean status predicates
4. Documentation is good ✓
Module-level doc (lua.carp:635-656) includes a complete coroutine usage example. Luax section (lua.carp:752-765) documents resume-coroutine and status predicates. The tail-call yield restriction for C functions is clearly noted.
5. Test coverage is thorough (if it compiles) ✓
test/coroutine.carp covers: YIELD/TYPE_THREAD constants, new-thread, coroutine-status, Lua-defined coroutines (multi-yield, value passing, error propagation), C function coroutines (yield and non-yield), Luax.resume-coroutine (success/yield/error), string values, and multiple independent coroutines with separate state. Good edge cases.
Verdict: revise
The code looks correct and well-designed, but two things block a merge recommendation:
- The new test file must be added to CI — without this, there is zero automated verification of the coroutine bindings. Add
carp -x test/coroutine.carpto.github/workflows/ci.yml. - macOS CI is still pending — wait for it to finish before merging.
Summary
Adds bindings for Lua 5.4's coroutine API, enabling async/concurrent Lua patterns from Carp.
Low-level
Luamodule:YIELDstatus constant,TYPE_THREADtype constantnew-thread— create a coroutine thread (wrapslua_newthread)resume— resume a coroutine (wrapslua_resume, handles thenresoutput parameter internally)yield— yield from a C function coroutine as a tail call (wrapslua_yield)coroutine-status— query coroutine state (wrapslua_status)is-yieldable?— check if the current context can yield (wrapslua_isyieldable)to-thread— read a thread value from the stack (wrapslua_tothread)Luaxconvenience helpers:resume-coroutine— resume withResulterror handling (returns(Success status)or(Error msg))coroutine-suspended?/coroutine-finished?— status predicatesTests: 25 new tests in
test/coroutine.carpcovering:Luax.resume-coroutineResult handlingNote:
Lua.yieldcan only be used as a tail call in C functions (Lua 5.4 limitation). Lua-defined coroutines usingcoroutine.yield()have no such restriction.Opened by the carpentry-org heartbeat agent (Claude). Veit has not reviewed this yet.