The withdrawal last iteration was caused by `rm -f /tmp/xenia-canary.lock` -- the obvious way past a lock orphaned by kill -9, which also disables the guard for every later launch. Three instances ended up live at once, sharing /tmp/xenia_pad.txt and display :98, and silently confounded an input experiment. Care is not a fix, so this is tooling. ensure_single_emulator.sh counts live instances, stops them (plain kill, then -9, each with a bounded wait), verifies ZERO, and only then removes the lock -- refusing loudly if any remain. The lock is never removed before the condition it guards against is verified absent. FOUR scripts did the bare `rm -f`, and only two were mine from today: menu_loop_session.sh, title_draw_capture.sh, poke_control.sh and resume_reliability.sh. So the footgun was corpus-wide rather than introduced this session. All four now route through the guard. The guard is controlled rather than assumed: run against a deliberately started live instance it reports "1 instance(s) live -- stopping them", ends at 0 with the lock cleared, and exits 0. A guard that only ever passes on an already-clean slate would prove nothing. It also kills by process NAME. `pkill -f xenia_canary` matches the shell running it -- that has now cost this corpus three commands, one of them a cleanup that died halfway and left the very instances it was meant to remove. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
43 lines
1.9 KiB
Bash
Executable File
43 lines
1.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Guarantee EXACTLY ZERO xenia instances, then clear the lock. Source or run.
|
|
#
|
|
# `run-canary` holds /tmp/xenia-canary.lock to enforce the "one emulator at a
|
|
# time" rule. A `kill -9` orphans it, and the obvious unblock -- `rm -f` the lock
|
|
# -- ALSO DISABLES THE GUARD FOR EVERY LATER LAUNCH. On 2026-08-30 that left
|
|
# three instances live at once, all reading /tmp/xenia_pad.txt and sharing
|
|
# display :98, which silently confounded an input experiment: a scripted press
|
|
# reaches every instance while `screenshot` grabs whichever window is topmost.
|
|
# The finding built on it had to be withdrawn.
|
|
#
|
|
# So: the lock is only ever removed AFTER the condition it guards against is
|
|
# verified absent. Never `rm -f` it directly.
|
|
#
|
|
# . ensure_single_emulator.sh (or) ensure_single_emulator.sh
|
|
emu_count(){ ps -C xenia_canary --no-headers 2>/dev/null | wc -l; }
|
|
|
|
ensure_single_emulator() {
|
|
local n; n=$(emu_count)
|
|
if [ "$n" -gt 0 ]; then
|
|
echo "ensure_single_emulator: $n instance(s) live — stopping them"
|
|
# kill by NAME. `pkill -f xenia_canary` matches the shell running it and
|
|
# kills the caller instead; that has happened three times in this corpus.
|
|
ps -o pid= -C xenia_canary | xargs -r kill
|
|
for _ in 1 2 3 4 5 6 7 8 9 10; do [ "$(emu_count)" -eq 0 ] && break; sleep 1; done
|
|
if [ "$(emu_count)" -gt 0 ]; then
|
|
ps -o pid= -C xenia_canary | xargs -r kill -9
|
|
for _ in 1 2 3 4 5 6 7 8 9 10; do [ "$(emu_count)" -eq 0 ] && break; sleep 1; done
|
|
fi
|
|
fi
|
|
n=$(emu_count)
|
|
if [ "$n" -ne 0 ]; then
|
|
echo "ensure_single_emulator: REFUSING — $n instance(s) still live." >&2
|
|
echo " Do NOT rm the lock: it is the only thing stopping a second one." >&2
|
|
return 3
|
|
fi
|
|
rm -f /tmp/xenia-canary.lock # safe now, and only now
|
|
echo "ensure_single_emulator: 0 instances, lock cleared"
|
|
return 0
|
|
}
|
|
# run directly (not sourced) -> do it
|
|
(return 0 2>/dev/null) || ensure_single_emulator
|