Repository navigation
feat: drive Lua apps with an init/update/fini lifecycle in the flua host - #33
Merged
Merged
Conversation
esrrhs
force-pushed
the
feat/app-lifecycle-host
branch
13 times, most recently
from
October 16, 2026 11:59
189dc77 to
92b11ae
Compare
…lua host
flua used to take one entry function (default `main`) and leave the frame loop
to the script. fakelua has no GC and its frame arena is only reset when a
top-level Call starts (reentrant count back to zero), so a `while true` loop
inside one Call makes the arena grow without bound and per-request allocations
pile up forever. Every app had to hand-roll its own loop, and getting that wrong
is exactly how the majiang server ended up leaking 2.3GB in 85 seconds.
Entry points are now fixed and mandatory, always in package "main" -- no flags
to rename them, so every app is written the same way:
package "main"
function init() ... end called once at startup
function update() ... end called every frame
function fini() ... end called once before exit
- each phase is its own top-level Call, so the frame arena is reclaimed every
tick; the host does not pace the loop (a script that wants a fixed cadence
calls os.sleep(ms) itself)
- missing entry points are reported up front with the fixed layout spelled out,
instead of failing later with "function not found"
- graceful shutdown, both forms run fini first: a non-zero return from update, or
flua.set_exit(code) from Lua. os.exit stays a hard exit that skips fini.
- flua deliberately installs no signal handler: reacting to SIGINT / SIGTERM is
the application's decision, so the host stays out of it
- --once runs init + fini without the update loop (one-shot script / self-check)
- the script argument may be a directory: every .lua file inside is compiled in
filename order
- three small wrappers (GetNativeIntArg / NativeNil / HasEntryFunction) so a host
can write native callbacks and probe for entries without touching inter::,
which stays an internal detail of the interpreter and JIT
- new cmd/test.lua asserts the lifecycle contract from inside the script (call
counts and order, cross-frame state only surviving in container.map,
flua.set_exit semantics, pcall/error across frame boundaries); it runs in the
Linux, macOS and Windows CI jobs, replacing the bare `flua` invocation that
only proved the binary starts
- READMEs document the fixed layout
The library behaviour is unchanged: include/fakelua.h only gains the three
additive helpers above, so hosts that embed fakelua keep working as before.
esrrhs
force-pushed
the
feat/app-lifecycle-host
branch
from
October 16, 2026 12:17
92b11ae to
92d7424
Compare
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #33 +/- ##
=======================================
Coverage 88.07% 88.07%
=======================================
Files 124 124
Lines 25486 25494 +8
=======================================
+ Hits 22446 22454 +8
Misses 3040 3040 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
fluatook a single entry function (defaultmain) and left the frame loop to the script.fakelua has no GC and its frame arena is only reset when a top-level Call starts (reentrant
count back to zero), so a
while trueloop inside one Call keeps the arena alive andper-request allocations pile up without bound -- the majiang server leaked 2.3GB in 85 seconds
that way, and every app had to hand-roll its own loop.
What changes
Entry points are fixed and mandatory, always in package
main; there are no flags to renamethem, so every app is written the same way:
host adds no pacing (a script that wants a fixed cadence calls
os.sleep(ms)itself)failing later with "function not found"
finifirst: a non-zero return fromupdate, orflua.set_exit(code)from Lua.os.exitstays a hard exit that skipsfiniapplication's own decision
--oncerunsinit+finiwithout the update loop (one-shot script / self-check).luainside is compiled in filename orderGetNativeIntArg/NativeNil/HasEntryFunction) let a hostwrite native callbacks and probe for entries without touching
inter::, which stays aninternal detail of the interpreter and JIT
Test
cmd/test.luaasserts the contract from inside the script -- call counts and order,cross-frame state surviving only in
container.map,flua.set_exitsemantics (the rest ofthe current frame still runs), pcall/error across frame boundaries, exit code 0. It runs in
the Linux, macOS and Windows CI jobs, replacing the bare
fluainvocation that only provedthe binary starts.
Expected stdout:
Compatibility
include/fakelua.honly gains the three helpers, so hoststhat embed fakelua keep working
--entry/--repeatare gone (nothing depended on them),--init/--update/--finiare replaced by the fixed entries plus
--once--repeat=2plusfunction main()