1use core::convert::Into;
2use core::mem::ManuallyDrop;
3use core::panic::Location;
4use core::sync::atomic::{AtomicPtr, AtomicU8, AtomicUsize, Ordering};
5use log::warn;
6
7pub type Spinlock<T: ?Sized> = lock_api::Mutex<RawSpinlock, T>;
9
10pub type SpinlockGuard<'a, T: ?Sized> = lock_api::MutexGuard<'a, RawSpinlock, T>;
14
15pub type MappedSpinlockGuard<'a, T: ?Sized> = lock_api::MappedMutexGuard<'a, RawSpinlock, T>;
16
17pub trait SpinlockGuardExt {
19 fn unlock_no_interrupts(this: Self);
22}
23
24impl<T> SpinlockGuardExt for SpinlockGuard<'_, T> {
25 fn unlock_no_interrupts(this: Self) {
26 let this = ManuallyDrop::new(this);
27 unsafe {
28 let spinlock = Self::mutex(&this).raw();
29 spinlock.unlock_no_interrupts();
30 }
31 }
32}
33
34#[derive(Debug, Copy, Clone, Eq, PartialEq)]
35enum State {
36 Unlocked,
37 Locked,
38}
39
40impl State {
41 const fn const_into_u8(self) -> u8 {
42 match self {
43 State::Unlocked => 0,
44 State::Locked => 1,
45 }
46 }
47
48 const fn const_from_u8(value: u8) -> Result<Self, ()> {
49 match value {
50 0 => Ok(State::Unlocked),
51 1 => Ok(State::Locked),
52 _ => Err(())
53 }
54 }
55}
56
57impl From<State> for u8 {
58 fn from(value: State) -> Self {
59 value.const_into_u8()
60 }
61}
62
63impl TryFrom<u8> for State {
64 type Error = ();
65
66 fn try_from(value: u8) -> Result<Self, Self::Error> {
67 Self::const_from_u8(value)
68 }
69}
70
71pub struct RawSpinlock {
72 state: AtomicU8,
73 irq_state: AtomicUsize,
74 location: AtomicPtr<Location<'static>>,
75}
76
77unsafe impl Send for RawSpinlock {}
78
79unsafe impl Sync for RawSpinlock {}
80
81impl RawSpinlock {
82 unsafe fn unlock_no_interrupts(&self) {
83 let old_state = self.state.swap(State::Unlocked.into(), Ordering::Release);
84 let old_state = State::try_from(old_state).expect("Spinlock in undefined state");
85
86 match old_state {
87 State::Unlocked => unreachable!("Mutex was unlocked while unlocked"),
88 State::Locked => {},
89 }
90 }
91
92 fn lock_location(&self) -> Option<&'static Location<'static>> {
93 let location = self.location.load(Ordering::Relaxed);
94 unsafe { location.as_ref::<'static>() }
95 }
96}
97
98unsafe impl lock_api::RawMutex for RawSpinlock {
99 const INIT: Self = Self {
100 state: AtomicU8::new(State::Unlocked.const_into_u8()),
101 irq_state: AtomicUsize::new(0),
102 location: AtomicPtr::new(core::ptr::null_mut()),
103 };
104
105 type GuardMarker = lock_api::GuardNoSend; #[track_caller]
108 fn lock(&self) {
109 let irq_state = crate::bridge::irq::disable();
110
111 let mut p = true;
112 while let Err(_) = self.state.compare_exchange_weak(
113 State::Unlocked.into(),
114 State::Locked.into(),
115 Ordering::Acquire,
116 Ordering::Relaxed
117 ) {
118 core::hint::spin_loop();
119 if p {
120 p = false;
121 warn!("locked at {:?}", self.lock_location());
122 }
123 }
124
125 self.irq_state.store(irq_state, Ordering::Relaxed);
126 self.location.store(Location::caller() as *const _ as *mut _, Ordering::Relaxed);
127 }
128
129 fn try_lock(&self) -> bool {
130 let irq_state = crate::bridge::irq::disable();
131 let success = self.state.compare_exchange(
132 State::Unlocked.into(),
133 State::Locked.into(),
134 Ordering::Acquire,
135 Ordering::Relaxed
136 ).is_ok();
137
138 if !success { crate::bridge::irq::set(irq_state) }
139 else { self.irq_state.store(irq_state, Ordering::Relaxed) }
140
141 success
142 }
143
144 unsafe fn unlock(&self) {
145 let old_irq_state = self.irq_state.load(Ordering::Relaxed);
146 let old_state = self.state.swap(State::Unlocked.into(), Ordering::Release);
147 let old_state = State::try_from(old_state).expect("Spinlock in undefined state");
148
149 match old_state {
150 State::Unlocked => unreachable!("Mutex was unlocked while unlocked"),
151 State::Locked => crate::bridge::irq::set(old_irq_state),
152 }
153 }
154}