diff --git a/README.md b/README.md index 73633ac..e4daed9 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,12 @@ starts on software rendering as it always did. None of this is essential. would leave the machine short, printing what is attached, what is large, and what usually frees enough. `EMU_FORCE=1` overrides it. +The emulator is launched in its own session with stdin closed, so it outlives +both an interactive terminal and an isolated command runner. A bare background +job appeared to work from long-lived agent shells but was shut down as soon as +a one-command execution session ended; the emulator's own log showed a clean +shutdown after boot rather than a crash. + The reason is a real incident rather than tidiness. On 2026-08-30 an emulator was started on this box with 2.8 GB available; the OOM killer ran, and what it took was not the emulator that had just started — it walked the user slice diff --git a/bin/emu b/bin/emu index bf944eb..a317293 100755 --- a/bin/emu +++ b/bin/emu @@ -193,6 +193,20 @@ headless_display() { cat "$run/display" } +# Starts the emulator outside the caller's terminal session. +# +# A plain background job survives an ordinary interactive shell here, which hid this until `emu up` +# was run through an isolated command runner: the emulator reached boot, `emu up` returned success, +# and the runner then closed its session and asked every job still attached to it to shut down. The +# emulator complied cleanly, so its log ended at "Boot completed" and looked like a device crash. +# `setsid` is the process-lifetime boundary; `nohup`, a closed stdin and disown cover the shell's +# own three ways of retaining or signalling the job. Keep every rendering branch on this path. +start_emulator() { + setsid nohup "$@" "$log" 2>&1 & + pid=$! + disown -h "$pid" +} + cmd_up() { if serial=$(avd_serial "$adb" "$avd"); then echo "emu: '$avd' is already running ($serial)" >&2 @@ -245,21 +259,20 @@ cmd_up() { # Vulkan that gfxstream cannot pair with Venus here. if [ "${EMU_GPU:-}" != software ] && { [ -n "${DISPLAY:-}" ] || [ -n "${WAYLAND_DISPLAY:-}" ]; }; then echo "emu: starting '$avd' with GPU acceleration" >&2 - "$sdk/emulator/emulator" -avd "$avd" -gpu host -no-audio >"$log" 2>&1 & + start_emulator "$sdk/emulator/emulator" -avd "$avd" -gpu host -no-audio elif [ "${EMU_GPU:-}" != software ] && display=$(headless_display); then echo "emu: no display -- starting '$avd' headless on the GPU ($display)" >&2 - DISPLAY="$display" "$sdk/emulator/emulator" -avd "$avd" \ - -gpu host $GPU_HOST_FEATURES -no-audio -no-window >"$log" 2>&1 & + start_emulator env DISPLAY="$display" "$sdk/emulator/emulator" -avd "$avd" \ + -gpu host $GPU_HOST_FEATURES -no-audio -no-window else if [ "${EMU_GPU:-}" = software ]; then echo "emu: EMU_GPU=software -- starting '$avd' on the CPU, off the host GPU" >&2 else echo "emu: no display and no compositor -- starting '$avd' on software rendering" >&2 fi - "$sdk/emulator/emulator" -avd "$avd" -gpu swiftshader_indirect -no-audio -no-window \ - >"$log" 2>&1 & + start_emulator "$sdk/emulator/emulator" -avd "$avd" -gpu swiftshader_indirect \ + -no-audio -no-window fi - pid=$! for _ in $(seq 150); do if ! kill -0 "$pid" 2>/dev/null; then