#!/usr/bin/env bash
# Ghost-flock detection, holder stamping and index-independent admission.
# A ghost — a held flock with no fd holder — cannot be produced on demand (the
# kernel releases a real holder's lock), so the holder lookup is shimmed while a
# live process holds the actual lock. Cases without the shim use the real lsof.
set -uo pipefail

BS="$HOME/.claude/lib/buildslot.sh"
TMP=$(mktemp -d "$HOME/.cache/bs-ghost-XXXXXX")
HOLDERS=()
cleanup() {
  local pid
  for pid in ${HOLDERS[@]+"${HOLDERS[@]}"}; do kill "$pid" 2>/dev/null; done
  rm -rf "$TMP"
}
trap cleanup EXIT
unset BUILD_SLOT_HELD BUILD_SLOT_PRIORITY BUILD_SLOTS BUILD_SLOT_FLOOR BUILD_SLOT_FIXED BUILD_SLOT_TIMEOUT
PASS=0; FAIL=0

check() {
  local name="$1" want="$2" got="$3" extra="${4:-0}"
  if [ "$want" = "$got" ] && [ "$extra" = "0" ]; then
    echo "PASS $name"; PASS=$((PASS+1))
  else
    echo "FAIL $name (want=$want got=$got extra=$extra)"; FAIL=$((FAIL+1))
  fi
}

hold_slot() {
  local dir="$1" index="$2" i
  mkdir -p "$dir"
  bash -c 'exec {fd}>>"$1"; flock -x "$fd" || exit 1; : >"$2"; sleep 300' _ "$dir/slot-$index.lock" "$dir/held-$index" &
  HOLDERS+=("$!")
  for i in $(seq 1 100); do
    [ -f "$dir/held-$index" ] && return 0
    sleep 0.1
  done
  return 1
}

