From 4c92f544393899bb5d8e5a4aa88541138c737de2 Mon Sep 17 00:00:00 2001 From: sylph-decoder Date: Sun, 30 Aug 2026 16:23:59 +0000 Subject: [PATCH] re: make the one-emulator rule enforceable instead of remembered 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) Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v --- tools/re-capture/ensure_single_emulator.sh | 42 ++++++++++++++++++++++ tools/re-capture/menu_loop_session.sh | 3 +- tools/re-capture/poke_control.sh | 3 +- tools/re-capture/resume_reliability.sh | 3 +- tools/re-capture/title_draw_capture.sh | 3 +- 5 files changed, 50 insertions(+), 4 deletions(-) create mode 100755 tools/re-capture/ensure_single_emulator.sh diff --git a/tools/re-capture/ensure_single_emulator.sh b/tools/re-capture/ensure_single_emulator.sh new file mode 100755 index 00000000..40a82f9f --- /dev/null +++ b/tools/re-capture/ensure_single_emulator.sh @@ -0,0 +1,42 @@ +#!/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 diff --git a/tools/re-capture/menu_loop_session.sh b/tools/re-capture/menu_loop_session.sh index d11cfa2b..a0dc98c4 100755 --- a/tools/re-capture/menu_loop_session.sh +++ b/tools/re-capture/menu_loop_session.sh @@ -8,7 +8,8 @@ export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98 SD="$(cd "$(dirname "$0")" && pwd)" OUT="${1:-/sylph-home/re/menuloop3}"; mkdir -p "$OUT" ps -o pid= -C xenia_canary | xargs -r kill # plain kill: it clears its own lock -rm -f /tmp/xenia-canary.lock +. "$(dirname "${BASH_SOURCE[0]}")/ensure_single_emulator.sh" +ensure_single_emulator || exit 3 ( cd "$OUT" && nohup run-canary --mem_watch=false \ --logged_profile_slot_0_xuid=B13EBABEBABEBABE \ >"$OUT/canary.stdout" 2>"$OUT/canary.stderr" & ) diff --git a/tools/re-capture/poke_control.sh b/tools/re-capture/poke_control.sh index dbf00947..6fa000f6 100755 --- a/tools/re-capture/poke_control.sh +++ b/tools/re-capture/poke_control.sh @@ -37,7 +37,8 @@ import frozen; d,_=frozen.frozen(5.0); sys.exit(1 if d else 0)"; } for a in $(seq 1 "$ATTEMPTS"); do echo "=== attempt $a/$ATTEMPTS ($(date +%T))" pkill -9 -x xenia_canary 2>/dev/null; pkill -9 -f '[p]ilot.py' 2>/dev/null; sleep 2 - rm -f /tmp/xenia-canary.lock +. "$(dirname "${BASH_SOURCE[0]}")/ensure_single_emulator.sh" +ensure_single_emulator || exit 3 "$SD/launch_mission.sh" fly >/tmp/pc-boot.log 2>&1 || { echo " boot failed"; continue; } if ! alive_and_moving; then echo " guest already frozen after boot"; continue; fi diff --git a/tools/re-capture/resume_reliability.sh b/tools/re-capture/resume_reliability.sh index 13919f75..08e8abba 100755 --- a/tools/re-capture/resume_reliability.sh +++ b/tools/re-capture/resume_reliability.sh @@ -35,7 +35,8 @@ fi for i in $(seq 1 "$RUNS"); do pkill -9 -x xenia_canary 2>/dev/null; sleep 3 - rm -f /tmp/xenia-canary.lock +. "$(dirname "${BASH_SOURCE[0]}")/ensure_single_emulator.sh" +ensure_single_emulator || exit 3 LOG_MASK=12 LOG_LEVEL=3 BOOT_MENU_LOG="$OUT/$TAG-$i.stdout" \ timeout 700 "$SD/boot_menu.sh" "$TAG-$i" > "$OUT/$TAG-$i.boot" 2>&1 sleep 20 diff --git a/tools/re-capture/title_draw_capture.sh b/tools/re-capture/title_draw_capture.sh index b5fb55ec..1e6973b3 100755 --- a/tools/re-capture/title_draw_capture.sh +++ b/tools/re-capture/title_draw_capture.sh @@ -16,7 +16,8 @@ set -u export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98 SD="$(cd "$(dirname "$0")" && pwd)" OUT="${1:-/sylph-home/re/titledraw}"; mkdir -p "$OUT"; rm -f "$OUT"/xenia_re_ui_draws_*.log -ps -o pid= -C xenia_canary | xargs -r kill; rm -f /tmp/xenia-canary.lock +. "$(dirname "${BASH_SOURCE[0]}")/ensure_single_emulator.sh" +ensure_single_emulator || exit 3 ( cd "$OUT" && nohup run-canary --mem_watch=false --log_ui_draws=true \ --ui_draw_capture_frames="${FRAMES:-150}" --ui_draw_capture_max=400000 \ --logged_profile_slot_0_xuid=B13EBABEBABEBABE \