|
| 1 | +use std::{ |
| 2 | + marker::PhantomData, |
| 3 | + sync::atomic::{AtomicPtr, Ordering}, |
| 4 | +}; |
| 5 | + |
| 6 | +use crate::{ffi, Borrowed, Bound, Py, Python}; |
| 7 | + |
| 8 | +/// Variant of [`Py<T>`] that can be atomically swapped |
| 9 | +#[repr(transparent)] |
| 10 | +pub struct AtomicPy<T> { |
| 11 | + inner: AtomicPtr<ffi::PyObject>, |
| 12 | + _marker: PhantomData<T>, |
| 13 | +} |
| 14 | + |
| 15 | +// SAFETY: same as `Py<T>` |
| 16 | +unsafe impl<T> Send for AtomicPy<T> {} |
| 17 | +unsafe impl<T> Sync for AtomicPy<T> {} |
| 18 | + |
| 19 | +impl<T> AtomicPy<T> { |
| 20 | + /// Create an [`AtomicPy<T>`] holding the [`Py<T>`] |
| 21 | + #[inline] |
| 22 | + pub fn from_py(obj: Py<T>) -> Self { |
| 23 | + Self { |
| 24 | + inner: AtomicPtr::new(obj.into_ptr()), |
| 25 | + _marker: PhantomData, |
| 26 | + } |
| 27 | + } |
| 28 | + |
| 29 | + /// Create an [`AtomicPy<T>`] holding the [`Bound<T>`] |
| 30 | + #[inline] |
| 31 | + pub fn from_bound(obj: Bound<'_, T>) -> Self { |
| 32 | + Self::from_py(obj.unbind()) |
| 33 | + } |
| 34 | + |
| 35 | + /// Consumes the [`AtomicPy<T>`] and turns it back into a [`Py<T>`] |
| 36 | + #[inline] |
| 37 | + pub fn into_inner(self) -> Py<T> { |
| 38 | + let ptr = *std::mem::ManuallyDrop::new(self).inner.get_mut(); |
| 39 | + unsafe { Py::from_owned_ptr_unchecked(ptr) } |
| 40 | + } |
| 41 | + |
| 42 | + /// Loads the object stored in the [`AtomicPy`] |
| 43 | + #[inline] |
| 44 | + pub fn load<'py>(&self, py: Python<'py>, order: Ordering) -> Bound<'py, T> { |
| 45 | + let ptr = self.inner.load(order); |
| 46 | + unsafe { |
| 47 | + Borrowed::from_ptr_unchecked(py, ptr) |
| 48 | + .to_owned() |
| 49 | + .cast_into_unchecked() |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + /// Stores `obj` in the [`AtomicPy`], dropping its current value |
| 54 | + /// |
| 55 | + /// Note: This uses [`swap`](Self::swap) under the hood. |
| 56 | + #[inline] |
| 57 | + pub fn store<'py>(&self, obj: Bound<'py, T>, order: Ordering) { |
| 58 | + let _ = self.swap(obj, order); |
| 59 | + } |
| 60 | + |
| 61 | + /// Stores `obj` in the [`AtomicPy`], returning the previous value. |
| 62 | + /// |
| 63 | + /// See [`swap`](AtomicPy::swap) |
| 64 | + #[inline] |
| 65 | + pub fn swap_unbound(&self, obj: Py<T>, order: Ordering) -> Py<T> { |
| 66 | + let ptr = self.inner.swap(obj.into_ptr(), order); |
| 67 | + // SAFETY: `ptr` is a non-null, owned pointer to `ffi::PyObject` of type `T` |
| 68 | + unsafe { Py::from_owned_ptr_unchecked(ptr) } |
| 69 | + } |
| 70 | + |
| 71 | + /// Stores `obj` in the [`AtomicPy`], returning the previous value. |
| 72 | + /// |
| 73 | + /// `swap` takes an [Ordering] argument which describes the memory ordering of this operation. |
| 74 | + /// All ordering modes are possible. Note that using [Acquire](Ordering::Acquire) makes the |
| 75 | + /// store part of this operation [Relaxed](Ordering::Relaxed), and using |
| 76 | + /// [Release](Ordering::Release) makes the load part [Relaxed](Ordering::Relaxed). |
| 77 | + #[inline] |
| 78 | + pub fn swap<'py>(&self, obj: Bound<'py, T>, order: Ordering) -> Bound<'py, T> { |
| 79 | + let py = obj.py(); |
| 80 | + self.swap_unbound(obj.unbind(), order).into_bound(py) |
| 81 | + } |
| 82 | + |
| 83 | + /// Stores `new` into the [`AtomicPy`] if its current value is `current` (Python `current is |
| 84 | + /// self`) |
| 85 | + /// |
| 86 | + /// The return value is a [`Result`] indicating whether `new` was written and containing the |
| 87 | + /// previous value. |
| 88 | + /// |
| 89 | + /// `compare_exchange` takes two [Ordering] arguments to describe the memory ordering of this |
| 90 | + /// operation. `success` describes the required ordering for the read-modify-write operation |
| 91 | + /// that takes place if the comparison with `current` succeeds. `failure` describes the required |
| 92 | + /// ordering for the load operation that takes place when the comparison fails. Using |
| 93 | + /// [Acquire](Ordering::Acquire) as success ordering makes the store part of this operation |
| 94 | + /// [Relaxed](Ordering::Relaxed), and using [Release](Ordering::Release) makes the successful |
| 95 | + /// load [Relaxed](Ordering::Relaxed). The failure ordering can only be |
| 96 | + /// [SeqCst](Ordering::SeqCst), [Acquire](Ordering::Acquire) or [Relaxed](Ordering::Relaxed). |
| 97 | + #[inline] |
| 98 | + pub fn compare_exchange<'py>( |
| 99 | + &self, |
| 100 | + current: &Bound<'py, T>, |
| 101 | + new: &Bound<'py, T>, |
| 102 | + success: Ordering, |
| 103 | + failure: Ordering, |
| 104 | + ) -> Result<Bound<'py, T>, Bound<'py, T>> { |
| 105 | + let py = current.py(); |
| 106 | + match self |
| 107 | + .inner |
| 108 | + .compare_exchange(current.as_ptr(), new.as_ptr(), success, failure) |
| 109 | + { |
| 110 | + // stored `new`, `ptr` is owned |
| 111 | + Ok(ptr) => unsafe { |
| 112 | + let _ = std::mem::ManuallyDrop::new(new.clone()); // only incref if `new` was successfully stored |
| 113 | + |
| 114 | + // SAFETY: `ptr` is a non-null, owned pointer to `ffi::PyObject` of type `T` |
| 115 | + Ok(Bound::from_owned_ptr_unchecked(py, ptr).cast_into_unchecked()) |
| 116 | + }, |
| 117 | + // did not store `new`, `ptr` is borrowed |
| 118 | + Err(ptr) => unsafe { |
| 119 | + // SAFETY: `ptr` is a non-null, borrowed pointer to `ffi::PyObject` of type `T` |
| 120 | + Err(Borrowed::from_ptr_unchecked(py, ptr) |
| 121 | + .to_owned() |
| 122 | + .cast_into_unchecked()) |
| 123 | + }, |
| 124 | + } |
| 125 | + } |
| 126 | + |
| 127 | + /// Stores `new` into the [`AtomicPy`] if its current value is `current` (Python `current is |
| 128 | + /// self`) |
| 129 | + /// |
| 130 | + /// Unlike [AtomicPy::compare_exchange], this function is allowed to spuriously fail even when |
| 131 | + /// the comparison succeeds, which can result in more efficient code on some platforms. The |
| 132 | + /// return value is a [`Result`] indicating whether `new` was written and containing the |
| 133 | + /// previous value. |
| 134 | + /// |
| 135 | + /// `compare_exchange_weak` takes two [Ordering] arguments to describe the memory ordering of |
| 136 | + /// this operation. `success` describes the required ordering for the read-modify-write |
| 137 | + /// operation that takes place if the comparison with `current` succeeds. `failure` describes |
| 138 | + /// the required ordering for the load operation that takes place when the comparison fails. |
| 139 | + /// Using [Acquire](Ordering::Acquire) as success ordering makes the store part of this |
| 140 | + /// operation [Relaxed](Ordering::Relaxed), and using [Release](Ordering::Release) makes the |
| 141 | + /// successful load [Relaxed](Ordering::Relaxed). The failure ordering can only be |
| 142 | + /// [SeqCst](Ordering::SeqCst), [Acquire](Ordering::Acquire) or [Relaxed](Ordering::Relaxed). |
| 143 | + #[inline] |
| 144 | + pub fn compare_exchange_weak<'py>( |
| 145 | + &self, |
| 146 | + current: &Bound<'py, T>, |
| 147 | + new: &Bound<'py, T>, |
| 148 | + success: Ordering, |
| 149 | + failure: Ordering, |
| 150 | + ) -> Result<Bound<'py, T>, Bound<'py, T>> { |
| 151 | + let py = current.py(); |
| 152 | + match self |
| 153 | + .inner |
| 154 | + .compare_exchange_weak(current.as_ptr(), new.as_ptr(), success, failure) |
| 155 | + { |
| 156 | + // stored `new`, `ptr` is owned `current` |
| 157 | + Ok(ptr) => unsafe { |
| 158 | + let _ = std::mem::ManuallyDrop::new(new.clone()); // only incref if `new` was successfully stored |
| 159 | + |
| 160 | + // SAFETY: `ptr` is a non-null, owned pointer to `ffi::PyObject` of type `T` |
| 161 | + Ok(Bound::from_owned_ptr_unchecked(py, ptr).cast_into_unchecked()) |
| 162 | + }, |
| 163 | + // did not store `new`, `ptr` is borrowed |
| 164 | + Err(ptr) => unsafe { |
| 165 | + // SAFETY: `ptr` is a non-null, borrowed pointer to `ffi::PyObject` of type `T` |
| 166 | + Err(Borrowed::from_ptr_unchecked(py, ptr) |
| 167 | + .to_owned() |
| 168 | + .cast_into_unchecked()) |
| 169 | + }, |
| 170 | + } |
| 171 | + } |
| 172 | +} |
| 173 | + |
| 174 | +impl<T> From<Py<T>> for AtomicPy<T> { |
| 175 | + /// Create an [`AtomicPy<T>`] holding the [`Py<T>`] |
| 176 | + fn from(obj: Py<T>) -> Self { |
| 177 | + Self::from_py(obj) |
| 178 | + } |
| 179 | +} |
| 180 | + |
| 181 | +impl<T> From<Bound<'_, T>> for AtomicPy<T> { |
| 182 | + /// Create an [`AtomicPy<T>`] holding the [`Bound<T>`] |
| 183 | + fn from(obj: Bound<'_, T>) -> Self { |
| 184 | + Self::from_bound(obj) |
| 185 | + } |
| 186 | +} |
| 187 | + |
| 188 | +impl<T> Drop for AtomicPy<T> { |
| 189 | + /// Drop the inner [`Py<T>`] |
| 190 | + fn drop(&mut self) { |
| 191 | + // SAFETY: `inner` is a non-null, owned pointer to `ffi::PyObject` of type `T` |
| 192 | + unsafe { Py::<T>::from_owned_ptr_unchecked(*self.inner.get_mut()) }; |
| 193 | + } |
| 194 | +} |
| 195 | + |
| 196 | +#[cfg(test)] |
| 197 | +mod tests {} |
0 commit comments