Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Lib/test/_test_multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -4815,9 +4815,9 @@ def test_finalize(self):
result = [obj for obj in iter(conn.recv, 'STOP')]
self.assertEqual(result, ['a', 'b', 'd10', 'd03', 'd02', 'd01', 'e'])

# TODO: RUSTPYTHON - gc.get_threshold() and gc.set_threshold() not implemented
@unittest.expectedFailure
@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():
Expand Down
4 changes: 0 additions & 4 deletions Lib/test/test_threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -2141,10 +2141,6 @@ def __init__(self, a, *, b) -> None:
CustomRLock(1, b=2)
self.assertEqual(warnings_log, [])

@unittest.expectedFailure # TODO: RUSTPYTHON
def test_release_save_unacquired(self):
return super().test_release_save_unacquired()

@unittest.skip('TODO: RUSTPYTHON; flaky test')
def test_different_thread(self):
return super().test_different_thread()
Expand Down
2 changes: 1 addition & 1 deletion crates/common/src/refcount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ impl RefCount {
pub fn safe_inc(&self) -> bool {
let mut old = State::from_raw(self.state.load(Ordering::Relaxed));
loop {
if old.destructed() {
if old.destructed() || old.strong() == 0 {
return false;
}
if (old.strong() as usize) >= STRONG {
Expand Down
50 changes: 35 additions & 15 deletions crates/compiler-core/src/bytecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use core::{
cell::UnsafeCell,
hash, mem,
ops::Deref,
sync::atomic::{AtomicU8, AtomicU16, Ordering},
sync::atomic::{AtomicU8, AtomicU16, AtomicUsize, Ordering},
};
use itertools::Itertools;
use malachite_bigint::BigInt;
Expand Down Expand Up @@ -411,6 +411,10 @@ impl TryFrom<&[u8]> for CodeUnit {
pub struct CodeUnits {
units: UnsafeCell<Box<[CodeUnit]>>,
adaptive_counters: Box<[AtomicU16]>,
/// Pointer-sized cache entries for descriptor pointers.
/// Single atomic load/store prevents torn reads when multiple threads
/// specialize the same instruction concurrently.
pointer_cache: Box<[AtomicUsize]>,
}

// SAFETY: All cache operations use atomic read/write instructions.
Expand All @@ -432,9 +436,15 @@ impl Clone for CodeUnits {
.iter()
.map(|c| AtomicU16::new(c.load(Ordering::Relaxed)))
.collect();
let pointer_cache = self
.pointer_cache
.iter()
.map(|c| AtomicUsize::new(c.load(Ordering::Relaxed)))
.collect();
Self {
units: UnsafeCell::new(units),
adaptive_counters,
pointer_cache,
}
}
}
Expand Down Expand Up @@ -472,13 +482,19 @@ impl<const N: usize> From<[CodeUnit; N]> for CodeUnits {
impl From<Vec<CodeUnit>> for CodeUnits {
fn from(value: Vec<CodeUnit>) -> Self {
let units = value.into_boxed_slice();
let adaptive_counters = (0..units.len())
let len = units.len();
let adaptive_counters = (0..len)
.map(|_| AtomicU16::new(0))
.collect::<Vec<_>>()
.into_boxed_slice();
let pointer_cache = (0..len)
.map(|_| AtomicUsize::new(0))
.collect::<Vec<_>>()
.into_boxed_slice();
Self {
units: UnsafeCell::new(units),
adaptive_counters,
pointer_cache,
}
}
}
Expand Down Expand Up @@ -600,25 +616,29 @@ impl CodeUnits {
lo | (hi << 16)
}

/// Write a u64 value across four consecutive CACHE code units starting at `index`.
/// Store a pointer-sized value atomically in the pointer cache at `index`.
///
/// Uses a single `AtomicUsize` store to prevent torn writes when
/// multiple threads specialize the same instruction concurrently.
///
/// # Safety
/// Same requirements as `write_cache_u16`.
pub unsafe fn write_cache_u64(&self, index: usize, value: u64) {
unsafe {
self.write_cache_u32(index, value as u32);
self.write_cache_u32(index + 2, (value >> 32) as u32);
}
/// - `index` must be in bounds.
/// - `value` must be `0` or a valid `*const PyObject` encoded as `usize`.
/// - Callers must follow the cache invalidation/upgrade protocol:
/// invalidate the version guard before writing and publish the new
/// version after writing.
pub unsafe fn write_cache_ptr(&self, index: usize, value: usize) {
self.pointer_cache[index].store(value, Ordering::Relaxed);
}

/// Read a u64 value from four consecutive CACHE code units starting at `index`.
/// Load a pointer-sized value atomically from the pointer cache at `index`.
///
/// Uses a single `AtomicUsize` load to prevent torn reads.
///
/// # Panics
/// Panics if `index + 3` is out of bounds.
pub fn read_cache_u64(&self, index: usize) -> u64 {
let lo = self.read_cache_u32(index) as u64;
let hi = self.read_cache_u32(index + 2) as u64;
lo | (hi << 32)
/// Panics if `index` is out of bounds.
pub fn read_cache_ptr(&self, index: usize) -> usize {
self.pointer_cache[index].load(Ordering::Relaxed)
}

/// Read adaptive counter bits for instruction at `index`.
Expand Down
Loading
Loading