Skip to main content

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}