Skip to content

Commit ce3dd25

Browse files
committed
Use mutation counter instead of capacity check for sort mutation detection
The capacity heuristic missed mutations when `clear()` reset capacity to 0 via `mem::take`. An AtomicU32 counter on PyList, incremented in `borrow_vec_mut()`, reliably detects all mutations during sort.
1 parent baac056 commit ce3dd25

1 file changed

Lines changed: 9 additions & 3 deletions

File tree

crates/vm/src/builtins/list.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@ use alloc::fmt;
3030
use core::cell::Cell;
3131
use core::ops::DerefMut;
3232
use core::ptr::NonNull;
33+
use core::sync::atomic::{AtomicU32, Ordering};
3334

3435
#[pyclass(module = false, name = "list", unhashable = true, traverse = "manual")]
3536
#[derive(Default)]
3637
pub struct PyList {
3738
elements: PyRwLock<Vec<PyObjectRef>>,
39+
mutation_counter: AtomicU32,
3840
}
3941

4042
impl fmt::Debug for PyList {
@@ -48,6 +50,7 @@ impl From<Vec<PyObjectRef>> for PyList {
4850
fn from(elements: Vec<PyObjectRef>) -> Self {
4951
Self {
5052
elements: PyRwLock::new(elements),
53+
mutation_counter: AtomicU32::new(0),
5154
}
5255
}
5356
}
@@ -135,6 +138,7 @@ impl PyList {
135138
}
136139

137140
pub fn borrow_vec_mut(&self) -> PyRwLockWriteGuard<'_, Vec<PyObjectRef>> {
141+
self.mutation_counter.fetch_add(1, Ordering::Relaxed);
138142
self.elements.write()
139143
}
140144

@@ -391,12 +395,14 @@ impl PyList {
391395
// replace list contents with [] for duration of sort.
392396
// this prevents keyfunc from messing with the list and makes it easy to
393397
// check if it tries to append elements to it.
394-
let mut elements = core::mem::take(self.borrow_vec_mut().deref_mut());
398+
let mut elements = core::mem::take(self.elements.write().deref_mut());
399+
let version_before = self.mutation_counter.load(Ordering::Relaxed);
395400
let res = do_sort(vm, &mut elements, options.key, options.reverse);
396-
core::mem::swap(self.borrow_vec_mut().deref_mut(), &mut elements);
401+
let mutated = self.mutation_counter.load(Ordering::Relaxed) != version_before;
402+
core::mem::swap(self.elements.write().deref_mut(), &mut elements);
397403
res?;
398404

399-
if elements.capacity() > 0 {
405+
if mutated {
400406
return Err(vm.new_value_error("list modified during sort"));
401407
}
402408

0 commit comments

Comments
 (0)