From 233b798aa877c4e8ed11ab68095bc3f75b061a55 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Sun, 8 Mar 2026 16:48:34 +0900 Subject: [PATCH] Wait for overlapped WriteFile completion in Rust On Windows, SimpleQueue skips write locking because pipe writes are assumed atomic. Without GIL, PipeConnection. _send_bytes races on _send_ov when multiple threads call send_bytes concurrently (e.g. _terminate_pool vs workers). Wait for pending overlapped writes inside WriteFile before returning to Python, so ERROR_IO_PENDING is never exposed and the _send_ov assignment window is negligible. --- crates/vm/src/stdlib/_winapi.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/vm/src/stdlib/_winapi.rs b/crates/vm/src/stdlib/_winapi.rs index 1a22e06e93f..9f3650b37a2 100644 --- a/crates/vm/src/stdlib/_winapi.rs +++ b/crates/vm/src/stdlib/_winapi.rs @@ -1251,6 +1251,26 @@ mod _winapi { err }; + + // Without GIL, the Python-level PipeConnection._send_bytes has a + // race on _send_ov when the caller (SimpleQueue) skips locking on + // Windows. Wait for completion here so the caller never sees + // ERROR_IO_PENDING and never blocks in WaitForMultipleObjects, + // keeping the _send_ov window negligibly small. + if err == ERROR_IO_PENDING { + let event = ov.inner.lock().overlapped.hEvent; + vm.allow_threads(|| unsafe { + windows_sys::Win32::System::Threading::WaitForSingleObject( + event, + windows_sys::Win32::System::Threading::INFINITE, + ); + }); + let result = vm + .ctx + .new_tuple(vec![ov.into_pyobject(vm), vm.ctx.new_int(0u32).into()]); + return Ok(result.into()); + } + let result = vm .ctx .new_tuple(vec![ov.into_pyobject(vm), vm.ctx.new_int(err).into()]);