fix(shikane): detect silent deadlocks; re-apply profile on sway reload
watchdog: covers both observed deadlock flavours -- the loud one that repeats "Received invalid input AtomicChangeDone at state Discarded", and the silent one that logs nothing and just never leaves VariantInProgress. shikane-recover-on-reload.sh (new, previously referenced by sway/config but never committed): shikanectl reload -> sleep 2 -> watchdog check, falling back to a service restart when reload cannot recover. clamshell-mode.sh: documents why the laptop panel is no longer enabled unconditionally on lid-open, which raced shikane's "External Display" profile (eDP-1 disabled by design) and produced "Atomic commit failed: Device or resource busy" churn.
This commit is contained in:
1 parent
0ceec0b4ab
commit
d1bd6aae48
3 files changed
+43
-9
No files matched your search
@@ -1,4 +1,12 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
# Lid-driven laptop panel handling.
|
||||||
|
#
|
||||||
|
# shikane is authoritative for output layout/scaling
|
||||||
|
# (~/.config/shikane/config.toml + shikane-watchdog.timer). This script used to
|
||||||
|
# run `swaymsg output eDP-1 enable` whenever the lid was open, which on every
|
||||||
|
# `swaymsg reload` raced the shikane "External Display" profile (eDP-1 off) and
|
||||||
|
# contributed to "Atomic commit failed: Device or resource busy" churn.
|
||||||
|
# It now only ever disables.
|
||||||
|
|
||||||
LAPTOP_OUTPUT="eDP-1"
|
LAPTOP_OUTPUT="eDP-1"
|
||||||
LID_STATE_FILE="/proc/acpi/button/lid/LID/state"
|
LID_STATE_FILE="/proc/acpi/button/lid/LID/state"
|
||||||
@@ -6,7 +14,7 @@ LID_STATE_FILE="/proc/acpi/button/lid/LID/state"
|
|||||||
read -r LS < "$LID_STATE_FILE"
|
read -r LS < "$LID_STATE_FILE"
|
||||||
|
|
||||||
case "$LS" in
|
case "$LS" in
|
||||||
*open) swaymsg output "$LAPTOP_OUTPUT" enable ;;
|
|
||||||
*closed) swaymsg output "$LAPTOP_OUTPUT" disable ;;
|
*closed) swaymsg output "$LAPTOP_OUTPUT" disable ;;
|
||||||
|
*open) : ;; # leave to shikane
|
||||||
*) echo "Could not get lid state" >&2 ; exit 1 ;;
|
*) echo "Could not get lid state" >&2 ; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# shikane-recover-on-reload: run after every sway startup/reload
|
||||||
|
# (wired up via exec_always in the sway config).
|
||||||
|
#
|
||||||
|
# Re-applies the shikane profile, then verifies the daemon came out
|
||||||
|
# the other side: if the re-apply hit the commit-failure race, the
|
||||||
|
# watchdog check (2s later) restarts the service to recover.
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
shikanectl reload 2>/dev/null
|
||||||
|
sleep 2
|
||||||
|
exec /home/work/.config/sway/scripts/shikane-watchdog.sh
|
||||||
@@ -3,8 +3,11 @@
|
|||||||
#
|
#
|
||||||
# Bug (shikane 1.1.1): if a variant application is discarded mid-way
|
# Bug (shikane 1.1.1): if a variant application is discarded mid-way
|
||||||
# (e.g. by a failing DP atomic commit), the daemon gets stuck in
|
# (e.g. by a failing DP atomic commit), the daemon gets stuck in
|
||||||
# VariantInProgress/Discarded and keeps logging
|
# VariantInProgress/Discarded. Two flavors observed:
|
||||||
# [WARN shikane::variant] Received invalid input AtomicChangeDone at state Discarded
|
# loud: keeps logging
|
||||||
|
# [WARN shikane::variant] Received invalid input AtomicChangeDone at state Discarded
|
||||||
|
# silent: no WARN at all; the variant state ends at "Discarded" and the
|
||||||
|
# daemon state machine simply never leaves VariantInProgress
|
||||||
# `shikanectl reload` does not recover it; a service restart does
|
# `shikanectl reload` does not recover it; a service restart does
|
||||||
# (re-applies the active profile in ~1s).
|
# (re-applies the active profile in ~1s).
|
||||||
#
|
#
|
||||||
@@ -25,21 +28,31 @@ if [ -n "$latest3" ]; then
|
|||||||
*VariantApplied*)
|
*VariantApplied*)
|
||||||
: ;; # healthy
|
: ;; # healthy
|
||||||
*)
|
*)
|
||||||
# InProgress/Discarded within the last 3 min: only a deadlock
|
# Daemon still InProgress. Deadlock signatures (a legitimate
|
||||||
# if the WARN signature is present (a legitimate slow transition
|
# slow transition has neither):
|
||||||
# has no WARN)
|
# loud: the WARN from the discarded-variant race
|
||||||
|
# silent: variant discarded but daemon never processed it
|
||||||
printf '%s\n' "$logs3" | grep -q 'invalid input' && stuck=1
|
printf '%s\n' "$logs3" | grep -q 'invalid input' && stuck=1
|
||||||
|
if [ "$stuck" -eq 0 ]; then
|
||||||
|
lastvariant=$(printf '%s\n' "$logs3" \
|
||||||
|
| grep 'shikane::variant' | grep 'New variant state' | tail -1)
|
||||||
|
case "$lastvariant" in
|
||||||
|
*Discarded*) stuck=1 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
else
|
else
|
||||||
# No state change in the last 3 min — check the last known state
|
# No state change in the last 3 min — check the last known state
|
||||||
last10=$(journalctl --user -u "$unit" -q --since "-10min" 2>/dev/null \
|
# (unbounded: a stuck daemon may have gone silent long ago,
|
||||||
|
# e.g. across a laptop suspend)
|
||||||
|
lastline=$(journalctl --user -u "$unit" -q -n 500 2>/dev/null \
|
||||||
| grep 'daemon::state_machine' | tail -1)
|
| grep 'daemon::state_machine' | tail -1)
|
||||||
case "$last10" in
|
case "$lastline" in
|
||||||
'' | *VariantApplied*)
|
'' | *VariantApplied*)
|
||||||
: ;; # stable and applied, or nothing to check
|
: ;; # stable and applied, or nothing to check
|
||||||
*)
|
*)
|
||||||
stuck=1 ;; # stuck in-progress/discarded for >3 min
|
stuck=1 ;; # stuck in-progress/discarded, any age
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user