parking_lot/raw_mutex.rs
1use crate::{deadlock, util};
2use core::{
3 sync::atomic::{AtomicU8, Ordering},
4 time::Duration,
5};
6use lock_api::RawMutex as RawMutex_;
7use parking_lot_core::{self, DEFAULT_PARK_TOKEN, ParkResult, SpinWait, UnparkResult, UnparkToken};
8use std::time::Instant;
9
10// UnparkToken used to indicate that the target thread should attempt to
11// lock the mutex again as soon as it is unparked.
12pub(crate) const TOKEN_NORMAL: UnparkToken = UnparkToken(0);
13
14// UnparkToken used to indicate that the mutex is being handed off to the target
15// thread directly without unlocking it.
16pub(crate) const TOKEN_HANDOFF: UnparkToken = UnparkToken(1);
17
18/// This bit is set in the `state` of a `RawMutex` when that mutex is locked by some thread.
19const LOCKED_BIT: u8 = 0b01;
20/// This bit is set in the `state` of a `RawMutex` just before parking a thread. A thread is being
21/// parked if it wants to lock the mutex, but it is currently being held by some other thread.
22const PARKED_BIT: u8 = 0b10;
23
24/// Raw mutex type backed by the parking lot.
25pub struct RawMutex {
26 /// This atomic integer holds the current state of the mutex instance. Only the two lowest bits
27 /// are used. See `LOCKED_BIT` and `PARKED_BIT` for the bitmask for these bits.
28 ///
29 /// # State table:
30 ///
31 /// PARKED_BIT | LOCKED_BIT | Description
32 /// 0 | 0 | The mutex is not locked, nor is anyone waiting for it.
33 /// -----------+------------+------------------------------------------------------------------
34 /// 0 | 1 | The mutex is locked by exactly one thread. No other thread is
35 /// | | waiting for it.
36 /// -----------+------------+------------------------------------------------------------------
37 /// 1 | 0 | The mutex is not locked. One or more threads are parked or about to
38 /// | | park. At least one of the parked threads is just about to be
39 /// | | unparked, or a thread heading for parking might abort the park.
40 /// -----------+------------+------------------------------------------------------------------
41 /// 1 | 1 | The mutex is locked by exactly one thread. One or more threads are
42 /// | | parked or about to park, waiting for the lock to become available.
43 /// | | In this state, PARKED_BIT is only ever cleared when a bucket lock
44 /// | | is held (i.e. in a parking_lot_core callback). This ensures that
45 /// | | we never end up in a situation where there are parked threads but
46 /// | | PARKED_BIT is not set (which would result in those threads
47 /// | | potentially never getting woken up).
48 state: AtomicU8,
49}
50
51unsafe impl lock_api::RawMutex for RawMutex {
52 const INIT: RawMutex = RawMutex {
53 state: AtomicU8::new(0),
54 };
55
56 type Guard = crate::GuardMarker;
57
58 #[inline]
59 fn lock(&self) -> Self::Guard {
60 let guard = if self
61 .state
62 .compare_exchange_weak(0, LOCKED_BIT, Ordering::Acquire, Ordering::Relaxed)
63 .is_err()
64 {
65 self.lock_slow(None).unwrap()
66 } else {
67 Self::Guard::new()
68 };
69 unsafe { deadlock::acquire_resource(core::ptr::from_ref(self).addr()) };
70 guard
71 }
72
73 #[inline]
74 fn try_lock(&self) -> Option<Self::Guard> {
75 self.state
76 .try_update(Ordering::Acquire, Ordering::Relaxed, |state| {
77 (state & LOCKED_BIT == 0).then_some(state | LOCKED_BIT)
78 })
79 .ok()?;
80 unsafe { deadlock::acquire_resource(core::ptr::from_ref(self).addr()) };
81 Some(Self::Guard::new())
82 }
83
84 #[inline]
85 unsafe fn unlock(&self, _guard: Self::Guard) {
86 unsafe { deadlock::release_resource(core::ptr::from_ref(self).addr()) };
87 unsafe { self.unlock_inner() };
88 }
89
90 #[inline]
91 fn is_locked(&self) -> bool {
92 let state = self.state.load(Ordering::Relaxed);
93 state & LOCKED_BIT != 0
94 }
95}
96
97unsafe impl lock_api::RawMutexFair for RawMutex {
98 #[inline]
99 unsafe fn unlock_fair(&self, _guard: Self::Guard) {
100 unsafe { deadlock::release_resource(core::ptr::from_ref(self).addr()) };
101 if self
102 .state
103 .compare_exchange(LOCKED_BIT, 0, Ordering::Release, Ordering::Relaxed)
104 .is_err()
105 {
106 self.unlock_fair_slow();
107 }
108 }
109
110 #[inline]
111 unsafe fn bump(&self, _guard: &mut Self::Guard) {
112 if self.state.load(Ordering::Relaxed) & PARKED_BIT != 0 {
113 self.bump_slow();
114 }
115 }
116}
117
118unsafe impl lock_api::RawMutexTimed for RawMutex {
119 type Duration = Duration;
120 type Instant = Instant;
121
122 #[inline]
123 fn try_lock_until(&self, timeout: Instant) -> Option<Self::Guard> {
124 let guard = if self
125 .state
126 .compare_exchange_weak(0, LOCKED_BIT, Ordering::Acquire, Ordering::Relaxed)
127 .is_ok()
128 {
129 Self::Guard::new()
130 } else {
131 self.lock_slow(Some(timeout))?
132 };
133 unsafe { deadlock::acquire_resource(core::ptr::from_ref(self).addr()) };
134 Some(guard)
135 }
136
137 #[inline]
138 fn try_lock_for(&self, timeout: Duration) -> Option<Self::Guard> {
139 let guard = if self
140 .state
141 .compare_exchange_weak(0, LOCKED_BIT, Ordering::Acquire, Ordering::Relaxed)
142 .is_ok()
143 {
144 Self::Guard::new()
145 } else {
146 self.lock_slow(util::to_deadline(timeout))?
147 };
148 unsafe { deadlock::acquire_resource(core::ptr::from_ref(self).addr()) };
149 Some(guard)
150 }
151}
152
153impl RawMutex {
154 /// Unlocks the mutex without updating the deadlock detector.
155 ///
156 /// # Safety
157 ///
158 /// The caller must own the mutex.
159 #[inline]
160 pub(crate) unsafe fn unlock_inner(&self) {
161 let state = self.state.fetch_sub(LOCKED_BIT, Ordering::Release);
162 if state & PARKED_BIT != 0 {
163 self.unlock_slow();
164 }
165 }
166
167 // Used by Condvar when requeuing threads to us, must be called while
168 // holding the queue lock.
169 #[inline]
170 pub(crate) fn mark_parked_if_locked(&self) -> bool {
171 self.state
172 .try_update(Ordering::Relaxed, Ordering::Relaxed, |state| {
173 (state & LOCKED_BIT != 0).then_some(state | PARKED_BIT)
174 })
175 .is_ok()
176 }
177
178 // Used by Condvar when requeuing threads to us, must be called while
179 // holding the queue lock.
180 #[inline]
181 pub(crate) fn mark_parked(&self) {
182 self.state.fetch_or(PARKED_BIT, Ordering::Relaxed);
183 }
184
185 #[cold]
186 fn lock_slow(&self, timeout: Option<Instant>) -> Option<crate::GuardMarker> {
187 let mut spinwait = SpinWait::new();
188 let mut state = self.state.load(Ordering::Relaxed);
189 loop {
190 // Grab the lock if it isn't locked, even if there is a queue on it
191 if state & LOCKED_BIT == 0 {
192 match self.state.compare_exchange_weak(
193 state,
194 state | LOCKED_BIT,
195 Ordering::Acquire,
196 Ordering::Relaxed,
197 ) {
198 Ok(_) => return Some(crate::GuardMarker::new()),
199 Err(x) => state = x,
200 }
201 continue;
202 }
203
204 // If there is no queue, try spinning a few times
205 if state & PARKED_BIT == 0 && spinwait.spin() {
206 state = self.state.load(Ordering::Relaxed);
207 continue;
208 }
209
210 // Set the parked bit
211 if state & PARKED_BIT == 0
212 && let Err(x) = self.state.compare_exchange_weak(
213 state,
214 state | PARKED_BIT,
215 Ordering::Relaxed,
216 Ordering::Relaxed,
217 )
218 {
219 state = x;
220 continue;
221 }
222
223 // Park our thread until we are woken up by an unlock
224 let addr = core::ptr::from_ref(self).addr();
225 let validate = || self.state.load(Ordering::Relaxed) == LOCKED_BIT | PARKED_BIT;
226 let before_sleep = || {};
227 let timed_out = |_, was_last_thread| {
228 // Clear the parked bit if we were the last parked thread
229 if was_last_thread {
230 self.state.fetch_and(!PARKED_BIT, Ordering::Relaxed);
231 }
232 };
233 // SAFETY:
234 // * `addr` is an address we control.
235 // * `validate`/`timed_out` does not panic or call into any function of `parking_lot`.
236 // * `before_sleep` does not call `park`, nor does it panic.
237 match unsafe {
238 parking_lot_core::park(
239 addr,
240 validate,
241 before_sleep,
242 timed_out,
243 DEFAULT_PARK_TOKEN,
244 timeout,
245 )
246 } {
247 // The thread that unparked us passed the lock on to us
248 // directly without unlocking it.
249 ParkResult::Unparked(TOKEN_HANDOFF) => return Some(crate::GuardMarker::new()),
250
251 // We were unparked normally, try acquiring the lock again
252 ParkResult::Unparked(_) => (),
253
254 // The validation function failed, try locking again
255 ParkResult::Invalid => (),
256
257 // Timeout expired
258 ParkResult::TimedOut => return None,
259 }
260
261 // Loop back and try locking again
262 spinwait.reset();
263 state = self.state.load(Ordering::Relaxed);
264 }
265 }
266
267 #[cold]
268 fn unlock_slow(&self) {
269 let addr = core::ptr::from_ref(self).addr();
270 let callback = |result: UnparkResult| {
271 // Ownership has already been released. Preserve LOCKED_BIT since
272 // another thread may have acquired the mutex in the meantime.
273 if !result.have_more_threads {
274 self.state.fetch_and(!PARKED_BIT, Ordering::Relaxed);
275 }
276 TOKEN_NORMAL
277 };
278 // SAFETY:
279 // * `addr` is an address we control.
280 // * `callback` does not panic or call into any function of `parking_lot`.
281 unsafe {
282 parking_lot_core::unpark_one(addr, callback);
283 }
284 }
285
286 #[cold]
287 fn unlock_fair_slow(&self) {
288 // Unpark one thread and leave the parked bit set if there might
289 // still be parked threads on this address.
290 let addr = core::ptr::from_ref(self).addr();
291 let callback = |result: UnparkResult| {
292 // If we are using a fair unlock then we should keep the
293 // mutex locked and hand it off to the unparked thread.
294 if result.unparked_threads != 0 {
295 // Clear the parked bit if there are no more parked
296 // threads.
297 if !result.have_more_threads {
298 self.state.store(LOCKED_BIT, Ordering::Relaxed);
299 }
300 return TOKEN_HANDOFF;
301 }
302
303 // Clear the locked bit, and the parked bit as well if there
304 // are no more parked threads.
305 if result.have_more_threads {
306 self.state.store(PARKED_BIT, Ordering::Release);
307 } else {
308 self.state.store(0, Ordering::Release);
309 }
310 TOKEN_NORMAL
311 };
312 // SAFETY:
313 // * `addr` is an address we control.
314 // * `callback` does not panic or call into any function of `parking_lot`.
315 unsafe {
316 parking_lot_core::unpark_one(addr, callback);
317 }
318 }
319
320 #[cold]
321 fn bump_slow(&self) {
322 unsafe { deadlock::release_resource(core::ptr::from_ref(self).addr()) };
323 self.unlock_fair_slow();
324 self.lock();
325 }
326}