From 348895eb7efa83fb014aa11641adbc2f59a6ccd8 Mon Sep 17 00:00:00 2001 From: Greg Pomerantz Date: Tue, 13 Oct 2026 16:57:46 -0400 Subject: [PATCH] Streamline repro_gioui_hang.sh Shorter header (bug in two lines), one combined emulator wait loop, am start -S replaces the separate force-stop, fewer section comments. Same behavior: verified REPRODUCED (exit 0) on the emulator. --- scripts/repro_gioui_hang.sh | 84 ++++++++++++------------------------- 1 file changed, 26 insertions(+), 58 deletions(-) diff --git a/scripts/repro_gioui_hang.sh b/scripts/repro_gioui_hang.sh index bbf7d89..9d506dc 100755 --- a/scripts/repro_gioui_hang.sh +++ b/scripts/repro_gioui_hang.sh @@ -1,89 +1,57 @@ #!/usr/bin/env bash -# Reproduce the gioui Android activity-recreation hang with a STOCK gioui -# example app (the "hello" example from gioui.org/example). +# gioui v0.10.x Android bug: after the activity is recreated (relaunch, +# config change), the second GioView.onCreateView sends its window-creation +# ack on the UI thread with no receiver -> the UI thread deadlocks, the +# window never gets an input channel, the app is black and unresponsive. # -# What it does: -# 1. Builds gioui.org/example's hello app (unmodified) with gogio. -# 2. Installs it on an emulator (boots one if needed). -# 3. Recreates the activity via `cmd uimode night yes` (uiMode is not in -# gioui's configChanges mask, so Android destroys/recreates the -# activity in the same process). -# 4. Taps the app and checks logcat for NO_INPUT_CHANNEL. +# This script runs the stock gioui "hello" example on an emulator, recreates +# its activity with a uiMode toggle, taps it, and greps logcat. # -# The bug (gioui v0.10.x, app/os_android.go onCreateView): on re-attach the -# second onCreateView sends the window-creation ack -# (mainWindow.windows <- struct{}{}) on the UI thread, but its only -# receiver (newWindow in Window.init) never re-runs on Android — the driver -# is never reset, so w.Event() never returns to re-init. The UI thread -# blocks forever, the activity never attaches an input channel, and the app -# is black and unresponsive (process still alive). -# -# Usage: -# scripts/repro_gioui_hang.sh [avd-name] [path-to-apk] -# -# Exit codes: 0 = bug REPRODUCED, 1 = not reproduced, 2 = setup failure. +# scripts/repro_gioui_hang.sh [avd] [apk] (apk omitted: build stock example) +# exit 0 = REPRODUCED, 1 = clean, 2 = setup failure set -euo pipefail AVD=${1:-pad_avd} APK=${2:-} SDK=${ANDROID_HOME:-$HOME/android-sdk} export PATH=$PATH:$HOME/go/bin:$SDK/platform-tools:$SDK/build-tools/35.0.0 - say() { printf '\n=== %s\n' "$*"; } die() { say "FAIL: $*"; exit 2; } -# --- 1. Build the stock hello example, unless an APK was supplied --------- +# 1. Build the stock example, if an APK was not supplied if [ -z "$APK" ]; then + say "building stock gioui.org/example hello (v0.10.3)" SRC=$(mktemp -d) - trap 'rm -rf "$SRC"' EXIT - say "building stock gioui.org/example hello app (v0.10.3)" go mod download gioui.org/example@v0.10.3 cp "$HOME/go/pkg/mod/gioui.org/example@v0.10.3/hello/hello.go" "$SRC/main.go" - cat > "$SRC/go.mod" <<'EOF' -module hello - -go 1.23 - -require gioui.org v0.10.3 -EOF - (cd "$SRC" && go mod tidy && gogio -target android -targetsdk 34 -arch amd64 -o /tmp/hello-gioui-repro.apk .) - APK=/tmp/hello-gioui-repro.apk + printf 'module hello\n\ngo 1.23\n\nrequire gioui.org v0.10.3\n' > "$SRC/go.mod" + (cd "$SRC" && go mod tidy && gogio -target android -targetsdk 34 -arch amd64 -o "$SRC/hello.apk" .) + APK=$SRC/hello.apk fi [ -f "$APK" ] || die "APK not found: $APK" -# --- 2. Emulator: reuse a booted one or start $AVD ------------------------ -say "waiting for emulator (avd $AVD)" +# 2. Emulator: reuse a booted one, else boot $AVD +say "emulator ($AVD)" EMU="" for _ in $(seq 1 60); do EMU=$(adb devices | awk '$1 ~ /^emulator-/ {print $1; exit}') - [ -n "$EMU" ] && break - emulator -avd "$AVD" -no-window -no-audio -no-boot-anim -gpu swiftshader_indirect >/dev/null 2>&1 & + [ -n "$EMU" ] && [ "$(adb -s "$EMU" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" = "1" ] && break + [ -z "$EMU" ] && emulator -avd "$AVD" -no-window -no-audio -no-boot-anim -gpu swiftshader_indirect >/dev/null 2>&1 & sleep 5 done -[ -n "$EMU" ] || die "no emulator available" -for _ in $(seq 1 60); do - bc=$(adb -s "$EMU" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r') - [ "$bc" = "1" ] && break - sleep 5 -done -[ "$(adb -s "$EMU" shell getprop sys.boot_completed | tr -d '\r')" = "1" ] || die "emulator did not boot" +[ -n "$EMU" ] || die "no booted emulator" say "emulator: $EMU" -# --- 3. Install + launch --------------------------------------------------- +# 3. Install + launch APPID=localhost.hello # gogio's default app id for module "hello" adb -s "$EMU" install -r "$APK" >/dev/null -adb -s "$EMU" shell am force-stop "$APPID" >/dev/null 2>&1 || true -adb -s "$EMU" logcat -c -adb -s "$EMU" shell am start -n "$APPID/org.gioui.GioActivity" >/dev/null +adb -s "$EMU" shell am start -S -n "$APPID/org.gioui.GioActivity" >/dev/null sleep 5 adb -s "$EMU" shell pidof "$APPID" >/dev/null || die "app did not start" -say "baseline: app running" -# --- 4. Recreate the activity ---------------------------------------------- -# uiMode is not in gioui's configChanges mask -> Android recreates the -# activity. Force a real state change (a no-op toggle recreates nothing). -cur=$(adb -s "$EMU" shell cmd uimode night | tr -d '\r') # "Night mode: yes/no" -case $cur in +# 4. Recreate the activity, then tap and check logcat. +# Toggle to the OPPOSITE uiMode: a no-op toggle recreates nothing. +case $(adb -s "$EMU" shell cmd uimode night | tr -d '\r') in *yes*) adb -s "$EMU" shell cmd uimode night no >/dev/null ;; *) adb -s "$EMU" shell cmd uimode night yes >/dev/null ;; esac @@ -92,11 +60,11 @@ adb -s "$EMU" logcat -c adb -s "$EMU" shell input tap 540 1200 sleep 2 -# --- 5. Verdict ------------------------------------------------------------- +# 5. Verdict adb -s "$EMU" exec-out screencap -p > /tmp/gioui-hang-repro.png if adb -s "$EMU" logcat -d | grep -q NO_INPUT_CHANNEL; then - say "REPRODUCED: activity recreation left the window with NO_INPUT_CHANNEL (see /tmp/gioui-hang-repro.png)" + say "REPRODUCED: window has NO_INPUT_CHANNEL after recreation (screenshot: /tmp/gioui-hang-repro.png)" exit 0 fi -say "NOT reproduced: no NO_INPUT_CHANNEL after recreation (gioui handles re-attach correctly)" +say "NOT reproduced: gioui handles re-attach cleanly" exit 1