Fix app-server thread listener startup and shutdown races (#57787)

## Why

A thread runtime can shut down while listener setup is awaiting I/O, and a cold resume can start before the previous listener forwards `ShutdownComplete`. Listener replacement must preserve those buffered terminal events.

## What changed

- Coordinate listener installation and connection subscriptions with pending unloads, and reject closing or replaced runtimes.
- Allow a newly created runtime that has already shut down to drain its queued events, while enforcing one event reader per runtime.
- Track shutdown draining with a shared watch channel so cold resume and runtime replacement wait for terminal events to be processed.
- Serialize idle unloading with goal edits and cold resume, and recheck thread state after acquiring the lock.

GitOrigin-RevId: d05e348b0a81bddb2defa963d0cfd3fde08dbd03
This commit is contained in:
Charlie Marsh
2026-10-28 16:53:55 +00:00
committed by copyberry
parent c8bd6c1da9
commit 575cc5351e
10 changed files with 541 additions and 322 deletions
+5
View File
@@ -858,6 +858,11 @@ impl CodexThread {
self.io.agent_status().await
}
/// Returns whether the runtime has published its shutdown status.
pub fn has_shutdown_status(&self) -> bool {
matches!(*self.io.agent_status.borrow(), AgentStatus::Shutdown)
}
pub async fn list_background_terminals(&self) -> Vec<BackgroundTerminalInfo> {
self.session.list_background_terminals().await
}