🏅 Sync 2026-10-30 20:56:14

This commit is contained in:
github-actions[bot] committed 2026-10-30 20:56:14 +08:00
1 parent f470897df5
commit 2d2368945f
32 files changed
+2793 -2828

No files matched your search

@@ -36,25 +36,60 @@ write_state() {
# Only one worker at a time: a second click while a reload is running would
# queue another network reload on top of an unfinished one. mkdir is atomic and
# is the only portable primitive available here; a test-then-write pair lets
# two workers through at the same moment. Stale locks are recognised by age so
# a recycled pid or a killed worker cannot block forever.
# two workers through at the same moment. Stale locks are recognised by age and
# by a dead pid so a killed worker cannot block forever, but a lock that is
# merely young is never taken over.
LOCK_DIR="$LOCK_FILE.d"
lock_age() {
local ts now
ts=$(cat "$LOCK_DIR/ts" 2>/dev/null)
case "$ts" in ''|*[!0-9]*) echo 999999; return ;; esac
# A missing or unreadable timestamp means mkdir just won the race and the
# holder has not written pid/ts yet. Reporting that window as ancient let a
# late worker rip the lock away mid start up, so two reloads ran at once:
# the very thing this lock exists to prevent. It is a fresh lock instead.
#
# The directory mtime covers the other side of the same window: a worker
# killed between mkdir and the ts write would otherwise leave an ageless
# lock that nobody ever takes over.
case "$ts" in
''|*[!0-9]*)
ts=$(date -r "$LOCK_DIR" +%s 2>/dev/null)
case "$ts" in ''|*[!0-9]*) echo 0; return ;; esac
;;
esac
now=$(date +%s)
echo $((now - ts))
}
# kill -0 is the portable liveness probe available in busybox ash. No readable
# pid means the holder never got to write one, which is what a lock left behind
# by a crash looks like, so it counts as gone.
lock_holder_alive() {
local pid
pid=$(cat "$LOCK_DIR/pid" 2>/dev/null)
case "$pid" in ''|*[!0-9]*) return 1 ;; esac
kill -0 "$pid" 2>/dev/null
}
# Returns 0 when this process owns a freshly created lock directory. Deletes are
# not in place: rm -rf then mkdir leaves a window where two workers both think
# they won. Renaming the old directory is a single step that only one worker can
# win; the loser reports busy. mkdir can still lose to a third worker.
take_over_stale_lock() {
local aside
[ "$(lock_age)" -gt 300 ] || return 1
lock_holder_alive && return 1
aside="$LOCK_DIR.stale.$$"
mv "$LOCK_DIR" "$aside" 2>/dev/null || return 1
rm -rf "$aside"
mkdir "$LOCK_DIR" 2>/dev/null
}
if ! mkdir "$LOCK_DIR" 2>/dev/null; then
if [ "$(lock_age)" -gt 300 ]; then
# the holder is gone; take the lock over
rm -rf "$LOCK_DIR"
mkdir "$LOCK_DIR" 2>/dev/null || exit 0
else
# someone else is already reloading: say so instead of leaving the
# status file at "queued" and letting the UI time out
if ! take_over_stale_lock; then
# someone else is already reloading, or another worker won the takeover:
# say so instead of leaving the status file at "queued" and letting the
# UI time out
write_state busy locked "Another firewall reload is already running"
exit 0
fi