Skip to content

fix(io): per-VM LStream side table — VM churn retention (#317) - #319

Merged
ianm199 merged 1 commit into
mainfrom
nightshift/317-lstream-retention
Jul 21, 2026
Merged

ianm199 merged 1 commit into
mainfrom
nightshift/317-lstream-retention

Conversation

@ianm199

@ianm199 ianm199 commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Closes #317. Produced by the agent-console night-shift on the prod box: a Claude Fable 5 queue run (task 9bee9cc6, run 14e66509) investigating the Windows leak-canary envelope.

Key finding: the retention is platform-neutral, not Windows-specific — reproduced on Linux at ~165 B/iteration inside the reported 40-260 envelope (io_registry_churn: live bytes +10,608 over 64 iterations after warmup). Root cause: the std-file LStream side table outliving VM churn. Fix: the side table becomes per-VM so it dies with the VM; regression coverage extends leak_canaries.rs (passes independently: 1/1); CHANGELOG entry included.

Operator note: the run settled needs_review with the commit retained in its worktree (the task was delivery-none — an investigation brief that turned into a fix); the coordinator harvested, re-verified the canary in place, and delivered. The in-turn gh credential gap this exposed is recorded on agent-console#340.

🤖 Generated with Claude Code

https://claude.ai/code/session_01F5gteHsAKQ6CLqfiVTS8yr

The io library's LStream side table (file-handle userdata identity ->
stream) lived in a process-wide thread_local!, so it outlived every VM
that filled it: each Lua instance registered stdin/stdout/stderr (plus
any opened files) on stdlib open and never removed them. An entry was
only ever displaced when a later VM's file userdata landed at an
already-seen address, so retention scaled with how rarely the platform
allocator reuses addresses — small on Linux, large on Windows, which is
exactly the ~40-260 B/iteration envelope issue #317 recorded from the
first Windows suite run.

Measured on Linux with the leak-canary counting allocator (pre-fix ->
post-fix, per iteration after warmup):

  vm_churn            32 B  ->  0 B
  runtime_churn       29 B  ->  0 B
  io_registry_churn  151 B  ->  0 B   (new scenario, 16 VM churns/iter)

The table now lives on GlobalState (type-erased Rc<dyn Any>, since the
concrete map type is lua-stdlib's) and dies with its VM. The new
io_registry_churn canary scenario pins the fix at the strict POSIX
tolerance on every platform. The Windows tolerance envelope stays until
a Windows suite run re-measures; not verified on Windows here.

Gates: leak_canaries green (0 growth all scenarios), multiversion_oracle
185/185, lua-stdlib suite green, strict_guard_check workspace run green
except debug_repl_renders_real_error_text, which fails on this rig
because the reference lua5.1.5 binary itself segfaults printing a NULL
lua_tostring result (environment-dependent C UB, unrelated to io).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ianm199
ianm199 merged commit d425379 into main Jul 21, 2026
3 checks passed
@ianm199
ianm199 deleted the nightshift/317-lstream-retention branch July 21, 2026 20:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows: embedding VM churn retains ~40-260 B/iteration (leak canary envelope); pre-existing, GC-reclaimable, non-detached

2 participants