osiris/sync/
spinlock.rs

1//! Synchronization primitives.
2
3use core::cell::UnsafeCell;
4use core::ptr::NonNull;
5use core::sync::atomic::AtomicBool;
6use core::sync::atomic::Ordering;
7
8/// A mutual exclusion primitive, facilitating busy-waiting.
9#[proc_macros::fmt]
10pub struct SpinLock {
11    lock: AtomicBool,
12}
13
14#[allow(dead_code)]
15impl SpinLock {
16    /// Creates a new SpinLock.
17    pub const fn new() -> Self {
18        SpinLock {
19            lock: AtomicBool::new(false),
20        }
21    }
22
23    /// Waits until the SpinLock can be acquired and lock it.
24    pub fn lock(&self) {
25        let lock = &self.lock;
26
27        if lock.load(Ordering::Relaxed) {
28            hal::asm::nop!();
29        }
30
31        loop {
32            if lock
33                .compare_exchange_weak(false, true, Ordering::SeqCst, Ordering::Relaxed)
34                .is_ok()
35            {
36                break;
37            }
38        }
39    }
40
41    /// Tries to lock the SpinLock.
42    /// Returns `true` if the lock was acquired.
43    pub fn try_lock(&self) -> bool {
44        !self.lock.swap(true, Ordering::Acquire)
45    }
46
47    /// Unlocks the SpinLock.
48    /// Returns `true` if the lock was released.
49    ///
50    /// # Safety
51    /// Precondition: The SpinLock must be locked by the current thread.
52    /// Postcondition: The SpinLock is unlocked.
53    pub unsafe fn unlock(&self) {
54        self.lock.store(false, Ordering::Release)
55    }
56}
57
58/// A guard that releases the SpinLock when dropped.
59#[proc_macros::fmt]
60pub struct SpinLockGuard<'a, T: ?Sized> {
61    lock: &'a SpinLock,
62    value: NonNull<T>,
63    marker: core::marker::PhantomData<&'a mut T>,
64}
65
66impl<T: ?Sized> core::ops::Deref for SpinLockGuard<'_, T> {
67    type Target = T;
68
69    #[inline]
70    fn deref(&self) -> &T {
71        unsafe { self.value.as_ref() }
72    }
73}
74
75impl<T: ?Sized> core::ops::DerefMut for SpinLockGuard<'_, T> {
76    fn deref_mut(&mut self) -> &mut T {
77        unsafe { self.value.as_mut() }
78    }
79}
80
81impl<T: ?Sized> Drop for SpinLockGuard<'_, T> {
82    fn drop(&mut self) {
83        unsafe {
84            self.lock.unlock();
85        }
86    }
87}
88
89/// A mutual exclusion primitive that allows at most one thread to access a resource at a time.
90pub struct SpinLocked<T> {
91    lock: SpinLock,
92    value: UnsafeCell<T>,
93}
94
95unsafe impl<T> Sync for SpinLocked<T> {}
96
97/// Test
98#[allow(dead_code)]
99impl<T> SpinLocked<T> {
100    /// Creates a new SpinLocked.
101    pub const fn new(value: T) -> Self {
102        SpinLocked {
103            lock: SpinLock::new(),
104            value: UnsafeCell::new(value),
105        }
106    }
107
108    /// Locks the SpinLocked and returns a guard that releases the lock when dropped.
109    pub fn lock(&self) -> SpinLockGuard<'_, T> {
110        self.lock.lock();
111        SpinLockGuard {
112            lock: &self.lock,
113            value: unsafe { NonNull::new_unchecked(self.value.get()) },
114            marker: core::marker::PhantomData,
115        }
116    }
117
118    /// Tries to lock the SpinLocked and returns a guard that releases the lock when dropped.
119    pub fn try_lock(&self) -> Option<SpinLockGuard<'_, T>> {
120        if self.lock.try_lock() {
121            Some(SpinLockGuard {
122                lock: &self.lock,
123                value: unsafe { NonNull::new_unchecked(self.value.get()) },
124                marker: core::marker::PhantomData,
125            })
126        } else {
127            None
128        }
129    }
130}