You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
runtime: harden the queue turn record (idempotent release, State panics, retention test) #129
An independent review of #124 (the fix for #123) found three small gaps in the queue turn record. None of them is reachable on a normal path today, so #124 did not change its reviewed head for them.
1. The queue release wrapper is not idempotent
admit returns an idempotent release (admission.go, the admit doc comment and its sync.OnceFunc). Under DispatchDeferred, dispatch in #124 wraps that release so it also calls leaveQueueScope, and the wrapper is not idempotent. A second call would decrement the scope's active count twice and trip the leaveQueueScope assert, which panics in production.
Today each queue delivery calls the wrapper exactly once: either in the resolved or errored branch of dispatchDeferred, or through defer work.releaseAdmission() in the detached tail. Under burst, leaveQueueScope does nothing.
Suggested fix: wrap the deferred release in sync.OnceFunc, or clear entry.scope in leaveQueueScope.
2. A panicking State call can leave a stale waiter behind the record
Traced by reading the code, not tested. Under DispatchSync, suppose State.AcquireLock panics while a queue waiter polls, and net/http recovers the panic. The waiter's pending entry stays. The deferred leaveQueueScope then finds active == 0 with a pending entry, and its assert fires. Later ignored, duplicate or errored deliveries for that scope fire the same assert until some delivery takes the turn or registers as the waiter. On main, the same panic only leaves the stale entry.
The fix for item 1 (clearing entry.scope) does not cover this. One option: clear the delivery's own pending entry in leaveQueueScope before the assert, or make queueForLock clear it in a defer.
3. No test pins the record's retention
No test reads queueTurns, so a missing delete in leaveQueueScope would pass every Go test (the TLA+ model's IdleScopeRetainsNothing catches it). Add a bounded-retention test with an export hook, as BurstScopeCount in export_test.go does for burst.
Docs
ConcurrencyQueue's GoDoc relies on Adapter.ValidateThreadID not blocking, but only the Observer interface states that rule. Add it to ValidateThreadID in types.go.
Owner and trigger
The coder/chat maintainer triages this. Fix it with the next change to queue coordination, or before the queue strategy is called production-grade, whichever comes first.
Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: xhigh
Triage: ready-for-agent. Each item names its fix. Start this after #124 merges, because all four items change code that #124 adds.
Order of work:
Item 3 (retention test) first. It gives the later steps a red check.
Item 1 (wrap the deferred release in sync.OnceFunc).
Item 2 (clear the delivery's own pending entry in leaveQueueScope before the assert). It needs a test: a State whose AcquireLock panics while a waiter polls.
The ValidateThreadID doc line.
Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: medium
Activity
ThomasK33 commentedon Oct 29, 2026
Triage:
ready-for-agent. Each item names its fix. Start this after #124 merges, because all four items change code that #124 adds.Order of work:
sync.OnceFunc).leaveQueueScopebefore the assert). It needs a test: aStatewhoseAcquireLockpanics while a waiter polls.ValidateThreadIDdoc line.Generated with
xum• Model:anthropic:claude-opus-5-5• Thinking:mediumfix(runtime): harden the queue turn record against a double release a…