Skip to content

Commit 2dec957

Browse files
claude[bot]claude
andauthored
docs(pm): os-verify-lock's ordering header states headship's one measured exception (#15760)
The ordering block justified head-only `flock` with "headship is stable — tickets sort by arrival and the ones ahead of you can only disappear" (47c1021, #9921). True when written, and overtaken six days later by the slot mechanism (c312a56, #12335): a parked slot keeps its ORIGINAL arrival stamp and re-enters the live queue at that stamp on resume, so a ticket ahead of you can now APPEAR. Nothing sent that change back to re-read the sentence. Both measured halves are now written down (#15581): the exception, and the bound on it — a resuming slot cannot displace an incumbent head already blocked inside `flock`, because that caller left the queue loop and never re-reads its position, so the slip is exactly one position and is spent only against callers that are merely polling. The one-position slip is recorded as ACCEPTED with its price attached (one holder's hold: p50 95s, p90 418s, max 643s on the ledger as read for #14944) rather than quietly corrected, and the known unfixed boundary is stated: re-checking headship per 30s slice does not close the window, and `flock -n` plus polling is a contract change to the ordering layer and the maintainer's, not a fix on sight. Four self-test cases pin both halves, using the battery's existing multi-party harness on its PRIVATE lock (OS_VERIFY_LOCK_FILE, from which the queue, ledger and boots paths derive) — the shared /tmp lock is not touched. No behaviour change: the diff is the header block plus self-test cases. Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
1 parent 7dafaae commit 2dec957

1 file changed

Lines changed: 85 additions & 3 deletions

File tree

scripts/pm/os-verify-lock.sh

100755100644
Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,46 @@
6464
#
6565
# - Every entry-point call drops a ticket file named by arrival time into
6666
# `<lock>.q/`. Only the ticket at the HEAD of the live queue ever calls
67-
# `flock`; everyone else polls. Head-only means no thundering herd, and
68-
# headship is stable — tickets sort by arrival and the ones ahead of you can
69-
# only disappear.
67+
# `flock`; everyone else polls. Head-only means no thundering herd.
68+
#
69+
# ⚠️ HEADSHIP IS STABLE AMONG ENTRY-POINT CALLERS WITH EXACTLY ONE MEASURED
70+
# EXCEPTION — A RESUMING SLOT. This bullet used to end "headship is stable
71+
# — tickets sort by arrival and the ones ahead of you can only disappear."
72+
# That sentence was TRUE WHEN WRITTEN (47c1021b4, #9921) and was overtaken
73+
# six days later by the slot mechanism (c312a562e, #12335), which had no
74+
# reason to come back here and re-read it. A parked slot keeps its ORIGINAL
75+
# arrival stamp and re-enters the live queue at that stamp on resume ⇒
76+
# ⭐ A TICKET AHEAD OF YOU CAN APPEAR, not only disappear (#15581).
77+
#
78+
# ⭐ AND THE EXCEPTION IS BOUNDED — the half that keeps this from being a
79+
# starvation mechanism. A resuming slot cannot DISPLACE an incumbent head:
80+
# once a caller leaves the queue loop at `pos == 1` it is blocked inside
81+
# `flock` and never re-reads its position, so the ordering layer has nothing
82+
# left to ask it to yield with. The resumed place is delivered against every
83+
# caller that is merely POLLING, and against nobody else. ⇒ The slip is
84+
# EXACTLY ONE POSITION, and only ever to the caller already inside `flock`;
85+
# FIFO among entry-point callers is otherwise intact. Measured three-party
86+
# trace (#15581): acquisition went newcomer A (already in `flock`), then the
87+
# returner, then newcomer B — though the returner's stamp was older than
88+
# both. `--self-test` pins both halves.
89+
#
90+
# ⛔ THAT ONE-POSITION SLIP IS ACCEPTED, NOT UNNOTICED, and the cost is
91+
# written down so a later reader knows it was measured rather than missed.
92+
# The price is one holder's hold: on the ledger AS READ FOR #14944, p50 95s,
93+
# p90 418s, max 643s — a reading of that ledger at that time, not a constant,
94+
# and not something this script measures. On a deep queue that is enough to
95+
# turn a would-be acquisition into an exit 99, on precisely the population
96+
# the slot exists to protect: the caller that obeyed the cap, left, and
97+
# came back.
98+
#
99+
# ⛔ THE OBVIOUS REPAIR DOES NOT CLOSE THE WINDOW, which is why none is
100+
# attempted here. Having the incumbent head re-check headship after each
101+
# failed `flock` slice still loses: `SLICE_S` is 30s, so a slot resuming
102+
# inside the last slice before a release arrives after the check it would
103+
# have had to win. Tightening further means abandoning blocking `flock` for
104+
# `flock -n` plus polling — a CONTRACT CHANGE to the ordering layer, which
105+
# is the maintainer's and not a fix on sight. Known, unfixed boundary,
106+
# recorded rather than closed (#15581).
70107
#
71108
# - A LEGACY free-hand `flock` user (an agent still on the old line, or any
72109
# script that locks this file directly) contends on the same file with the
@@ -3772,6 +3809,51 @@ FAKEDATE
37723809
"$(bash "$SELF" --status 2>&1 | grep -c 'blocks nobody')" 1
37733810
rm -rf "$sq"
37743811

3812+
# ⭐ THE ONE MEASURED EXCEPTION TO HEADSHIP (#15581) — pinned from both sides,
3813+
# because only the pair is the claim the ordering block at the top of this
3814+
# file now makes. That block used to say the tickets ahead of you "can only
3815+
# disappear"; a resuming slot makes one APPEAR. What keeps that from being a
3816+
# starvation mechanism is the second half: the ticket that appears cannot take
3817+
# the place of a caller ALREADY BLOCKED INSIDE `flock`, because that caller
3818+
# left the queue loop and never re-reads its position. So the slip is exactly
3819+
# one position, and it is spent only against callers that are merely polling.
3820+
#
3821+
# Three parties behind one holder. Everything here runs on the PRIVATE lock
3822+
# this suite exports (OS_VERIFY_LOCK_FILE="$L"), from which the queue, holder,
3823+
# ledger and boots paths are all derived — the shared /tmp lock is never named
3824+
# in this file's tests and is not touched by this case.
3825+
local ord3="${tmp}/order3" qsnap="${tmp}/qsnap" ord3s
3826+
: > "$ord3"
3827+
bash "$SELF" -c 'sleep 8' > /dev/null 2>&1 &
3828+
local h3=$!
3829+
sleep 1.2
3830+
# The polite caller: obeys a small budget, times out, and PARKS its place.
3831+
OS_VERIFY_LOCK_SLOT=polite OS_VERIFY_LOCK_WAIT=2 bash "$SELF" -c true > /dev/null 2>&1
3832+
# Newcomer A: the only live ticket, so it takes the head and blocks in `flock`.
3833+
bash "$SELF" -c "printf '%s' A >> '$ord3'" > /dev/null 2>&1 &
3834+
sleep 0.5
3835+
# Newcomer B: position 2, polling — it never reaches `flock` while A is there.
3836+
bash "$SELF" -c "printf '%s' B >> '$ord3'" > /dev/null 2>&1 &
3837+
sleep 0.5
3838+
# The returner: resumes the parked place, whose stamp is older than either
3839+
# newcomer's, so it re-enters the live queue AHEAD of both.
3840+
OS_VERIFY_LOCK_SLOT=polite bash "$SELF" -c "printf '%s' R >> '$ord3'" > /dev/null 2>&1 &
3841+
sleep 0.5
3842+
# Read the live queue while A is still inside `flock` and the holder still holds.
3843+
ls "$sq" 2> /dev/null | sort | head -1 > "$qsnap"
3844+
wait "$h3" 2> /dev/null
3845+
wait
3846+
ord3s="$(< "$ord3")"
3847+
st_case 'a resumed slot re-enters AHEAD of newcomers that arrived while it was parked' \
3848+
"$([[ "$(< "$qsnap")" == *-spolite ]] && echo yes || echo no)" yes
3849+
st_case 'so the resumed place is delivered against a caller that is merely polling' \
3850+
"$([[ "$ord3s" == *R*B* ]] && echo yes || echo no)" yes
3851+
st_case 'but a resuming slot does NOT displace the incumbent head inside flock' \
3852+
"${ord3s:0:1}" A
3853+
st_case 'so the slip is exactly one position — three parties, one overtake' \
3854+
"$ord3s" ARB
3855+
rm -rf "$sq"
3856+
37753857
QUEUE_DIR="${tmp}/q"
37763858
HOLDER_FILE="${tmp}/holder"
37773859
mkdir -p "$QUEUE_DIR"

0 commit comments

Comments
 (0)