diff --git a/Lib/test/_test_eintr.py b/Lib/test/_test_eintr.py index 095ccd032b3..b462f67b9c9 100644 --- a/Lib/test/_test_eintr.py +++ b/Lib/test/_test_eintr.py @@ -163,7 +163,6 @@ def test_readinto(self): self.assertEqual(os.readinto(fd, buffer), len(expected)) self.assertEqual(buffer, expected) - @unittest.expectedFailure # TODO: RUSTPYTHON; InterruptedError: [Errno 4] Interrupted system call def test_write(self): rd, wr = os.pipe() self.addCleanup(os.close, wr) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 965c0c0f493..a2848f79d8f 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -4814,8 +4814,6 @@ def test_finalize(self): self.assertEqual(result, ['a', 'b', 'd10', 'd03', 'd02', 'd01', 'e']) @support.requires_resource('cpu') - # TODO: RUSTPYTHON; dict iteration races with concurrent GC mutations - @unittest.expectedFailure def test_thread_safety(self): # bpo-24484: _run_finalizers() should be thread-safe def cb(): diff --git a/crates/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs index ac455ba4dd5..bc9d24aa4aa 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -321,12 +321,19 @@ pub(super) mod _os { } #[pyfunction] - fn write( - fd: crt_fd::Borrowed<'_>, - data: ArgBytesLike, - vm: &VirtualMachine, - ) -> io::Result { - data.with_ref(|b| vm.allow_threads(|| crt_fd::write(fd, b))) + fn write(fd: crt_fd::Borrowed<'_>, data: ArgBytesLike, vm: &VirtualMachine) -> PyResult { + data.with_ref(|b| { + loop { + match vm.allow_threads(|| crt_fd::write(fd, b)) { + Ok(n) => return Ok(n), + Err(e) if e.raw_os_error() == Some(libc::EINTR) => { + vm.check_signals()?; + continue; + } + Err(e) => return Err(e.into_pyexception(vm)), + } + } + }) } #[cfg(not(windows))] diff --git a/crates/vm/src/vm/mod.rs b/crates/vm/src/vm/mod.rs index 05210eb09d7..eee2055b751 100644 --- a/crates/vm/src/vm/mod.rs +++ b/crates/vm/src/vm/mod.rs @@ -22,7 +22,7 @@ use crate::{ self, PyBaseExceptionRef, PyDict, PyDictRef, PyInt, PyList, PyModule, PyStr, PyStrInterned, PyStrRef, PyTypeRef, PyUtf8Str, PyUtf8StrInterned, PyWeak, code::PyCode, - dict::{PyDictItems, PyDictValues}, + dict::{PyDictItems, PyDictKeys, PyDictValues}, pystr::AsPyStr, tuple::PyTuple, }, @@ -1822,7 +1822,9 @@ impl VirtualMachine { where F: Fn(PyObjectRef) -> PyResult, { - // Extract elements from item, if possible: + // Type-specific fast paths corresponding to _list_extend() in CPython + // Objects/listobject.c. Each branch takes an atomic snapshot to avoid + // race conditions from concurrent mutation (no GIL). let cls = value.class(); let list_borrow; let slice = if cls.is(self.ctx.types.tuple_type) { @@ -1830,8 +1832,13 @@ impl VirtualMachine { } else if cls.is(self.ctx.types.list_type) { list_borrow = value.downcast_ref::().unwrap().borrow_vec(); &list_borrow + } else if cls.is(self.ctx.types.dict_type) { + let keys = value.downcast_ref::().unwrap().keys_vec(); + return keys.into_iter().map(func).collect(); + } else if cls.is(self.ctx.types.dict_keys_type) { + let keys = value.downcast_ref::().unwrap().dict.keys_vec(); + return keys.into_iter().map(func).collect(); } else if cls.is(self.ctx.types.dict_values_type) { - // Atomic snapshot of dict values - prevents race condition during iteration let values = value .downcast_ref::() .unwrap() @@ -1839,7 +1846,6 @@ impl VirtualMachine { .values_vec(); return values.into_iter().map(func).collect(); } else if cls.is(self.ctx.types.dict_items_type) { - // Atomic snapshot of dict items - prevents race condition during iteration let items = value .downcast_ref::() .unwrap()