Development record
The load gate's stand-back window (the open pause plus the start tolerance) is measured from the look, not from the waiter's own re-check read, so a waiter whose path from that read to starting its mark is slow under load (about 2.5 s) discards a concurrent lower-pid opener it did see, and both run their commands, which can bring back two preflights each waiting on the other's running waiter; no test pins the window's bound.
issueopenminoriss-2610162306239284
The load gate's stand-back window (the open pause plus the start tolerance) is measured from the look, not from the waiter's own re-check read, so a waiter whose path from that read to starting its mark is slow under load (about 2.5 s) discards a concurrent lower-pid opener it did see, and both run their commands, which can bring back two preflights each waiting on the other's running waiter; no test pins the window's bound.
Frontmatter
| id | iss-2610162306239284 |
|---|---|
| type | issue |
| lifecycle | open |
| severity | minor |
.abcd/work/issues/open/iss-2610162306239284-the-load-gate-s-stand-back-window-the.md
open on GitHub ↗ · commit history ↗
References1
- itd-2610170634394993Headless agents in parallel coordinate through the shared remote, not conversationrelated
mentions1
- dcn-26101622574775712026-10-16 — Refines dcn-2610162239308903, the load gate's queue (iss-2610161634043273), on the gap it accepted and on how far its no-cycle claim reaches. The gap is larger than that record says. Two waiters whose start and pid disagree, as after the process ids wrap, wait on neither, and across accounts no hold keeps them apart, so re-checks that fall in one moment, before either shows in the process table as running its command, both open, and their commands overlap. Under `--wait -- make preflight` the overlap becomes a stall: each preflight's own gate, the inner waiter that takes its running ancestor's place, still counts every other waiter running its command as ahead, so each waits on the other's outer waiter and both stand until their two-hour timeouts. It needs a pid wrap, two accounts and coinciding re-checks. The mitigation uses no shared file, which iss-2610140638074587 still rules out. The command form shows itself in the table as running its command from the moment it opens, by a short-lived child of the waiter (a /bin/sleep, its mark, stopped once the command has started), waits two seconds (WaitOpenPause) and reads the table once more before it starts its command. If another waiter is then running its command, opened within that pause and the start tolerance, and has the lower pid, this waiter stands back: it stops its mark, gives up its hold, re-draws its jitter and waits again, while the lower pid keeps going. Because each opener is marked from the moment it opens, two that open at the same instant each see the other, and exactly one runs. A waiter stands back at most three times (WaitBackoffCap) and then opens without the look, so no wait goes round for ever. The plain form starts no command and neither marks nor looks. Within one account nothing changes: the hold already admits one. What stays open: a mark that cannot be started, a table that cannot be read at the look, the cap, and a process start slower than the pause. Rejected: having only the lower pid look, since an opener cannot tell that it is the lower of a disagreeing pair; and closing the stall in the inner gate alone, which leaves two plain race runs overlapping. The earlier record's 'whatever mix of code' covers this code and the pid-only waiters built before the start-order change, and nothing else. A waiter still running the branch's first version, which ordered the queue by start alone, waits on a higher pid that started earlier and can deadlock against pid-only waiters, and no change here can break that, since that waiter never runs it. So when this lands, any waiter running that start-only build is stopped and requeued.