# Like hold_slot, but the holder also writes its own live pid+starttime stamp
# (the shape a real buildslot acquire leaves behind).
hold_slot_stamped() {
  local dir="$1" index="$2" i
  mkdir -p "$dir"
  bash -c 'exec {fd}>>"$1"; flock -x "$fd" || exit 1
    raw=$(</proc/$$/stat); rest=${raw##*") "}; read -r -a f <<< "$rest"
    printf "%s\t%s\t%s\n" "$$" "${f[19]}" "test-holder" > "$1"
    : >"$2"; sleep 300' _ "$dir/slot-$index.lock" "$dir/held-$index" &
  HOLDERS+=("$!")
  for i in $(seq 1 100); do
    [ -f "$dir/held-$index" ] && return 0
    sleep 0.1
  done
  return 1
}

# no fd holder anywhere: the ghost signature
BIN="$TMP/bin"; mkdir -p "$BIN"
printf '#!/usr/bin/env bash\nexit 1\n' > "$BIN/lsof"
chmod +x "$BIN/lsof"

# ghost slot is rotated: the run proceeds on a fresh inode
D="$TMP/ghost"; hold_slot "$D" 0 || { echo "FAIL holder setup"; exit 1; }
before=$(stat -c %i "$D/slot-0.lock")
out=$(PATH="$BIN:$PATH" BUILD_SLOT_DIR="$D" BUILD_SLOTS=1 BUILD_SLOT_TIMEOUT=20 \
  "$BS" bash -c 'echo RAN' 2>"$TMP/err-ghost")
rc=$?
after=$(stat -c %i "$D/slot-0.lock" 2>/dev/null || echo gone)
rotated=0; [ "$before" = "$after" ] && rotated=1
ran=1; [ "$out" = "RAN" ] && ran=0
said=1; grep -q "cleared ghost lock .* (held, no fd holder)" "$TMP/err-ghost" && said=0
check "ghost-slot-rotated-and-admitted" 0 "$rc" "$((rotated || ran || said))"

# a real holder is visible to lsof and must never be rotated out from under
D="$TMP/live"; hold_slot "$D" 0 || { echo "FAIL holder setup"; exit 1; }
before=$(stat -c %i "$D/slot-0.lock")
BUILD_SLOT_DIR="$D" BUILD_SLOTS=1 BUILD_SLOT_TIMEOUT=4 \
  "$BS" bash -c 'echo RAN' >"$TMP/out-live" 2>/dev/null
rc=$?
after=$(stat -c %i "$D/slot-0.lock")
kept=0; [ "$before" = "$after" ] || kept=1
quiet=0; [ -s "$TMP/out-live" ] && quiet=1
check "live-holder-blocks-and-is-not-rotated" 75 "$rc" "$((kept || quiet))"

# holder stamps itself into the slot it acquired
D="$TMP/stamp"; mkdir -p "$D"
out=$(BUILD_SLOT_DIR="$D" BUILD_SLOTS=1 BUILD_SLOT_TIMEOUT=10 \
  "$BS" bash -c 'read -r p _ < "$1/slot-0.lock"; [ "$p" = "$$" ] && echo STAMP_OK' _ "$D" 2>/dev/null)
check "holder-stamped-into-slot-file" "STAMP_OK" "$out"

# admission counts held slots, not indexes: eff=2 with slots 0 and 2 held is full
D="$TMP/spread"; hold_slot "$D" 0 || { echo "FAIL holder setup"; exit 1; }
hold_slot "$D" 2 || { echo "FAIL holder setup"; exit 1; }
printf 'cpu 100 0 100 100 0 0 0 0 0 0\n' > "$TMP/stat-static"
BUILD_SLOT_DIR="$D" BUILD_SLOT_STAT="$TMP/stat-static" BUILD_SLOTS=4 BUILD_SLOT_FLOOR=2 BUILD_SLOT_TIMEOUT=4 \
  "$BS" bash -c 'echo RAN' >"$TMP/out-spread" 2>/dev/null
rc=$?
quiet=0; [ -s "$TMP/out-spread" ] && quiet=1
check "held-count-caps-admission-across-indexes" 75 "$rc" "$quiet"

# a free slot above a held index is still admitted
D="$TMP/free"; hold_slot "$D" 0 || { echo "FAIL holder setup"; exit 1; }
out=$(BUILD_SLOT_DIR="$D" BUILD_SLOTS=2 BUILD_SLOT_FIXED=1 BUILD_SLOT_TIMEOUT=10 \
  "$BS" bash -c 'echo RAN' 2>/dev/null)
check "free-slot-above-held-index-admitted" "RAN" "$out"

# a live stamp must veto rotation even when lsof reads empty (concurrent
# waiters' probe fds made lsof-only detection rotate live holders / wedge)
D="$TMP/livestamp"; hold_slot_stamped "$D" 0 || { echo "FAIL holder setup"; exit 1; }
before=$(stat -c %i "$D/slot-0.lock")
PATH="$BIN:$PATH" BUILD_SLOT_DIR="$D" BUILD_SLOTS=1 BUILD_SLOT_TIMEOUT=6 \
  "$BS" bash -c 'echo RAN' >"$TMP/out-livestamp" 2>/dev/null
rc=$?
after=$(stat -c %i "$D/slot-0.lock")
kept=0; [ "$before" = "$after" ] || kept=1
quiet=0; [ -s "$TMP/out-livestamp" ] && quiet=1
check "live-stamp-vetoes-rotation-despite-empty-lsof" 75 "$rc" "$((kept || quiet))"

# a dead stamp on a held flock is the real-incident ghost: rotated, admitted
D="$TMP/deadstamp"; hold_slot "$D" 0 || { echo "FAIL holder setup"; exit 1; }
printf '4194000\t1\tdead-holder\n' > "$D/slot-0.lock"
before=$(stat -c %i "$D/slot-0.lock")
out=$(PATH="$BIN:$PATH" BUILD_SLOT_DIR="$D" BUILD_SLOTS=1 BUILD_SLOT_TIMEOUT=20 \
  "$BS" bash -c 'echo RAN' 2>/dev/null)
rc=$?
after=$(stat -c %i "$D/slot-0.lock" 2>/dev/null || echo gone)
rotated=0; [ "$before" = "$after" ] && rotated=1
ran=1; [ "$out" = "RAN" ] && ran=0
check "dead-stamp-ghost-rotated-and-admitted" 0 "$rc" "$((rotated || ran))"

# stamp-based reentrancy: an env-scrubbed descendant of a slot holder must
# inherit the slot instead of deadlocking behind its own ancestor (lsof-free)
D="$TMP/reent"
out=$(BUILD_SLOT_DIR="$D" BUILD_SLOTS=1 BUILD_SLOT_TIMEOUT=10 \
  "$BS" bash -c 'env -u BUILD_SLOT_HELD "$1" bash -c "echo NESTED_OK"' _ "$BS" 2>/dev/null)
check "env-scrubbed-descendant-inherits-slot" "NESTED_OK" "$out"

echo
echo "passed=$PASS failed=$FAIL"
[ "$FAIL" -eq 0 ]
