From 34b294666c893ca8c57fe39faf69c0aa2e41d739 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Tue, 17 Mar 2026 21:02:58 +0900 Subject: [PATCH 1/2] Fix allow_threads and EINTR handling - Wrap Windows SemLock acquire wait with allow_threads - Retry nanosleep on EINTR with remaining time instead of returning early - Remove expectedFailure for test_sleep in _test_eintr.py --- Lib/test/_test_eintr.py | 1 - crates/stdlib/src/multiprocessing.rs | 3 ++- crates/vm/src/stdlib/time.rs | 35 +++++++++++++++++++--------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Lib/test/_test_eintr.py b/Lib/test/_test_eintr.py index a826d31bcd6..095ccd032b3 100644 --- a/Lib/test/_test_eintr.py +++ b/Lib/test/_test_eintr.py @@ -392,7 +392,6 @@ def test_os_open(self): class TimeEINTRTest(EINTRBaseTest): """ EINTR tests for the time module. """ - @unittest.expectedFailure # TODO: RUSTPYTHON def test_sleep(self): t0 = time.monotonic() time.sleep(self.sleep_time) diff --git a/crates/stdlib/src/multiprocessing.rs b/crates/stdlib/src/multiprocessing.rs index 26d1bea8859..fe30abfdcb5 100644 --- a/crates/stdlib/src/multiprocessing.rs +++ b/crates/stdlib/src/multiprocessing.rs @@ -193,7 +193,8 @@ mod _multiprocessing { remaining.min(poll_ms) }; - let res = unsafe { WaitForSingleObjectEx(self.handle.as_raw(), wait_ms, 0) }; + let handle = self.handle.as_raw(); + let res = vm.allow_threads(|| unsafe { WaitForSingleObjectEx(handle, wait_ms, 0) }); match res { WAIT_OBJECT_0 => { diff --git a/crates/vm/src/stdlib/time.rs b/crates/vm/src/stdlib/time.rs index d38152db84a..9235d8c89b7 100644 --- a/crates/vm/src/stdlib/time.rs +++ b/crates/vm/src/stdlib/time.rs @@ -115,17 +115,30 @@ mod decl { #[cfg(unix)] { - // this is basically std::thread::sleep, but that catches interrupts and we don't want to; - let ts = nix::sys::time::TimeSpec::from(dur); - // Capture errno inside the closure: attach_thread (called by - // allow_threads on return) can clobber errno via syscalls. - let (res, err) = vm.allow_threads(|| { - let r = unsafe { libc::nanosleep(ts.as_ref(), core::ptr::null_mut()) }; - (r, nix::Error::last_raw()) - }); - let interrupted = res == -1 && err == libc::EINTR; - - if interrupted { + // Loop on nanosleep, recomputing the + // remaining timeout after each EINTR so that signals don't + // shorten the requested sleep duration. + use std::time::Instant; + let deadline = Instant::now() + dur; + loop { + let remaining = deadline.saturating_duration_since(Instant::now()); + if remaining.is_zero() { + break; + } + let ts = nix::sys::time::TimeSpec::from(remaining); + let (res, err) = vm.allow_threads(|| { + let r = unsafe { libc::nanosleep(ts.as_ref(), core::ptr::null_mut()) }; + (r, nix::Error::last_raw()) + }); + if res == 0 { + break; + } + if err != libc::EINTR { + return Err( + vm.new_os_error(format!("nanosleep: {}", nix::Error::from_raw(err))) + ); + } + // EINTR: run signal handlers, then retry with remaining time vm.check_signals()?; } } From ba58ffc39d42052cf3a38139345f7f2cb2509f31 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Fri, 20 Mar 2026 04:53:21 +0900 Subject: [PATCH 2/2] Remove expectedFailureIfWindows for testHashComparisonOfMethods --- Lib/test/test_class.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index 5a3f0744ad2..7420f289b16 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -614,7 +614,6 @@ def assertNotOrderable(self, a, b): with self.assertRaises(TypeError): a >= b - @unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; AssertionError: 1543448294720 != 1543448295392") def testHashComparisonOfMethods(self): # Test comparison and hash of methods class A: