|
64 | 64 | # |
65 | 65 | # - Every entry-point call drops a ticket file named by arrival time into |
66 | 66 | # `<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). |
70 | 107 | # |
71 | 108 | # - A LEGACY free-hand `flock` user (an agent still on the old line, or any |
72 | 109 | # script that locks this file directly) contends on the same file with the |
@@ -3772,6 +3809,51 @@ FAKEDATE |
3772 | 3809 | "$(bash "$SELF" --status 2>&1 | grep -c 'blocks nobody')" 1 |
3773 | 3810 | rm -rf "$sq" |
3774 | 3811 |
|
| 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 | + |
3775 | 3857 | QUEUE_DIR="${tmp}/q" |
3776 | 3858 | HOLDER_FILE="${tmp}/holder" |
3777 | 3859 | mkdir -p "$QUEUE_DIR" |
|
0 commit comments