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