spin_sync/mutex.rs
1// Copyright 2020 Shin Yoshida
2//
3// "LGPL-3.0-or-later OR Apache-2.0 OR BSD-2-Clause"
4//
5// This is part of spin-sync
6//
7// spin-sync is free software: you can redistribute it and/or modify
8// it under the terms of the GNU Lesser General Public License as published by
9// the Free Software Foundation, either version 3 of the License, or
10// (at your option) any later version.
11//
12// spin-sync is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15// GNU Lesser General Public License for more details.
16//
17// You should have received a copy of the GNU Lesser General Public License
18// along with spin-sync. If not, see <http://www.gnu.org/licenses/>.
19//
20//
21// Licensed under the Apache License, Version 2.0 (the "License");
22// you may not use this file except in compliance with the License.
23// You may obtain a copy of the License at
24//
25// http://www.apache.org/licenses/LICENSE-2.0
26//
27// Unless required by applicable law or agreed to in writing, software
28// distributed under the License is distributed on an "AS IS" BASIS,
29// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30// See the License for the specific language governing permissions and
31// limitations under the License.
32//
33//
34// Redistribution and use in source and binary forms, with or without modification, are permitted
35// provided that the following conditions are met:
36//
37// 1. Redistributions of source code must retain the above copyright notice, this list of
38// conditions and the following disclaimer.
39// 2. Redistributions in binary form must reproduce the above copyright notice, this
40// list of conditions and the following disclaimer in the documentation and/or other
41// materials provided with the distribution.
42//
43// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
44// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
45// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
46// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
47// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
48// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
49// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
50// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
51// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
52// POSSIBILITY OF SUCH DAMAGE.
53
54use std::cell::UnsafeCell;
55use std::fmt;
56use std::ops::{Deref, DerefMut};
57use std::panic::{RefUnwindSafe, UnwindSafe};
58use std::sync::atomic::{AtomicU8, Ordering};
59
60use crate::misc::{PhantomMutex, PhantomMutexGuard};
61use crate::result::{LockResult, PoisonError, TryLockError, TryLockResult};
62
63/// A mutual exclusion primitive useful for protecting shared data.
64///
65/// It behaves like std::sync::Mutex except for using spinlock.
66/// What is more, the constructor is a const function; i.e. it is possible to declare
67/// static Mutex<T> variable as long as the inner data can be built statically.
68///
69/// This mutex will block threads waiting for the lock to become available. The
70/// mutex can also be statically initialized or created via a [`new`]
71/// constructor. Each mutex has a type parameter which represents the data that
72/// it is protecting. The data can only be accessed through the RAII guards
73/// returned from [`lock`] and [`try_lock`], which guarantees that the data is only
74/// ever accessed when the mutex is locked.
75///
76/// # Poisoning
77///
78/// The mutexes in this module implement a strategy called "poisoning" where a
79/// mutex is considered poisoned whenever a thread panics while holding the
80/// mutex. Once a mutex is poisoned, all other threads are unable to access the
81/// data by default as it is likely tainted.
82///
83/// For a mutex, this means that the [`lock`] and [`try_lock`] methods return a
84/// `Result` which indicates whether a mutex has been poisoned or not. Most
85/// usage of a mutex will simply `unwrap()` these results, propagating panics
86/// among threads to ensure that a possibly invalid invariant is not witnessed.
87///
88/// A poisoned mutex, however, does not prevent all access to the underlying
89/// data. The [`PoisonError`] type has an `into_inner` method which will return
90/// the guard that would have otherwise been returned on a successful lock. This
91/// allows access to the data, despite the lock being poisoned.
92///
93/// [`new`]: #method.new
94/// [`lock`]: #method.lock
95/// [`try_lock`]: #method.try_lock
96/// [`PoisonError`]: type.PoisonError.html
97///
98/// # Examples
99///
100/// Protect a variable (non-atomically) and update it in worker threads.
101///
102/// ```
103/// use spin_sync::Mutex;
104/// use std::thread;
105///
106/// const WORKER_NUM: usize = 10;
107///
108/// // We can declare static Mutex<usize> variable because Mutex::new is const.
109/// static MUTEX: Mutex<usize> = Mutex::new(0);
110///
111/// let mut handles = Vec::with_capacity(WORKER_NUM);
112///
113/// // Create worker threads to inclement the value by 1.
114/// for _ in 0..WORKER_NUM {
115/// let handle = thread::spawn(move || {
116/// let mut num = MUTEX.lock().unwrap();
117/// *num += 1;
118/// });
119///
120/// handles.push(handle);
121/// }
122///
123/// // Wait for the all worker threads are finished.
124/// for handle in handles {
125/// handle.join().unwrap();
126/// }
127///
128/// // Make sure the value is incremented by the worker count.
129/// let num = MUTEX.lock().unwrap();
130/// assert_eq!(WORKER_NUM, *num);
131/// ```
132///
133/// To recover from a poisoned mutex:
134///
135/// ```
136/// use spin_sync::Mutex;
137/// use std::sync::Arc;
138/// use std::thread;
139///
140/// // Like std::sync::Mutex, it can be declare as local variable, of course.
141/// let mutex = Arc::new(Mutex::new(0));
142/// let c_mutex = mutex.clone();
143///
144/// let _ = thread::spawn(move || -> () {
145/// // This thread will acquire the mutex first, unwrapping the result of
146/// // `lock` because the lock has not been poisoned.
147/// let _guard = c_mutex.lock().unwrap();
148///
149/// // This panic while holding the lock (`_guard` is in scope) will poison
150/// // the mutex.
151/// panic!();
152/// }).join();
153///
154/// // Here, the mutex has been poisoned.
155/// assert_eq!(true, mutex.is_poisoned());
156///
157/// // The returned result can be pattern matched on to return the underlying
158/// // guard on both branches.
159/// let mut guard = match mutex.lock() {
160/// Ok(guard) => guard,
161/// Err(poisoned) => poisoned.into_inner(),
162/// };
163///
164/// *guard += 1;
165/// assert_eq!(1, *guard);
166/// ```
167pub struct Mutex<T: ?Sized> {
168 lock: AtomicU8,
169 _phantom: PhantomMutex<T>,
170 data: UnsafeCell<T>,
171}
172
173impl<T> Mutex<T> {
174 /// Creates a new mutex in an unlocked state ready for use.
175 ///
176 /// unlike to `std::sync::Mutex::new`, this is a const function.
177 /// It can be use for static variable.
178 ///
179 /// # Examples
180 ///
181 /// Declare as a static variable.
182 ///
183 /// ```
184 /// use spin_sync::Mutex;
185 ///
186 /// static MUTEX: Mutex<i32> = Mutex::new(0);
187 /// ```
188 ///
189 /// Declare as a local variable.
190 ///
191 /// ```
192 /// use spin_sync::Mutex;
193 ///
194 /// let mutex = Mutex::new(0);
195 /// ```
196 pub const fn new(t: T) -> Self {
197 Mutex {
198 lock: AtomicU8::new(INIT),
199 data: UnsafeCell::new(t),
200 _phantom: PhantomMutex {},
201 }
202 }
203
204 /// Consumes this mutex and returns the underlying data.
205 ///
206 /// Note that this method won't acquire any lock because we know there is
207 /// no other references to `self`.
208 ///
209 /// # Errors
210 ///
211 /// If another user panicked while holding this mutex, this call wraps
212 /// the result in an error and returns it.
213 ///
214 /// # Examples
215 ///
216 /// ```
217 /// use spin_sync::Mutex;
218 ///
219 /// let mutex = Mutex::new(0);
220 /// assert_eq!(0, mutex.into_inner().unwrap());
221 /// ```
222 pub fn into_inner(self) -> LockResult<T> {
223 let is_err = self.is_poisoned();
224 let data = self.data.into_inner();
225
226 if is_err {
227 Err(PoisonError::new(data))
228 } else {
229 Ok(data)
230 }
231 }
232}
233
234impl<T: ?Sized> Mutex<T> {
235 /// Blocks the current thread until acquiring the lock, and returns an RAII guard object.
236 ///
237 /// The actual flow will be as follows.
238 ///
239 /// 1. User calls this method.
240 /// 1. Blocks until this thread acquires the exclusive lock.
241 /// 1. Creates an RAII guard object.
242 /// 1. Wraps the guard in `Result` and returns it. If this mutex has been
243 /// poisoned, it is wrapped in an `Err`; otherwise wrapped in a `Ok`.
244 /// 1. User accesses to the underlying data through the returned guard.
245 /// (No other thread can access to the data then.)
246 /// 1. The guard is dropped (falls out of scope) and the lock is released.
247 ///
248 /// # Errors
249 ///
250 /// If another user panicked while holding this mutex, this method call wraps
251 /// the guard in an error and returns it.
252 ///
253 /// # Examples
254 ///
255 /// ```
256 /// use spin_sync::Mutex;
257 ///
258 /// let mutex = Mutex::new(0);
259 ///
260 /// let mut guard = mutex.lock().unwrap();
261 /// assert_eq!(0, *guard);
262 ///
263 /// *guard += 1;
264 /// assert_eq!(1, *guard);
265 ///
266 /// assert_eq!(true, mutex.try_lock().is_err());
267 /// ```
268 pub fn lock(&self) -> LockResult<MutexGuard<T>> {
269 loop {
270 match self.do_try_lock() {
271 s if is_locked(s) => std::thread::yield_now(),
272 s if is_poisoned(s) => return Err(PoisonError::new(MutexGuard::new(self))),
273 _ => return Ok(MutexGuard::new(self)),
274 }
275 }
276 }
277
278 /// Attempts to acquire this lock and returns an RAII guard object if succeeded.
279 ///
280 /// Behaves like [`lock`] except for this method returns an error immediately if another
281 /// user is holding the lock.
282 ///
283 /// This method does not block.
284 ///
285 /// The actual flow will be as follows.
286 ///
287 /// 1. User calls this method.
288 /// 1. Tries to acquire the lock. If failed (i.e. if the lock is being held,)
289 /// returns an error immediately and this flow is finished here.
290 /// 1. Creates an RAII guard object.
291 /// 1. Wrapps the guard in `Result` and returns it. If this mutex has been
292 /// poisoned, it is wrapped in an `Err`; otherwise wrapped in an `Ok`.
293 /// 1. User accesses to the underlying data through the returned guard.
294 /// (No other thread can access to the data then.)
295 /// 1. The guard is dropped (falls out of scope) and the lock is released.
296 ///
297 /// # Errors
298 ///
299 /// - If another user is holding this mutex, [`TryLockError::WouldBlock`] is returned.
300 /// - If this function call succeeded to acquire the lock, and if another
301 /// user panicked while holding this mutex, [`TryLockError::Poisoned`] is returned.
302 ///
303 /// [`lock`]: #method.lock
304 /// [`TryLockError::WouldBlock`]: type.TryLockError.html
305 /// [`TryLockError::Poisoned`]: type.TryLockError.html
306 ///
307 /// # Examples
308 ///
309 /// ```
310 /// use spin_sync::Mutex;
311 ///
312 /// let mutex = Mutex::new(0);
313 ///
314 /// // try_lock() fails while another guard is.
315 /// // It doesn't cause a deadlock.
316 /// {
317 /// let _guard = mutex.lock().unwrap();
318 /// assert_eq!(true, mutex.try_lock().is_err());
319 /// }
320 ///
321 /// // try_lock() behaves like lock() if no other guard is.
322 /// {
323 /// let mut guard = mutex.try_lock().unwrap();
324 /// assert_eq!(true, mutex.try_lock().is_err());
325 /// *guard += 1;
326 /// }
327 ///
328 /// let guard = mutex.try_lock().unwrap();
329 /// assert_eq!(1, *guard);
330 /// ```
331 pub fn try_lock(&self) -> TryLockResult<MutexGuard<T>> {
332 match self.do_try_lock() {
333 s if is_locked(s) => Err(TryLockError::WouldBlock),
334 s if is_poisoned(s) => Err(TryLockError::Poisoned(PoisonError::new(MutexGuard::new(
335 self,
336 )))),
337 _ => Ok(MutexGuard::new(self)),
338 }
339 }
340
341 /// Tries to acquire lock and returns the lock status before updated.
342 fn do_try_lock(&self) -> LockStatus {
343 // Assume neither poisoned nor locked at first.
344 let mut expected = INIT;
345
346 loop {
347 let desired = acquire_lock(expected);
348 match self
349 .lock
350 .compare_and_swap(expected, desired, Ordering::Acquire)
351 {
352 s if s == expected => return s, // Succeeded
353 s if is_locked(s) => return s, // Another user is holding the lock.
354 s => expected = s, // Assumption was wrong. Try again.
355 }
356 }
357 }
358
359 /// Determines whether the mutex is poisoned or not.
360 ///
361 /// # Warnings
362 ///
363 /// This function won't acquire any lock. If another thread is active,
364 /// the mutex can become poisoned at any time. You should not trust a `false`
365 /// value for program correctness without additional synchronization.
366 ///
367 /// This behavior is same to `std::sync::Mutex::is_poisoned()`.
368 ///
369 /// # Examples
370 ///
371 /// ```
372 /// use spin_sync::Mutex;
373 /// use std::sync::Arc;
374 /// use std::thread;
375 ///
376 /// let mutex = Arc::new(Mutex::new(0));
377 /// assert_eq!(false, mutex.is_poisoned());
378 ///
379 /// // Panic and poison the mutex.
380 /// {
381 /// let mutex = mutex.clone();
382 ///
383 /// let _ = thread::spawn(move || {
384 /// // This panic while holding the lock (`_guard` is in scope) will poison
385 /// // the mutex.
386 /// let _guard = mutex.lock().unwrap();
387 /// panic!("Poison here");
388 /// }).join();
389 /// }
390 ///
391 /// assert_eq!(true, mutex.is_poisoned());
392 /// ```
393 pub fn is_poisoned(&self) -> bool {
394 // Don't acquire any lock; otherwise, this function will cause
395 // a deadlock if the caller thread is holding the lock.
396 let status = self.lock.load(Ordering::Relaxed);
397 return is_poisoned(status);
398 }
399
400 /// Returns a mutable reference to the underlying data.
401 ///
402 /// Note that this method won't acquire any lock because we know there is
403 /// no other references to `self`.
404 ///
405 /// # Errors
406 ///
407 /// If another user panicked while holding this mutex, this method call
408 /// wraps the result in an error and returns it.
409 ///
410 /// # Examples
411 ///
412 /// ```
413 /// use spin_sync::Mutex;
414 ///
415 /// let mut mutex = Mutex::new(0);
416 /// *mutex.get_mut().unwrap() = 10;
417 /// assert_eq!(10, *mutex.lock().unwrap());
418 /// ```
419 pub fn get_mut(&mut self) -> LockResult<&mut T> {
420 // There is no other references to `self` because the argument is
421 // a mutable reference.
422 // No lock is required.
423 let data = unsafe { &mut *self.data.get() };
424
425 if self.is_poisoned() {
426 Err(PoisonError::new(data))
427 } else {
428 Ok(data)
429 }
430 }
431}
432
433impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
434 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
435 match self.try_lock() {
436 Ok(guard) => f.debug_struct("Mutex").field("data", &&*guard).finish(),
437 Err(TryLockError::Poisoned(err)) => f
438 .debug_struct("Mutex")
439 .field("data", &&**err.get_ref())
440 .finish(),
441 Err(TryLockError::WouldBlock) => {
442 struct LockedPlaceholder;
443 impl fmt::Debug for LockedPlaceholder {
444 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
445 f.write_str("<locked>")
446 }
447 }
448
449 f.debug_struct("Mutex")
450 .field("data", &LockedPlaceholder)
451 .finish()
452 }
453 }
454 }
455}
456
457impl<T> From<T> for Mutex<T> {
458 fn from(t: T) -> Self {
459 Mutex::new(t)
460 }
461}
462
463impl<T: ?Sized + Default> Default for Mutex<T> {
464 fn default() -> Self {
465 Mutex::new(T::default())
466 }
467}
468
469/// An RAII implementation of a "scoped lock" of a mutex.
470///
471/// When this structure is dropped (falls out of scope), the lock will be released.
472///
473/// The data protected by the mutex can be accessed through this guard via its
474/// `Deref` and `DerefMut` implementations.
475///
476/// This structure is created by [`lock`] and [`try_lock`] methods on
477/// [`Mutex`].
478///
479/// [`lock`]: struct.Mutex.html#method.lock
480/// [`try_lock`]: struct.Mutex.html#method.try_lock
481/// [`Mutex`]: struct.Mutex.html
482#[must_use = "if unused the Mutex will immediately unlock"]
483pub struct MutexGuard<'a, T: ?Sized + 'a> {
484 mutex: &'a Mutex<T>,
485 _phantom: PhantomMutexGuard<'a, T>, // To implement !Send.
486}
487
488impl<'a, T: ?Sized> MutexGuard<'a, T> {
489 fn new(mutex: &'a Mutex<T>) -> Self {
490 Self {
491 mutex,
492 _phantom: Default::default(),
493 }
494 }
495}
496
497impl<T: ?Sized> Drop for MutexGuard<'_, T> {
498 fn drop(&mut self) {
499 let old_status = self.mutex.lock.load(Ordering::Relaxed);
500 debug_assert!(is_locked(old_status));
501
502 let mut new_status = release_lock(old_status);
503 if std::thread::panicking() {
504 new_status = set_poison_flag(new_status);
505 }
506
507 self.mutex.lock.store(new_status, Ordering::Release);
508 }
509}
510
511impl<T: ?Sized> Deref for MutexGuard<'_, T> {
512 type Target = T;
513
514 fn deref(&self) -> &Self::Target {
515 unsafe { &*self.mutex.data.get() }
516 }
517}
518
519impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
520 fn deref_mut(&mut self) -> &mut Self::Target {
521 unsafe { &mut *self.mutex.data.get() }
522 }
523}
524
525impl<T: ?Sized + fmt::Debug> fmt::Debug for MutexGuard<'_, T> {
526 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
527 fmt::Debug::fmt(&**self, f)
528 }
529}
530
531impl<T: ?Sized + fmt::Display> fmt::Display for MutexGuard<'_, T> {
532 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
533 fmt::Display::fmt(&**self, f)
534 }
535}
536
537//
538// Marker Traits
539//
540
541impl<T: ?Sized> UnwindSafe for Mutex<T> {}
542impl<T: ?Sized> RefUnwindSafe for Mutex<T> {}
543
544unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
545unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
546
547unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
548
549//
550// Constants to represent lock state
551//
552type LockStatus = u8;
553
554const INIT: LockStatus = 0;
555const LOCK_FLAG: LockStatus = 0x01;
556const POISON_FLAG: LockStatus = 0x02;
557const NOT_USED_MASK: LockStatus = 0xfc;
558
559#[inline]
560#[must_use]
561fn is_locked(s: LockStatus) -> bool {
562 debug_assert_eq!(0, s & NOT_USED_MASK);
563 (s & LOCK_FLAG) != 0
564}
565
566#[inline]
567#[must_use]
568fn acquire_lock(s: LockStatus) -> LockStatus {
569 debug_assert_eq!(false, is_locked(s));
570 s | LOCK_FLAG
571}
572
573#[inline]
574#[must_use]
575fn release_lock(s: LockStatus) -> LockStatus {
576 debug_assert_eq!(true, is_locked(s));
577 s & !(LOCK_FLAG)
578}
579
580#[inline]
581#[must_use]
582fn is_poisoned(s: LockStatus) -> bool {
583 debug_assert_eq!(0, s & NOT_USED_MASK);
584 (s & POISON_FLAG) != 0
585}
586
587#[inline]
588#[must_use]
589fn set_poison_flag(s: LockStatus) -> LockStatus {
590 debug_assert_eq!(0, s & NOT_USED_MASK);
591 s | POISON_FLAG
592}