Raised by Codex on #705 (catchup.go, awaitSupersededPoll).
The mechanism is real and is exactly what the function already documents. What is not
true is the thread's premise that a promised bound is being missed: the code publishes
two staleness windows, deliberately, and explains why. This issue carries the design
question underneath it, which #705 flagged as wanting a §23 conversation rather than a
patch.
The mechanism
awaitSupersededPoll has a fixed deadline read from the injected clock at the deferral
instant, but no timer of its own. It wakes on the staleness timer and on re-arms, and
checks the deadline after every wake:
select {
case r := <-done: ...
case <-l.runCtx.Done(): ...
case <-at.lc.stale.rearmed():
case <-staleTimer.C():
if _, ok := at.lc.stale.evaluate(staleGen); ok { ... }
}
if !l.cfg.clock.Now().Before(deadline) {
return pollAttempt{superseded: true}
}
So the wait wakes on the window's cadence, not the deadline's. A frame arriving shortly
before the deadline re-arms staleness; the next wake is a full window later; the lapse is
observed then. Worst case is just under two windows — and it cannot exceed two, because by
the first re-armed firing the clock is necessarily past a deadline set one window after
the deferral, so a second re-arm is never waited out.
Codex's example is right on the mechanism and wrong on the consequence it names:
"advancing virtual time to the promised bound leaves the stalled poll active" assumes a
promised bound of one window. Nothing promises one. §23 puts the obligation on the seam
("Prompt return required"), and the function's own comment publishes two.
The question
Should the superseded-poll wait get a fixed grace phase — immune to frame resets and to
the staleness suspension rule — instead of borrowing the staleness window?
For: the bound would be the one stated; virtual-time tests could advance to it exactly;
and the "two windows" caveat would leave a public timing contract.
Against, and the reason #705 did not do it: §23 pins exactly six kebab-case timer kinds
and every state's exact timer set, both asserted by the cross-SDK fixtures. A dedicated
timer is a seventh kind — a spec change across six SDKs for what is otherwise a Go-local
wait. Shortening the re-armed window instead is not available either: a staleness window
that is not a staleness window is a different bug.
Related
#758 is a different cause in the same wait — pollPage's select has no staleness case at
all, so a silently half-open socket is unobservable before any frame is deferred. #760
notes that the same single-slot deferral is the obstacle in both. These three want deciding
together, and a grace phase is one of the shapes #758 costs.
Also noted in #705 and worth folding into the same decision: SPEC's published raw-frame
ceiling still says pump depth, while drain's budget is pumpDepth+1 and that +1 is
load-bearing. That is a spec correction across six SDKs rather than a Go change.
Raised by Codex on #705 (
catchup.go,awaitSupersededPoll).The mechanism is real and is exactly what the function already documents. What is not
true is the thread's premise that a promised bound is being missed: the code publishes
two staleness windows, deliberately, and explains why. This issue carries the design
question underneath it, which #705 flagged as wanting a §23 conversation rather than a
patch.
The mechanism
awaitSupersededPollhas a fixed deadline read from the injected clock at the deferralinstant, but no timer of its own. It wakes on the staleness timer and on re-arms, and
checks the deadline after every wake:
So the wait wakes on the window's cadence, not the deadline's. A frame arriving shortly
before the deadline re-arms staleness; the next wake is a full window later; the lapse is
observed then. Worst case is just under two windows — and it cannot exceed two, because by
the first re-armed firing the clock is necessarily past a deadline set one window after
the deferral, so a second re-arm is never waited out.
Codex's example is right on the mechanism and wrong on the consequence it names:
"advancing virtual time to the promised bound leaves the stalled poll active" assumes a
promised bound of one window. Nothing promises one. §23 puts the obligation on the seam
("Prompt return required"), and the function's own comment publishes two.
The question
Should the superseded-poll wait get a fixed grace phase — immune to frame resets and to
the staleness suspension rule — instead of borrowing the staleness window?
For: the bound would be the one stated; virtual-time tests could advance to it exactly;
and the "two windows" caveat would leave a public timing contract.
Against, and the reason #705 did not do it: §23 pins exactly six kebab-case timer kinds
and every state's exact timer set, both asserted by the cross-SDK fixtures. A dedicated
timer is a seventh kind — a spec change across six SDKs for what is otherwise a Go-local
wait. Shortening the re-armed window instead is not available either: a staleness window
that is not a staleness window is a different bug.
Related
#758 is a different cause in the same wait —
pollPage's select has no staleness case atall, so a silently half-open socket is unobservable before any frame is deferred. #760
notes that the same single-slot deferral is the obstacle in both. These three want deciding
together, and a grace phase is one of the shapes #758 costs.
Also noted in #705 and worth folding into the same decision: SPEC's published raw-frame
ceiling still says
pump depth, whiledrain's budget ispumpDepth+1and that+1isload-bearing. That is a spec correction across six SDKs rather than a Go change.