mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-09 06:28:59 +02:00
fix(gstack-memorable): stale-lock takeover by atomic rename; comments match behaviour
Two contenders that both saw a stale lock could both reclaim it with rm -rf; the settings-hook idiom (mv to a private name, exactly one winner) is used instead. The hook-manager fallback comment now says every verb falls back. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
af72e3d4ff
commit
abf5d96400
+13
-5
@@ -59,7 +59,9 @@ if [ ! -x "$CANONICAL_GSTACK_ROOT/bin/gstack-session-update" ] \
|
||||
fi
|
||||
HOOK_CMD_PATH="$CANONICAL_GSTACK_ROOT/$HOOK_REL"
|
||||
# Mutations go through the CANONICAL hook manager so the code that registers
|
||||
# is the code that will run; status falls back to this tree's copy for reads.
|
||||
# is the code that will run. Every verb falls back to this tree's copy when the
|
||||
# canonical one is missing (enable cannot get past compat_check then; disable
|
||||
# and status must still work against a half-removed install).
|
||||
SETTINGS_HOOK="$CANONICAL_GSTACK_ROOT/bin/gstack-settings-hook"
|
||||
[ -x "$SETTINGS_HOOK" ] || SETTINGS_HOOK="$SCRIPT_DIR/gstack-settings-hook"
|
||||
EGRESS_BIN="$CANONICAL_GSTACK_ROOT/bin/gstack-egress"
|
||||
@@ -101,7 +103,7 @@ _lock_release() {
|
||||
}
|
||||
_lock_acquire() {
|
||||
mkdir -p "$STATE_DIR/locks" 2>/dev/null || { _err "cannot create $STATE_DIR/locks"; return 5; }
|
||||
local tries=0 mtime now
|
||||
local tries=0 mtime now stale
|
||||
while ! mkdir "$LOCK_DIR" 2>/dev/null; do
|
||||
tries=$((tries + 1))
|
||||
# Staleness from the directory's own mtime (set atomically by the holder's
|
||||
@@ -112,7 +114,13 @@ _lock_acquire() {
|
||||
mtime="$(stat -c %Y "$LOCK_DIR" 2>/dev/null || stat -f %m "$LOCK_DIR" 2>/dev/null || echo "")"
|
||||
case "$mtime" in *[!0-9]*|"") mtime="" ;; esac
|
||||
now="$(date +%s)"
|
||||
if [ -n "$mtime" ] && [ $((now - mtime)) -gt "$LOCK_STALE_S" ]; then rm -rf "$LOCK_DIR"; continue; fi
|
||||
if [ -n "$mtime" ] && [ $((now - mtime)) -gt "$LOCK_STALE_S" ]; then
|
||||
# Atomic rename: exactly one contender reclaims a stale lock; the loser
|
||||
# loops and re-contends against the winner's fresh mkdir.
|
||||
stale="$LOCK_DIR.stale.$$-$RANDOM"
|
||||
if mv "$LOCK_DIR" "$stale" 2>/dev/null; then rm -rf "$stale" 2>/dev/null || true; fi
|
||||
continue
|
||||
fi
|
||||
if [ "$tries" -ge "$LOCK_TRIES" ]; then _err "another gstack-memorable is running (lock $LOCK_DIR); try again"; return 5; fi
|
||||
sleep "$LOCK_SLEEP"
|
||||
done
|
||||
@@ -205,7 +213,7 @@ enable_bridge() {
|
||||
[ -x "$SETTINGS_HOOK" ] || { _err "missing hook manager: $SETTINGS_HOOK"; return 1; }
|
||||
[ -x "$GSTACK_CONFIG" ] || { _err "missing $GSTACK_CONFIG"; return 1; }
|
||||
if [ "$IS_WINDOWS" -eq 1 ]; then
|
||||
_err "Windows is not supported by the Memorable bridge yet (no way to contain the vendor process); tracked in TODOS.md D21"
|
||||
_err "Windows is not supported by the Memorable bridge yet (no way to contain the vendor process); tracked in TODOS.md: Windows support for the Memorable bridge (D21)"
|
||||
return 1
|
||||
fi
|
||||
vendor="$(resolve_memorable)" || { _err "Memorable CLI not found (checked $RESOLUTION_ORDER). Install it yourself: npm i -g memorable-cli. gstack never installs it."; return 1; }
|
||||
@@ -337,7 +345,7 @@ status_bridge() {
|
||||
esac
|
||||
if [ "$gate" = "on" ] && [ "$REG_STATE" = "none" ]; then echo "mismatch: gate on, no hook registered (run: gstack-memorable enable)"; fi
|
||||
if [ "$gate" != "on" ] && { [ "$REG_STATE" = "gstack" ] || [ "$REG_STATE" = "both" ]; }; then echo "mismatch: hook registered but gate is '$gate' (hook is inert; run: gstack-memorable disable to remove it)"; fi
|
||||
if [ "$IS_WINDOWS" -eq 1 ]; then echo "platform: Windows is not supported by this bridge yet (TODOS.md D21)"; fi
|
||||
if [ "$IS_WINDOWS" -eq 1 ]; then echo "platform: Windows is not supported by this bridge yet (TODOS.md: Windows support for the Memorable bridge, D21)"; fi
|
||||
if [ -x "$EGRESS_BIN" ] && command -v bun >/dev/null 2>&1; then
|
||||
# Count the filtered array, not a formatting artefact of the pretty-printed JSON.
|
||||
n="$("$EGRESS_BIN" list --sink "$SINK" --json 2>/dev/null | bun -e 'const a=JSON.parse(require("fs").readFileSync(0,"utf8")||"[]");console.log(Array.isArray(a)?a.length:0)' 2>/dev/null)"
|
||||
|
||||
Reference in New Issue
Block a user