fix: MCP leaks in app-server (#17223)

The disconnect path now reuses the same teardown flow as explicit
unsubscribe, and the thread-state bookkeeping consistently reports only
threads that lost their last subscriber

https://github.com/openai/codex/issues/16895
This commit is contained in:
jif-oai
2026-04-10 15:31:26 +01:00
committed by GitHub
parent 8035cb03f1
commit 8d58899297
3 changed files with 190 additions and 84 deletions

View File

@@ -352,8 +352,8 @@ impl ThreadStateManager {
true
}
pub(crate) async fn remove_connection(&self, connection_id: ConnectionId) {
let thread_states = {
pub(crate) async fn remove_connection(&self, connection_id: ConnectionId) -> Vec<ThreadId> {
{
let mut state = self.state.lock().await;
state.live_connections.remove(&connection_id);
let thread_ids = state
@@ -367,36 +367,13 @@ impl ThreadStateManager {
}
thread_ids
.into_iter()
.map(|thread_id| {
(
thread_id,
state
.threads
.get(&thread_id)
.is_none_or(|thread_entry| thread_entry.connection_ids.is_empty()),
state
.threads
.get(&thread_id)
.map(|thread_entry| thread_entry.state.clone()),
)
.filter(|thread_id| {
state
.threads
.get(thread_id)
.is_some_and(|thread_entry| thread_entry.connection_ids.is_empty())
})
.collect::<Vec<_>>()
};
for (thread_id, no_subscribers, thread_state) in thread_states {
if !no_subscribers {
continue;
}
let Some(thread_state) = thread_state else {
continue;
};
let listener_generation = thread_state.lock().await.listener_generation;
tracing::debug!(
thread_id = %thread_id,
connection_id = ?connection_id,
listener_generation,
"retaining thread listener after connection disconnect left zero subscribers"
);
}
}
}