#![no_std] mod guard; use core::{ cell::UnsafeCell, marker::PhantomData, sync::atomic::{AtomicBool, Ordering}, }; use eonix_sync_base::{Relax, SpinRelax}; pub use guard::{SpinGuard, UnlockedSpinGuard}; pub trait SpinContext { fn save() -> Self; fn restore(self); } pub trait ContextUnlock: SpinContext { type Unlocked: UnlockedContext; fn unlock(self) -> Self::Unlocked; } pub trait UnlockedContext { type Relocked: ContextUnlock; fn relock(self) -> Self::Relocked; } pub struct NoContext; pub struct DisablePreemption(); //// A spinlock is a lock that uses busy-waiting to acquire the lock. /// It is useful for short critical sections where the overhead of a context switch /// is too high. #[derive(Debug, Default)] pub struct Spin where T: ?Sized, { _phantom: PhantomData, locked: AtomicBool, value: UnsafeCell, } impl Spin where R: Relax, { pub const fn new(value: T) -> Self { Self { locked: AtomicBool::new(false), value: UnsafeCell::new(value), _phantom: PhantomData, } } pub fn into_inner(mut self) -> T { assert!( !*self.locked.get_mut(), "Spin::take(): Cannot take a locked Spin" ); self.value.into_inner() } } impl Spin where T: ?Sized, { /// # Safety /// This function is unsafe because the caller MUST ensure that the protected /// value is no longer accessed after calling this function. unsafe fn do_unlock(&self) { let locked = self.locked.swap(false, Ordering::Release); debug_assert!(locked, "Spin::unlock(): Unlocking an unlocked lock"); } } impl Spin where T: ?Sized, R: Relax, { pub fn lock_with_context(&self, context: C) -> SpinGuard where C: SpinContext, { self.do_lock(); SpinGuard::new( self, unsafe { // SAFETY: We are holding the lock, so we can safely access the value. &mut *self.value.get() }, context, ) } pub fn lock(&self) -> SpinGuard { self.lock_with_context(DisablePreemption::save()) } pub fn get_mut(&mut self) -> &mut T { // SAFETY: The exclusive access to the lock is guaranteed by the borrow checker. unsafe { &mut *self.value.get() } } fn do_lock(&self) { while let Err(_) = self.locked .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed) { R::relax(); } } } // SAFETY: As long as the value protected by the lock is able to be shared between threads, // we can send the lock between threads. unsafe impl Send for Spin where T: ?Sized + Send {} // SAFETY: As long as the value protected by the lock is able to be shared between threads, // we can provide exclusive access guarantees to the lock. unsafe impl Sync for Spin where T: ?Sized + Send {} impl SpinContext for NoContext { fn save() -> Self { Self } fn restore(self) {} } impl ContextUnlock for NoContext { type Unlocked = NoContext; fn unlock(self) -> Self::Unlocked { self } } impl UnlockedContext for NoContext { type Relocked = NoContext; fn relock(self) -> Self::Relocked { self } } impl SpinContext for DisablePreemption { fn save() -> Self { eonix_preempt::disable(); Self() } fn restore(self) { eonix_preempt::enable(); } } impl ContextUnlock for DisablePreemption { type Unlocked = DisablePreemption; fn unlock(self) -> Self::Unlocked { eonix_preempt::enable(); self } } impl UnlockedContext for DisablePreemption { type Relocked = DisablePreemption; fn relock(self) -> Self::Relocked { eonix_preempt::disable(); self } }