code-mode: clean up terminal cell dispatch gates#29310
Conversation
| } | ||
|
|
||
| fn cell_closed(&self, cell_id: &CellId) { | ||
| self.close_cell(cell_id); |
There was a problem hiding this comment.
This still has a close-before-ready race...
A fast yield_control(); ...complete... cell can call cell_closed before mark_cell_ready_for_dispatch, so this removes nothing. Then, readiness creates a fresh gate, and the initial observe only returns thebuffered yield
18fb5ed to
5ff1f52
Compare
2626759 to
64053a7
Compare
5ff1f52 to
f3de9a8
Compare
64053a7 to
b9578cb
Compare
f3de9a8 to
7545b94
Compare
b9578cb to
cb43921
Compare
7545b94 to
2f2de77
Compare
cb43921 to
9139181
Compare
56dffe9 to
df256e8
Compare
9139181 to
86200fe
Compare
df256e8 to
5baec19
Compare
86200fe to
28b4f57
Compare
5baec19 to
b79d2d5
Compare
28b4f57 to
2cb2170
Compare
b79d2d5 to
f12f4c9
Compare
2cb2170 to
474422b
Compare
f12f4c9 to
fb775b6
Compare
474422b to
abbe7df
Compare
fb775b6 to
1b0c2f7
Compare
abbe7df to
28536e0
Compare
| DispatchGate::ClosedBeforeReady => {} | ||
| }, | ||
| Entry::Vacant(entry) => { | ||
| entry.insert(DispatchGate::ClosedBeforeReady); |
There was a problem hiding this comment.
Vacant is not necessarily “closed before ready.” InitialObservationGuard already closes the ready gate before asynchronously terminating; the later cell_closed (or the guard if runtime close wins) lands here and installs a marker after the only readiness call has passed.
Nothing consumes it, so cancelled initial observations still leak one entry each.
Can we linearize the ready, terminal, and consumer-finished events and cover this cancellation path?
| .map(|observer| (observer.mode, observer.response_tx)), | ||
| ) { | ||
| ); | ||
| if completion_committed { |
There was a problem hiding this comment.
A successful commit can still produce CompletionDelivery::Rejected: terminate() may claim CellPhase::Completed between the commit await and deliver_completion. Sound pretty racy to me
Why
A yielded cell can complete in the background while its terminal result remains buffered. Waiting for another observation or actor shutdown before cleaning Core's dispatch state leaves one gate allocated per unobserved completion. Terminal notification can also race ahead of Core installing that gate.
What
cell_closed(&CellId) -> ()on the runtime and session delegate contracts as a synchronous, non-blocking terminal notification.ObserveOutcomeremains buffered.ObserveOutcome::CompletedorObserveOutcome::Terminatedbefore invoking terminal cleanup.Host/Core sequence
sequenceDiagram participant Core participant Host Host->>Host: commit terminal CellEvent opt observer is attached Host-->>Core: ObserveOutcome::Completed or ::Terminated end Host-->>Core: cell_closed(&CellId) -> () alt dispatch gate already exists Core->>Core: remove gate else readiness is not registered yet Core->>Core: retain closed marker Core->>Core: later readiness consumes marker end Note over Host,Core: cell_closed has no acknowledgement or retryStack boundary
This notification releases Core dispatch state; it does not acknowledge delivery of the buffered wire outcome. Observation replay storage is addressed separately by #29398 and #29401.
Validation
just test -p codex-code-modejust test -p codex-core code_modeStack parent: #29292.