lib.rs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. #![no_std]
  2. mod guard;
  3. use core::{
  4. cell::UnsafeCell,
  5. marker::PhantomData,
  6. sync::atomic::{AtomicBool, Ordering},
  7. };
  8. use eonix_sync_base::{Relax, SpinRelax};
  9. pub use guard::{SpinGuard, UnlockedSpinGuard};
  10. pub trait SpinContext {
  11. fn save() -> Self;
  12. fn restore(self);
  13. }
  14. pub trait ContextUnlock: SpinContext {
  15. type Unlocked: UnlockedContext<Relocked = Self>;
  16. fn unlock(self) -> Self::Unlocked;
  17. }
  18. pub trait UnlockedContext {
  19. type Relocked: ContextUnlock<Unlocked = Self>;
  20. fn relock(self) -> Self::Relocked;
  21. }
  22. pub struct NoContext;
  23. pub struct DisablePreemption();
  24. //// A spinlock is a lock that uses busy-waiting to acquire the lock.
  25. /// It is useful for short critical sections where the overhead of a context switch
  26. /// is too high.
  27. #[derive(Debug, Default)]
  28. pub struct Spin<T, R = SpinRelax>
  29. where
  30. T: ?Sized,
  31. {
  32. _phantom: PhantomData<R>,
  33. locked: AtomicBool,
  34. value: UnsafeCell<T>,
  35. }
  36. impl<T, R> Spin<T, R>
  37. where
  38. R: Relax,
  39. {
  40. pub const fn new(value: T) -> Self {
  41. Self {
  42. locked: AtomicBool::new(false),
  43. value: UnsafeCell::new(value),
  44. _phantom: PhantomData,
  45. }
  46. }
  47. pub fn into_inner(mut self) -> T {
  48. assert!(
  49. !*self.locked.get_mut(),
  50. "Spin::take(): Cannot take a locked Spin"
  51. );
  52. self.value.into_inner()
  53. }
  54. }
  55. impl<T, R> Spin<T, R>
  56. where
  57. T: ?Sized,
  58. {
  59. /// # Safety
  60. /// This function is unsafe because the caller MUST ensure that the protected
  61. /// value is no longer accessed after calling this function.
  62. unsafe fn do_unlock(&self) {
  63. let locked = self.locked.swap(false, Ordering::Release);
  64. debug_assert!(locked, "Spin::unlock(): Unlocking an unlocked lock");
  65. }
  66. }
  67. impl<T, R> Spin<T, R>
  68. where
  69. T: ?Sized,
  70. R: Relax,
  71. {
  72. pub fn lock_with_context<C>(&self, context: C) -> SpinGuard<T, C, R>
  73. where
  74. C: SpinContext,
  75. {
  76. self.do_lock();
  77. SpinGuard::new(
  78. self,
  79. unsafe {
  80. // SAFETY: We are holding the lock, so we can safely access the value.
  81. &mut *self.value.get()
  82. },
  83. context,
  84. )
  85. }
  86. pub fn lock(&self) -> SpinGuard<T, DisablePreemption, R> {
  87. self.lock_with_context(DisablePreemption::save())
  88. }
  89. pub fn get_mut(&mut self) -> &mut T {
  90. // SAFETY: The exclusive access to the lock is guaranteed by the borrow checker.
  91. unsafe { &mut *self.value.get() }
  92. }
  93. fn do_lock(&self) {
  94. while let Err(_) =
  95. self.locked
  96. .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
  97. {
  98. R::relax();
  99. }
  100. }
  101. }
  102. // SAFETY: As long as the value protected by the lock is able to be shared between threads,
  103. // we can send the lock between threads.
  104. unsafe impl<T, R> Send for Spin<T, R> where T: ?Sized + Send {}
  105. // SAFETY: As long as the value protected by the lock is able to be shared between threads,
  106. // we can provide exclusive access guarantees to the lock.
  107. unsafe impl<T, R> Sync for Spin<T, R> where T: ?Sized + Send {}
  108. impl SpinContext for NoContext {
  109. fn save() -> Self {
  110. Self
  111. }
  112. fn restore(self) {}
  113. }
  114. impl ContextUnlock for NoContext {
  115. type Unlocked = NoContext;
  116. fn unlock(self) -> Self::Unlocked {
  117. self
  118. }
  119. }
  120. impl UnlockedContext for NoContext {
  121. type Relocked = NoContext;
  122. fn relock(self) -> Self::Relocked {
  123. self
  124. }
  125. }
  126. impl SpinContext for DisablePreemption {
  127. fn save() -> Self {
  128. eonix_preempt::disable();
  129. Self()
  130. }
  131. fn restore(self) {
  132. eonix_preempt::enable();
  133. }
  134. }
  135. impl ContextUnlock for DisablePreemption {
  136. type Unlocked = DisablePreemption;
  137. fn unlock(self) -> Self::Unlocked {
  138. eonix_preempt::enable();
  139. self
  140. }
  141. }
  142. impl UnlockedContext for DisablePreemption {
  143. type Relocked = DisablePreemption;
  144. fn relock(self) -> Self::Relocked {
  145. eonix_preempt::disable();
  146. self
  147. }
  148. }