fix(agentos-sidecar): skip close_session teardown wait when the adapter already exited#1640
Draft
khaled-mansour-zid wants to merge 1 commit into
Draft
Conversation
…er already exited close_session runs SIGTERM -> wait_for_process_exit -> SIGKILL -> wait. That is correct for a live process, but wait_for_process_exit only polls the event stream for a future ProcessExited event and never checks current process state. When the adapter exited out-of-band before close_session (crash / OOM, or a lazily-observed idle-time crash), that event already fired and was drained, so both waits burn the full SESSION_CLOSE_TIMEOUT (~2x total) against a PID that is already gone. Because close_session is serial per sidecar, this also stalls a create_session issued right after it (session recovery / a returning user whose idle session was evicted). Detect the already-gone case from the SIGTERM result — reusing the existing is_adapter_gone_error classification plus the process-table ESRCH / "no such process" the signal path returns for a reaped PID — and skip straight to resource disposal. Live-process behavior is unchanged. Measured createSession-after-guest-death: ~10.4s without this change, ~0.3s with.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
AcpExtension::close_sessioncan block for ~2×SESSION_CLOSE_TIMEOUT(~10s) when theadapter process has already exited out-of-band before the client closes the session. During
that block, because
close_sessionruns serially per sidecar, acreate_sessionissued rightafterward (e.g. session recovery) is stalled behind it.
This PR short-circuits the teardown when the process is already gone, reusing the same
"adapter is gone" classification the prompt path already uses.
Root cause — and why the current shape is otherwise correct
close_sessiondoes the standard graceful-shutdown dance:That is correct for a live process being asked to shut down: SIGTERM, wait for its exit
event, escalate to SIGKILL if it's stubborn.
The gap is the already-dead case.
wait_for_process_exitis purely event-driven — it pollsthe event stream for a
ProcessExitedEventand never checks current process state:If the adapter exited before
close_session(crash / OOM, or a lazily-observed idle-timecrash), its
ProcessExitedevent already fired and was drained by the normal event loop. So thispoll can never observe it → it burns the full timeout for SIGTERM and again after SIGKILL.
Both signals are also no-ops (the PID is gone), so the whole ~10s is wasted.
Notably, the codebase already knows how to detect this —
is_adapter_gone_errorclassifies anout-of-band-dead adapter (via
ADAPTER_EXITED_ERROR_MARKER/ADAPTER_NO_ACTIVE_PROCESS_MARKER)and the prompt path uses it.
close_sessionjust doesn't consult it before waiting.Real-world impact
The dominant trigger is not a rare crash — it's a returning user after idle eviction. When
an idle session's adapter process is evicted, the user's next turn finds it gone and the host
recovers by recreating the session; the recreate's implicit close of the dead session hits this
stall, so the user eats ~10s on their first message back. Mid-turn OOM recovery hits the same path.
Fix
Capture the SIGTERM result and skip the
wait_for_process_exitdance when it reports the processis already gone — detected via the existing
is_adapter_gone_errorplus the lower-levelprocess-table
ESRCH/ "no such process" the signal path returns for an already-reaped PID:Behavior for a live process is unchanged; only the already-dead path is short-circuited straight to
resource disposal.
Validation
Built the sidecar with and without this change and measured
createSession-after-guest-kill (killthe ACP guest, then time the next
createSession, which internally closes the dead session):(35× on the recovery critical path.) Reordering the host-side recovery — recreate first, close the
dead session off the critical path — does not help, because the block is inside the sidecar's
teardown, not in host ordering; this sidecar-level fix is required.
Notes
close_session; reuses the existing gone-adapter classification.wait_for_process_exititself state-aware (returnimmediately if the process is no longer tracked) — happy to take that direction instead if you'd
prefer the fix at that layer.