Replace Windows app-server shutdown files with socket requests (#43308)

## What changed

Route managed Windows app-server shutdown through `/daemon/shutdown` on the local control socket. Require the server's PID and acknowledge it before triggering shutdown through the existing drain logic. Enable the endpoint only for managed Windows launches via `CODEX_DAEMON_SHUTDOWN_SOCKET`.

Watch running-turn changes whenever shutdown is requested so socket-triggered shutdown can finish draining. Keep the updater's file-based shutdown mechanism and retain forced termination as the manager's timeout fallback.

## Testing

Add a transport test covering rejection of unmanaged shutdown requests and mismatched PIDs, plus acknowledgment before emitting the shutdown event.

GitOrigin-RevId: 3e95dc1914f61e8a131f9b6fd56b6c63688d1744
This commit is contained in:
Eric Traut
2026-09-07 00:00:23 +00:00
committed by copyberry
parent a51da75131
commit 455318c202
16 changed files with 246 additions and 57 deletions
+15 -15
View File
@@ -35,6 +35,7 @@ use crate::plugin_config_reload::PluginStartupConfig;
use crate::transport::CHANNEL_CAPACITY;
use crate::transport::ConnectionOrigin;
use crate::transport::ConnectionState;
use crate::transport::DaemonShutdownAccess;
use crate::transport::OutboundConnectionState;
use crate::transport::RemoteControlPolicy;
use crate::transport::RemoteControlStartConfig;
@@ -226,20 +227,8 @@ async fn shutdown_signal() -> IoResult<ShutdownSignal> {
std::future::pending::<()>().await;
}
};
let daemon_signal = async {
let result = codex_app_server_transport::daemon_shutdown_signal().await;
// The processor retries listener errors; the updater instead needs
// immediate errors from its nonblocking shutdown probe.
if result.is_err() {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
result
};
tokio::select! {
_ = console_signal => Ok(ShutdownSignal::Forceable),
result = daemon_signal =>
result.map(|_| ShutdownSignal::Forceable),
}
console_signal.await;
Ok(ShutdownSignal::Forceable)
}
}
@@ -761,6 +750,14 @@ pub async fn run_main_with_transport_options(
socket_path.clone(),
transport_event_tx.clone(),
transport_shutdown_token.clone(),
if cfg!(windows)
&& std::env::var_os(codex_app_server_transport::DAEMON_SHUTDOWN_SOCKET_ENV)
.is_some()
{
DaemonShutdownAccess::Managed
} else {
DaemonShutdownAccess::Disabled
},
)
.await?;
transport_accept_handles.push(accept_handle);
@@ -989,7 +986,7 @@ pub async fn run_main_with_transport_options(
let running_turn_count = *running_turn_count_rx.borrow();
shutdown_state.on_signal(signal, connections.len(), running_turn_count);
}
changed = running_turn_count_rx.changed(), if graceful_signal_restart_enabled && shutdown_state.requested() => {
changed = running_turn_count_rx.changed(), if shutdown_state.requested() => {
if changed.is_err() {
warn!("running-turn watcher closed during graceful restart drain");
}
@@ -999,6 +996,9 @@ pub async fn run_main_with_transport_options(
break "transport_channel_closed";
};
match event {
TransportEvent::DaemonShutdown => {
shutdown_state.on_signal(ShutdownSignal::Forceable, connections.len(), *running_turn_count_rx.borrow());
}
TransportEvent::ConnectionOpened {
connection_id,
origin,
+1
View File
@@ -16,6 +16,7 @@ pub use codex_app_server_transport::AppServerTransport;
pub(crate) use codex_app_server_transport::CHANNEL_CAPACITY;
pub(crate) use codex_app_server_transport::ConnectionId;
pub(crate) use codex_app_server_transport::ConnectionOrigin;
pub(crate) use codex_app_server_transport::DaemonShutdownAccess;
pub(crate) use codex_app_server_transport::OutgoingMessage;
pub(crate) use codex_app_server_transport::QueuedOutgoingMessage;
pub(crate) use codex_app_server_transport::RemoteControlEnableError;