Skip to main content

shuttle_std/sync/atomic/
ptr.rs

1use crate::sync::atomic::Atomic;
2#[cfg(test)]
3use crate::sync::ResourceSignature;
4use std::sync::atomic::Ordering;
5
6/// A raw pointer type which can be safely shared between threads.
7pub struct AtomicPtr<T> {
8    inner: Atomic<*mut T>,
9}
10
11impl<T> Default for AtomicPtr<T> {
12    fn default() -> Self {
13        Self::new(std::ptr::null_mut())
14    }
15}
16
17impl<T> From<*mut T> for AtomicPtr<T> {
18    fn from(p: *mut T) -> Self {
19        Self::new(p)
20    }
21}
22
23impl<T> std::fmt::Debug for AtomicPtr<T> {
24    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
25        std::fmt::Debug::fmt(unsafe { &self.raw_load() }, f)
26    }
27}
28
29impl<T> std::fmt::Pointer for AtomicPtr<T> {
30    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
31        std::fmt::Pointer::fmt(unsafe { &self.raw_load() }, f)
32    }
33}
34
35// Atomic operations make it safe to Send + Sync this shared raw pointer (establishing the safety of
36// *using* the raw pointer is still up to the caller)
37unsafe impl<T> Send for AtomicPtr<T> {}
38unsafe impl<T> Sync for AtomicPtr<T> {}
39
40impl<T> AtomicPtr<T> {
41    /// Creates a new `AtomicPtr`.
42    #[track_caller]
43    pub const fn new(v: *mut T) -> Self {
44        Self { inner: Atomic::new(v) }
45    }
46
47    /// Returns a mutable reference to the underlying pointer.
48    pub fn get_mut(&mut self) -> &mut *mut T {
49        self.inner.get_mut()
50    }
51
52    /// Consumes the atomic and returns the contained value.
53    pub fn into_inner(self) -> *mut T {
54        self.inner.into_inner()
55    }
56
57    /// Loads a value from the pointer.
58    pub fn load(&self, order: Ordering) -> *mut T {
59        self.inner.load(order)
60    }
61
62    /// Stores a value into the pointer.
63    pub fn store(&self, val: *mut T, order: Ordering) {
64        self.inner.store(val, order)
65    }
66
67    /// Stores a value into the atomic pointer, returning the previous value.
68    pub fn swap(&self, val: *mut T, order: Ordering) -> *mut T {
69        self.inner.swap(val, order)
70    }
71
72    /// Fetches the value, and applies a function to it that returns an optional new value.
73    /// Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else
74    /// `Err(previous_value)`.
75    pub fn try_update(
76        &self,
77        set_order: Ordering,
78        fetch_order: Ordering,
79        mut f: impl FnMut(*mut T) -> Option<*mut T>,
80    ) -> Result<*mut T, *mut T> {
81        let mut prev = self.load(fetch_order);
82        while let Some(next) = f(prev) {
83            match self.compare_exchange_weak(prev, next, set_order, fetch_order) {
84                x @ Ok(_) => return x,
85                Err(next_prev) => prev = next_prev,
86            }
87        }
88        Err(prev)
89    }
90
91    /// Fetches the value, and applies a function to it that returns an optional new value.
92    ///
93    /// Since Rust 1.95 this is an alias for [`try_update`](Self::try_update). Deprecated since
94    /// Rust 1.99.
95    pub fn fetch_update<F>(&self, set_order: Ordering, fetch_order: Ordering, f: F) -> Result<*mut T, *mut T>
96    where
97        F: FnMut(*mut T) -> Option<*mut T>,
98    {
99        self.try_update(set_order, fetch_order, f)
100    }
101
102    /// Fetches the value, applies a function to it that returns a new value, and stores the new
103    /// value. Returns the previous value.
104    pub fn update(&self, set_order: Ordering, fetch_order: Ordering, mut f: impl FnMut(*mut T) -> *mut T) -> *mut T {
105        let mut prev = self.load(fetch_order);
106        loop {
107            match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) {
108                Ok(x) => break x,
109                Err(next_prev) => prev = next_prev,
110            }
111        }
112    }
113
114    /// Stores a value into the atomic pointer if the current value is the same as the
115    /// `current` value.
116    #[deprecated(since = "0.0.6", note = "Use `compare_exchange` or `compare_exchange_weak` instead")]
117    pub fn compare_and_swap(&self, current: *mut T, new: *mut T, order: Ordering) -> *mut T {
118        match self.compare_exchange(current, new, order, order) {
119            Ok(v) => v,
120            Err(v) => v,
121        }
122    }
123
124    /// Stores a value into the atomic pointer if the current value is the same as the
125    /// `current` value.
126    ///
127    /// The return value is a result indicating whether the new value was written and
128    /// containing the previous value. On success this value is guaranteed to be equal to
129    /// `current`.
130    pub fn compare_exchange(
131        &self,
132        current: *mut T,
133        new: *mut T,
134        success: Ordering,
135        failure: Ordering,
136    ) -> Result<*mut T, *mut T> {
137        self.inner
138            .try_update(success, failure, |val| (val == current).then_some(new))
139    }
140
141    /// Stores a value into the atomic pointer if the current value is the same as the
142    /// `current` value.
143    ///
144    /// Unlike [`AtomicPtr::compare_exchange`], this function is allowed to spuriously fail
145    /// even when the comparison succeeds, which can result in more efficient code on some
146    /// platforms. The return value is a result indicating whether the new value was written
147    /// and containing the previous value.
148    // TODO actually produce spurious failures
149    pub fn compare_exchange_weak(
150        &self,
151        current: *mut T,
152        new: *mut T,
153        success: Ordering,
154        failure: Ordering,
155    ) -> Result<*mut T, *mut T> {
156        self.compare_exchange(current, new, success, failure)
157    }
158
159    /// Offsets the pointer's address by adding `val` (in units of `T`). Returns the previous
160    /// pointer.
161    ///
162    /// This operation wraps around on overflow, like `<*mut T>::wrapping_add`.
163    pub fn fetch_ptr_add(&self, val: usize, order: Ordering) -> *mut T {
164        self.fetch_byte_add(val.wrapping_mul(std::mem::size_of::<T>()), order)
165    }
166
167    /// Offsets the pointer's address by subtracting `val` (in units of `T`). Returns the previous
168    /// pointer.
169    ///
170    /// This operation wraps around on overflow, like `<*mut T>::wrapping_sub`.
171    pub fn fetch_ptr_sub(&self, val: usize, order: Ordering) -> *mut T {
172        self.fetch_byte_sub(val.wrapping_mul(std::mem::size_of::<T>()), order)
173    }
174
175    /// Offsets the pointer's address by adding `val` bytes. Returns the previous pointer.
176    ///
177    /// This operation wraps around on overflow, like `<*mut T>::wrapping_byte_add`.
178    pub fn fetch_byte_add(&self, val: usize, order: Ordering) -> *mut T {
179        self.inner
180            .try_update(order, order, |old| Some(old.wrapping_byte_add(val)))
181            .unwrap()
182    }
183
184    /// Offsets the pointer's address by subtracting `val` bytes. Returns the previous pointer.
185    ///
186    /// This operation wraps around on overflow, like `<*mut T>::wrapping_byte_sub`.
187    pub fn fetch_byte_sub(&self, val: usize, order: Ordering) -> *mut T {
188        self.inner
189            .try_update(order, order, |old| Some(old.wrapping_byte_sub(val)))
190            .unwrap()
191    }
192
193    /// Bitwise "or" of the pointer's address with `val`, keeping the pointer's provenance.
194    /// Returns the previous pointer.
195    pub fn fetch_or(&self, val: usize, order: Ordering) -> *mut T {
196        self.inner
197            .try_update(order, order, |old| Some(old.map_addr(|addr| addr | val)))
198            .unwrap()
199    }
200
201    /// Bitwise "and" of the pointer's address with `val`, keeping the pointer's provenance.
202    /// Returns the previous pointer.
203    pub fn fetch_and(&self, val: usize, order: Ordering) -> *mut T {
204        self.inner
205            .try_update(order, order, |old| Some(old.map_addr(|addr| addr & val)))
206            .unwrap()
207    }
208
209    /// Bitwise "xor" of the pointer's address with `val`, keeping the pointer's provenance.
210    /// Returns the previous pointer.
211    pub fn fetch_xor(&self, val: usize, order: Ordering) -> *mut T {
212        self.inner
213            .try_update(order, order, |old| Some(old.map_addr(|addr| addr ^ val)))
214            .unwrap()
215    }
216
217    /// Load the atomic value directly without triggering any Shuttle context switches.
218    ///
219    /// # Safety
220    ///
221    /// Shuttle does not consider potential concurrent interleavings of this function call,
222    /// and so it should be used when those interleavings aren't important (primarily in
223    /// debugging scenarios where we might want to just print this atomic's value).
224    pub unsafe fn raw_load(&self) -> *mut T {
225        self.inner.raw_load()
226    }
227
228    #[cfg(test)]
229    pub(crate) fn signature(&self) -> ResourceSignature {
230        self.inner.signature()
231    }
232}