From e6f4af1d92bbfadcc49217006c930f2a760f1cc8 Mon Sep 17 00:00:00 2001 From: zm-oai Date: Mon, 28 Sep 2026 00:46:29 +0000 Subject: [PATCH] Wait briefly for the Windows sandbox provisioning service to start (#48829) ## Why Allow the provisioning service time to start without holding up desktop readiness checks for the full provisioning timeout. ## What changed - Poll service status while startup is pending, waiting up to five seconds within the caller's deadline. Treat absent, deletion-pending, or other non-running service states as unavailable. - Preserve the original provisioning deadline for pipe availability and request processing, and reject requests whose deadline expires during server authentication. - Recheck service status when the pipe disappears while waiting for an available instance. GitOrigin-RevId: ea89175cb41749b9768ac5915083bbfd8ad32713 --- .../src/provisioning_client.rs | 48 ++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/codex-rs/windows-sandbox-rs/src/provisioning_client.rs b/codex-rs/windows-sandbox-rs/src/provisioning_client.rs index bd74209aaa11..d1daafafee40 100644 --- a/codex-rs/windows-sandbox-rs/src/provisioning_client.rs +++ b/codex-rs/windows-sandbox-rs/src/provisioning_client.rs @@ -26,6 +26,8 @@ use windows_sys::Win32::Foundation::ERROR_NO_DATA; use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY; use windows_sys::Win32::Foundation::ERROR_PIPE_NOT_CONNECTED; use windows_sys::Win32::Foundation::ERROR_SEM_TIMEOUT; +use windows_sys::Win32::Foundation::ERROR_SERVICE_DOES_NOT_EXIST; +use windows_sys::Win32::Foundation::ERROR_SERVICE_MARKED_FOR_DELETE; use windows_sys::Win32::Foundation::HANDLE; use windows_sys::Win32::Security::SC_HANDLE; use windows_sys::Win32::Storage::FileSystem::SECURITY_IMPERSONATION; @@ -35,6 +37,7 @@ use windows_sys::Win32::System::Pipes::WaitNamedPipeW; use windows_sys::Win32::System::Services; const PROVISIONING_TIMEOUT: Duration = Duration::from_secs(120); +const SERVICE_STARTUP_TIMEOUT: Duration = Duration::from_secs(5); mod group_change; mod refresh_retry; @@ -256,6 +259,13 @@ fn exchange_request( ) -> anyhow::Result { verify_server(pipe.as_raw_handle() as HANDLE) .context("authenticate provisioning pipe server")?; + if Instant::now() >= deadline { + return Err(io::Error::new( + io::ErrorKind::TimedOut, + "sandbox provisioning deadline expired before sending request", + ) + .into()); + } crate::write_provisioning_frame(&mut *pipe, request) .context("send sandbox provisioning request")?; read_response(pipe, deadline) @@ -286,6 +296,9 @@ fn read_response( } fn connect(deadline: Instant) -> anyhow::Result> { + // Startup should not hold up the desktop's readiness check for the full + // provisioning timeout. Keep the caller's original deadline for real work. + let startup_deadline = deadline.min(Instant::now() + SERVICE_STARTUP_TIMEOUT); let pipe_name = crate::windows_sandbox_service_pipe_name()?; let open_pipe = || { OpenOptions::new() @@ -297,6 +310,31 @@ fn connect(deadline: Instant) -> anyhow::Result> { let pipe_name = crate::to_wide(&pipe_name); loop { + match query_service_status() { + Ok(status) if status.dwCurrentState == Services::SERVICE_START_PENDING => { + let remaining = startup_deadline.saturating_duration_since(Instant::now()); + if remaining.is_zero() { + return Ok(None); + } + std::thread::sleep(remaining.min(Duration::from_millis(25))); + continue; + } + Ok(status) if status.dwCurrentState == Services::SERVICE_RUNNING => {} + Ok(_) => return Ok(None), + Err(error) + if error.downcast_ref::().is_some_and(|error| { + matches!( + error.raw_os_error(), + Some(code) + if code == ERROR_SERVICE_DOES_NOT_EXIST as i32 + || code == ERROR_SERVICE_MARKED_FOR_DELETE as i32 + ) + }) => + { + return Ok(None); + } + Err(error) => return Err(error), + } match open_pipe() { Ok(pipe) => return Ok(Some(pipe)), Err(error) if error.raw_os_error() == Some(ERROR_FILE_NOT_FOUND as i32) => { @@ -312,12 +350,10 @@ fn connect(deadline: Instant) -> anyhow::Result> { .max(1); if unsafe { WaitNamedPipeW(pipe_name.as_ptr(), wait_ms) } == 0 { let error = io::Error::last_os_error(); - if matches!( - error.raw_os_error(), - Some(code) - if code == ERROR_FILE_NOT_FOUND as i32 - || code == ERROR_SEM_TIMEOUT as i32 - ) { + if error.raw_os_error() == Some(ERROR_FILE_NOT_FOUND as i32) { + continue; + } + if error.raw_os_error() == Some(ERROR_SEM_TIMEOUT as i32) { return Ok(None); } return Err(error).context("wait for sandbox provisioning pipe");