From d7f1f10887fc904ad7110c760204a854d51b1a6f Mon Sep 17 00:00:00 2001 From: Zehong Chen <2350288304@qq.com> Date: Mon, 7 Sep 2026 20:08:17 +0800 Subject: [PATCH] Coalesce concurrent shell restarts --- bin/omarchy-restart-shell | 7 ++ .../shell.d/restart-shell-concurrency-test.sh | 70 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100755 test/shell.d/restart-shell-concurrency-test.sh diff --git a/bin/omarchy-restart-shell b/bin/omarchy-restart-shell index dfc21724620..938af2d2fa2 100755 --- a/bin/omarchy-restart-shell +++ b/bin/omarchy-restart-shell @@ -11,6 +11,13 @@ session_omarchy_path=$(systemctl --user show-environment 2>/dev/null | sed -n 's CONFIG_DIR="$session_omarchy_path/shell" [[ -f $CONFIG_DIR/shell.qml ]] || { echo "Omarchy shell config not found: $CONFIG_DIR" >&2; exit 1; } +# A double-click or two setup helpers can request a restart at the same time. +# The first invocation already satisfies every overlapping request; letting a +# second one enter the kill loop can terminate the replacement shell while it +# is still starting. Coalesce concurrent restarts instead. +exec {restart_lock_fd}>"${XDG_RUNTIME_DIR:-/tmp}/omarchy-restart-shell.lock" +flock -n "$restart_lock_fd" || exit 0 + # Allow running from outside the session (e.g. over ssh) by deriving the # Hyprland instance signature from the newest instance runtime dir. if [[ -z ${HYPRLAND_INSTANCE_SIGNATURE:-} ]]; then diff --git a/test/shell.d/restart-shell-concurrency-test.sh b/test/shell.d/restart-shell-concurrency-test.sh new file mode 100755 index 00000000000..2c192c34799 --- /dev/null +++ b/test/shell.d/restart-shell-concurrency-test.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +require_command flock + +test_tmp=$(mktemp -d) +holder_pid="" + +cleanup() { + [[ -n $holder_pid ]] && kill "$holder_pid" 2>/dev/null || true + [[ -n $holder_pid ]] && wait "$holder_pid" 2>/dev/null || true + rm -rf "$test_tmp" +} +trap cleanup EXIT + +session_root="$test_tmp/session" +runtime_dir="$test_tmp/runtime" +mock_bin="$test_tmp/bin" +marker="$test_tmp/restart-progressed" +ready="$test_tmp/lock-ready" +mkdir -p "$session_root/shell" "$runtime_dir" "$mock_bin" +touch "$session_root/shell/shell.qml" + +cat >"$mock_bin/systemctl" <<'SH' +#!/bin/bash +if [[ ${1:-} == "--user" && ${2:-} == "show-environment" ]]; then + printf 'OMARCHY_PATH=%s\n' "$OMARCHY_TEST_SESSION_PATH" +else + exit 1 +fi +SH + +for command in omarchy-hyprland-session-locked omarchy-shell quickshell hyprctl; do + cat >"$mock_bin/$command" <<'SH' +#!/bin/bash +touch "$OMARCHY_TEST_MARKER" +exit 99 +SH + chmod +x "$mock_bin/$command" +done +chmod +x "$mock_bin/systemctl" + +( + exec 9>"$runtime_dir/omarchy-restart-shell.lock" + flock 9 + touch "$ready" + sleep 30 +) & +holder_pid=$! + +for (( attempt = 0; attempt < 100; attempt++ )); do + [[ -f $ready ]] && break + sleep 0.01 +done +[[ -f $ready ]] || fail "test acquired the shell restart lock" + +PATH="$mock_bin:$PATH" \ +OMARCHY_PATH="$session_root" \ +XDG_RUNTIME_DIR="$runtime_dir" \ +HYPRLAND_INSTANCE_SIGNATURE=test \ +OMARCHY_TEST_SESSION_PATH="$session_root" \ +OMARCHY_TEST_MARKER="$marker" \ + timeout 1 "$ROOT/bin/omarchy-restart-shell" || + fail "overlapping shell restart is coalesced successfully" + +[[ ! -e $marker ]] || fail "overlapping shell restart does not enter the restart sequence" +pass "overlapping shell restart is coalesced before it can kill the replacement shell"