From 8be5230a9b32d3afac80ee3857270a7b3efde0a4 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" <69878+youknowone@users.noreply.github.com> Date: Fri, 20 Mar 2026 19:59:13 +0900 Subject: [PATCH] Fix allow_threads and EINTR handling (#7457) * 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 * Remove expectedFailureIfWindows for testHashComparisonOfMethods --- Lib/test/_test_eintr.py | 1 - Lib/test/test_class.py | 1 - crates/stdlib/src/multiprocessing.rs | 3 ++- crates/vm/src/stdlib/time.rs | 35 +++++++++++++++++++--------- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/Lib/test/_test_eintr.py b/Lib/test/_test_eintr.py index a826d31bc..095ccd032 100644 --- a/Lib/test/_test_eintr.py +++ b/Lib/test/_test_eintr.py @@ -392,7 +392,6 @@ class SocketEINTRTest(EINTRBaseTest): 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/Lib/test/test_class.py b/Lib/test/test_class.py index 5a3f0744a..7420f289b 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -614,7 +614,6 @@ class ClassTests(unittest.TestCase): with self.assertRaises(TypeError): a >= b - @unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; AssertionError: 1543448294720 != 1543448295392") def testHashComparisonOfMethods(self): # Test comparison and hash of methods class A: diff --git a/crates/stdlib/src/multiprocessing.rs b/crates/stdlib/src/multiprocessing.rs index 26d1bea88..fe30abfdc 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 d38152db8..9235d8c89 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()?; } }