From 4eb77a1a083eaf4d1b5f807da9f2705ba36ec2ad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 13:19:54 +0000 Subject: [PATCH 1/4] Initial plan From c48ff142efc425a7c0b252a71d851d45ec9b4191 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 13:36:55 +0000 Subject: [PATCH 2/4] Fix array append reentrancy deadlock Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com> --- crates/stdlib/src/array.rs | 43 +++++++++++++++++++++++++++- extra_tests/snippets/stdlib_array.py | 23 +++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/crates/stdlib/src/array.rs b/crates/stdlib/src/array.rs index b51bc02d3fb..26041e3f2de 100644 --- a/crates/stdlib/src/array.rs +++ b/crates/stdlib/src/array.rs @@ -75,6 +75,16 @@ mod array { macro_rules! def_array_enum { ($(($n:ident, $t:ty, $c:literal, $scode:literal)),*$(,)?) => { + #[derive(Copy, Clone, Debug)] + enum ArrayType { + $($n,)* + } + + #[derive(Debug)] + enum ArrayElementValue { + $($n($t),)* + } + #[derive(Debug, Clone)] pub enum ArrayContentType { $($n(Vec<$t>),)* @@ -127,6 +137,12 @@ mod array { } } + fn kind(&self) -> ArrayType { + match self { + $(ArrayContentType::$n(_) => ArrayType::$n,)* + } + } + fn reserve(&mut self, len: usize) { match self { $(ArrayContentType::$n(v) => v.reserve(len),)* @@ -143,6 +159,15 @@ mod array { Ok(()) } + fn push_value(&mut self, kind: ArrayType, value: ArrayElementValue) { + match (kind, self, value) { + $((ArrayType::$n, ArrayContentType::$n(v), ArrayElementValue::$n(val)) => { + v.push(val); + },)* + _ => unreachable!(), + } + } + fn pop(&mut self, i: isize, vm: &VirtualMachine) -> PyResult { match self { $(ArrayContentType::$n(v) => { @@ -483,6 +508,15 @@ mod array { })* } } + + } + + impl ArrayType { + fn convert(self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { + match self { + $(ArrayType::$n => <$t>::try_into_from_object(vm, obj).map(ArrayElementValue::$n),)* + } + } } }; } @@ -741,7 +775,14 @@ mod array { #[pymethod] fn append(zelf: &Py, x: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - zelf.try_resizable(vm)?.push(x, vm) + let kind = { + let array = zelf.read(); + array.kind() + }; + let value = kind.convert(x, vm)?; + let mut w = zelf.try_resizable(vm)?; + w.push_value(kind, value); + Ok(()) } #[pymethod] diff --git a/extra_tests/snippets/stdlib_array.py b/extra_tests/snippets/stdlib_array.py index ed2a8f22369..33d71bd75c1 100644 --- a/extra_tests/snippets/stdlib_array.py +++ b/extra_tests/snippets/stdlib_array.py @@ -143,3 +143,26 @@ def write(self, chunk): arr = array("b", range(128)) arr.tofile(_ReenteringWriter(arr)) assert len(arr) == 129 + + +class _ReenteringIndex: + def __init__(self, arr): + self.arr = arr + + def __index__(self): + self.arr.append(0) + return 0 + + +class _ReenteringReader: + def __init__(self, arr): + self.arr = arr + + def read(self, n): + self.arr.append(_ReenteringIndex(self.arr)) + return b"\0" * n + + +arr = array("b") +arr.fromfile(_ReenteringReader(arr), 1) +assert list(arr) == [0, 0, 0] From 4e8680f39a0f2d12035afe0026c5cdff890f11b9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 13:47:37 +0000 Subject: [PATCH 3/4] Refine array reentrancy handling Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com> --- crates/stdlib/src/array.rs | 24 ++++++++++++++++-------- extra_tests/snippets/stdlib_array.py | 1 + 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/crates/stdlib/src/array.rs b/crates/stdlib/src/array.rs index 26041e3f2de..ac44874b606 100644 --- a/crates/stdlib/src/array.rs +++ b/crates/stdlib/src/array.rs @@ -75,7 +75,7 @@ mod array { macro_rules! def_array_enum { ($(($n:ident, $t:ty, $c:literal, $scode:literal)),*$(,)?) => { - #[derive(Copy, Clone, Debug)] + #[derive(Copy, Clone, Debug, Eq, PartialEq)] enum ArrayType { $($n,)* } @@ -159,12 +159,23 @@ mod array { Ok(()) } - fn push_value(&mut self, kind: ArrayType, value: ArrayElementValue) { - match (kind, self, value) { + // `expected_kind` is captured before acquiring the write lock to ensure the array + // type didn't unexpectedly change between conversion and mutation. + fn push_value(&mut self, expected_kind: ArrayType, value: ArrayElementValue) { + assert_eq!(self.kind(), expected_kind); + match (expected_kind, self, value) { $((ArrayType::$n, ArrayContentType::$n(v), ArrayElementValue::$n(val)) => { v.push(val); },)* - _ => unreachable!(), + (_, array, value) => { + let actual = array.kind(); + panic!( + "array element value type {:?} does not match array type {:?} (expected {:?})", + value, + actual, + expected_kind + ); + } } } @@ -775,10 +786,7 @@ mod array { #[pymethod] fn append(zelf: &Py, x: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - let kind = { - let array = zelf.read(); - array.kind() - }; + let kind = zelf.read().kind(); let value = kind.convert(x, vm)?; let mut w = zelf.try_resizable(vm)?; w.push_value(kind, value); diff --git a/extra_tests/snippets/stdlib_array.py b/extra_tests/snippets/stdlib_array.py index 33d71bd75c1..99c7cf4a164 100644 --- a/extra_tests/snippets/stdlib_array.py +++ b/extra_tests/snippets/stdlib_array.py @@ -145,6 +145,7 @@ def write(self, chunk): assert len(arr) == 129 +# Regression: `fromfile` should not deadlock when __index__ re-enters append. class _ReenteringIndex: def __init__(self, arr): self.arr = arr From 391a83d4897f6981d410c2cd92e3d2b45dca8efb Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 30 Dec 2025 00:25:16 +0900 Subject: [PATCH 4/4] rewrite --- crates/stdlib/src/array.rs | 46 ++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/crates/stdlib/src/array.rs b/crates/stdlib/src/array.rs index ac44874b606..6b29d448463 100644 --- a/crates/stdlib/src/array.rs +++ b/crates/stdlib/src/array.rs @@ -159,23 +159,22 @@ mod array { Ok(()) } - // `expected_kind` is captured before acquiring the write lock to ensure the array - // type didn't unexpectedly change between conversion and mutation. - fn push_value(&mut self, expected_kind: ArrayType, value: ArrayElementValue) { - assert_eq!(self.kind(), expected_kind); - match (expected_kind, self, value) { - $((ArrayType::$n, ArrayContentType::$n(v), ArrayElementValue::$n(val)) => { - v.push(val); + fn truncate(&mut self, len: usize) { + match self { + $(ArrayContentType::$n(v) => v.truncate(len),)* + } + } + + fn set_or_push(&mut self, index: usize, value: ArrayElementValue) { + match (self, value) { + $((ArrayContentType::$n(v), ArrayElementValue::$n(val)) => { + if index < v.len() { + v[index] = val; + } else { + v.push(val); + } },)* - (_, array, value) => { - let actual = array.kind(); - panic!( - "array element value type {:?} does not match array type {:?} (expected {:?})", - value, - actual, - expected_kind - ); - } + _ => unreachable!(), } } @@ -784,12 +783,21 @@ mod array { self.read().itemsize() } + /// Append a new item to the end of the array. + /// Matches `ins1()` in arraymodule.c. #[pymethod] fn append(zelf: &Py, x: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - let kind = zelf.read().kind(); + let (n, kind) = { + let r = zelf.read(); + (r.len(), r.kind()) + }; + // First conversion (validation, result discarded) + kind.convert(x.clone(), vm)?; + // Truncate to n+1 if array grew during first conversion + zelf.try_resizable(vm)?.truncate(n + 1); + // Second conversion and store at position n let value = kind.convert(x, vm)?; - let mut w = zelf.try_resizable(vm)?; - w.push_value(kind, value); + zelf.try_resizable(vm)?.set_or_push(n, value); Ok(()) }