atomic_borrow/
lib.rs

1#![deny(unsafe_op_in_unsafe_fn)]
2
3//! An simple atomic reference counter.
4
5use std::{
6    ops::{Deref, DerefMut},
7    sync::atomic::{AtomicUsize, Ordering},
8};
9
10/// An atomic reference counter.
11#[repr(transparent)]
12#[derive(Debug, Default)]
13pub struct AtomicBorrow {
14    borrow: AtomicUsize,
15}
16
17impl AtomicBorrow {
18    /// The mask for the shared borrow count.
19    pub const SHARED_MASK: usize = usize::MAX >> 1;
20    /// The mask for the unique borrow bit.
21    pub const UNIQUE_MASK: usize = !Self::SHARED_MASK;
22
23    const SPIN_COUNT: usize = 1 << 10;
24
25    /// Creates a new `AtomicBorrow`.
26    #[inline]
27    pub const fn new() -> Self {
28        Self {
29            borrow: AtomicUsize::new(0),
30        }
31    }
32
33    /// Returns number of shared borrows.
34    #[inline]
35    pub fn shared_count(&self) -> usize {
36        self.borrow.load(Ordering::Acquire) & Self::SHARED_MASK
37    }
38
39    /// Returns true if `self` is uniquely borrowed.
40    #[inline]
41    pub fn is_unique(&self) -> bool {
42        self.borrow.load(Ordering::Acquire) & Self::UNIQUE_MASK == 0
43    }
44
45    /// Returns true if `self` is borrowed in any way.
46    #[inline]
47    pub fn is_borrowed(&self) -> bool {
48        self.borrow.load(Ordering::Acquire) != 0
49    }
50
51    /// Tries to acquire a shared reference.
52    ///
53    /// Returns `true` if the reference was acquired.
54    #[inline]
55    pub fn borrow(&self) -> bool {
56        let prev = self.borrow.fetch_add(1, Ordering::Acquire);
57
58        if prev & Self::SHARED_MASK == Self::SHARED_MASK {
59            panic!("borrow counter overflowed");
60        }
61
62        if prev & Self::UNIQUE_MASK != 0 {
63            // we're already uniquely borrowed, so undo the increment and return false
64            self.borrow.fetch_sub(1, Ordering::Release);
65            false
66        } else {
67            true
68        }
69    }
70
71    /// Tries to acquire a unique reference.
72    ///
73    /// Returns `true` if the reference was acquired.
74    #[inline]
75    pub fn borrow_mut(&self) -> bool {
76        self.borrow
77            .compare_exchange(0, Self::UNIQUE_MASK, Ordering::Acquire, Ordering::Relaxed)
78            .is_ok()
79    }
80
81    /// Releases a shared reference.
82    ///
83    /// # Panics.
84    /// * If `self` is not borrowed. Only with `debug_assertions` enabled.
85    /// * If `self` is uniquely borrowed. Only with `debug_assertions` enabled.
86    #[inline]
87    pub fn release(&self) {
88        let prev = self.borrow.fetch_sub(1, Ordering::Release);
89        debug_assert_ne!(
90            prev, 0,
91            "borrow counter underflow, this means you released more times than you borrowed"
92        );
93        debug_assert_eq!(
94            prev & Self::UNIQUE_MASK,
95            0,
96            "shared release of unique borrow"
97        );
98    }
99
100    /// Releases a unique reference.
101    ///
102    /// # Panics.
103    /// * If `self` is not uniquely borrowed. Only with `debug_assertions` enabled.
104    #[inline]
105    pub fn release_mut(&self) {
106        let prev = self.borrow.fetch_and(!Self::UNIQUE_MASK, Ordering::Release);
107        debug_assert_ne!(
108            prev & Self::UNIQUE_MASK,
109            0,
110            "unique release of shared borrow"
111        );
112    }
113
114    /// Spins until a shared reference can be acquired.
115    #[inline]
116    pub fn spin_borrow(&self) {
117        for _ in 0..Self::SPIN_COUNT {
118            if self.borrow() {
119                return;
120            }
121
122            std::hint::spin_loop();
123        }
124
125        while !self.borrow() {
126            std::thread::yield_now();
127        }
128    }
129
130    /// Spins until a unique reference can be acquired.
131    #[inline]
132    pub fn spin_borrow_mut(&self) {
133        for _ in 0..Self::SPIN_COUNT {
134            if self.borrow_mut() {
135                return;
136            }
137
138            std::hint::spin_loop();
139        }
140
141        while !self.borrow_mut() {
142            std::thread::yield_now();
143        }
144    }
145}
146
147/// A guard that releases a shared reference when dropped.
148pub struct SharedGuard<'a, T> {
149    data: *const T,
150    borrow: &'a AtomicBorrow,
151}
152
153impl<'a, T> SharedGuard<'a, T> {
154    /// Creates a new [`SharedGuard`].
155    #[inline]
156    pub fn new(data: &'a T, borrow: &'a AtomicBorrow) -> Self {
157        Self { data, borrow }
158    }
159
160    /// Tries to borrow the data.
161    ///
162    /// # Safety
163    /// * Any borrows of `data` must be registered with `borrow`.
164    /// * `data` must be a valid pointer for the entire lifetime of `self`.
165    #[inline]
166    pub unsafe fn try_new(data: *const T, borrow: &'a AtomicBorrow) -> Option<Self> {
167        if borrow.borrow() {
168            Some(Self { data, borrow })
169        } else {
170            None
171        }
172    }
173
174    /// Spins until the data can be borrowed.
175    ///
176    /// # Safety
177    /// * Any borrows of `data` must be registered with `borrow`.
178    /// * `data` must be a valid pointer for the entire lifetime of `self`.
179    #[inline]
180    pub unsafe fn spin(data: *const T, borrow: &'a AtomicBorrow) -> Self {
181        borrow.spin_borrow();
182        Self { data, borrow }
183    }
184
185    /// Gets the inner [`AtomicBorrow`].
186    #[inline]
187    pub fn get_borrow(&self) -> &'a AtomicBorrow {
188        self.borrow
189    }
190
191    /// Gets the inner data.
192    #[inline]
193    pub fn ptr(&self) -> *const T {
194        self.data
195    }
196
197    /// Gets the inner data without releasing the borrow.
198    #[inline]
199    pub fn forget(self) -> *const T {
200        let ptr = self.ptr();
201        std::mem::forget(self);
202        ptr
203    }
204}
205
206impl<'a, T> Deref for SharedGuard<'a, T> {
207    type Target = T;
208
209    #[inline]
210    fn deref(&self) -> &Self::Target {
211        unsafe { &*self.data }
212    }
213}
214
215impl<'a, T> Drop for SharedGuard<'a, T> {
216    #[inline]
217    fn drop(&mut self) {
218        self.borrow.release();
219    }
220}
221
222/// A guard that releases a unique reference when dropped.
223pub struct UniqueGuard<'a, T> {
224    data: *mut T,
225    borrow: &'a AtomicBorrow,
226}
227
228impl<'a, T> UniqueGuard<'a, T> {
229    /// Creates a new [`UniqueGuard`].
230    #[inline]
231    pub fn new(data: &'a mut T, borrow: &'a AtomicBorrow) -> Self {
232        Self { data, borrow }
233    }
234
235    /// Tries to borrow the data.
236    ///
237    /// # Safety
238    /// * Any borrows of `data` must be registered with `borrow`.
239    /// * `data` must be a valid pointer for the entire lifetime of `self`.
240    #[inline]
241    pub unsafe fn try_new(data: *mut T, borrow: &'a AtomicBorrow) -> Option<Self> {
242        if borrow.borrow_mut() {
243            Some(Self { data, borrow })
244        } else {
245            None
246        }
247    }
248
249    /// Spins until the data can be borrowed.
250    ///
251    /// # Safety
252    /// * Any borrows of `data` must be registered with `borrow`.
253    /// * `data` must be a valid pointer for the entire lifetime of `self`.
254    #[inline]
255    pub unsafe fn spin(data: *mut T, borrow: &'a AtomicBorrow) -> Self {
256        borrow.spin_borrow_mut();
257        Self { data, borrow }
258    }
259
260    /// Gets the inner [`AtomicBorrow`].
261    #[inline]
262    pub fn get_borrow(&self) -> &'a AtomicBorrow {
263        self.borrow
264    }
265
266    /// Gets the inner data.
267    #[inline]
268    pub fn ptr(&self) -> *mut T {
269        self.data
270    }
271
272    /// Gets the inner data without releasing the borrow.
273    #[inline]
274    pub fn forget(self) -> *mut T {
275        let ptr = self.ptr();
276        std::mem::forget(self);
277        ptr
278    }
279}
280
281impl<'a, T> Deref for UniqueGuard<'a, T> {
282    type Target = T;
283
284    #[inline]
285    fn deref(&self) -> &Self::Target {
286        unsafe { &*self.data }
287    }
288}
289
290impl<'a, T> DerefMut for UniqueGuard<'a, T> {
291    #[inline]
292    fn deref_mut(&mut self) -> &mut Self::Target {
293        unsafe { &mut *self.data }
294    }
295}
296
297impl<'a, T> Drop for UniqueGuard<'a, T> {
298    #[inline]
299    fn drop(&mut self) {
300        self.borrow.release_mut();
301    }
302}
303
304#[cfg(test)]
305mod tests {
306    use super::*;
307
308    #[test]
309    fn atomic_borrow() {
310        let borrow = AtomicBorrow::new();
311
312        assert!(borrow.borrow());
313        assert!(borrow.borrow());
314
315        assert!(!borrow.borrow_mut());
316
317        borrow.release();
318        borrow.release();
319
320        assert!(borrow.borrow_mut());
321
322        assert!(!borrow.borrow());
323
324        borrow.release_mut();
325    }
326}