Skip to main content

parking_lot/
fair_mutex.rs

1use crate::raw_fair_mutex::RawFairMutex;
2
3/// A mutual exclusion primitive that uses fair unlocking, useful for protecting shared data.
4///
5/// This mutex will block threads waiting for the lock to become available. The
6/// mutex can be statically initialized or created by the `new`
7/// constructor. Each mutex has a type parameter which represents the data that
8/// it is protecting. The data can only be accessed through the RAII guards
9/// returned from `lock` and `try_lock`, which guarantees that the data is only
10/// ever accessed when the mutex is locked.
11///
12/// The regular mutex provided by `parking_lot` uses unfair unlocking by
13/// default, whereas this mutex always uses a fair unlock. When there are parked
14/// waiters, a fair unlock hands the mutex directly to one of them instead of
15/// making the mutex available for the unlocking thread to immediately
16/// re-acquire. Fair mutexes are generally slower, but can be useful when
17/// predictable handoff is more important than throughput.
18///
19/// Fair unlocking does not imply strict first-in first-out ordering: threads
20/// may acquire the mutex while spinning or through `try_lock` without joining
21/// the queue of parked waiters.
22///
23/// # Differences from the standard library `Mutex`
24///
25/// - No poisoning, the lock is released normally on panic.
26/// - Only requires 1 byte of lock state.
27/// - Always uses fair unlocking.
28/// - Supports locking with a timeout.
29/// - Allows raw locking & unlocking without a guard.
30///
31/// # Examples
32///
33/// ```
34/// use parking_lot::FairMutex;
35/// use std::sync::{Arc, mpsc::channel};
36/// use std::thread;
37///
38/// const N: usize = 10;
39///
40/// // Spawn a few threads to increment a shared variable (non-atomically), and
41/// // let the main thread know once all increments are done.
42/// //
43/// // Here we're using an Arc to share memory among threads, and the data inside
44/// // the Arc is protected with a mutex.
45/// let data = Arc::new(FairMutex::new(0));
46///
47/// let (tx, rx) = channel();
48/// for _ in 0..N {
49///     let (data, tx) = (Arc::clone(&data), tx.clone());
50///     thread::spawn(move || {
51///         // The shared state can only be accessed once the lock is held.
52///         // Our non-atomic increment is safe because we're the only thread
53///         // which can access the shared state when the lock is held.
54///         let mut data = data.lock();
55///         *data += 1;
56///         if *data == N {
57///             tx.send(()).unwrap();
58///         }
59///         // the lock is unlocked here when `data` goes out of scope.
60///     });
61/// }
62///
63/// rx.recv().unwrap();
64/// ```
65pub type FairMutex<T> = lock_api::Mutex<RawFairMutex, T>;
66
67/// An RAII guard which unlocks the mutex when dropped.
68///
69/// The data protected by the mutex can be accessed through this guard via its
70/// [`Deref`](core::ops::Deref) and [`DerefMut`](core::ops::DerefMut)
71/// implementations.
72pub type FairMutexGuard<'a, T> = lock_api::MutexGuard<'a, RawFairMutex, T>;
73
74/// An RAII mutex guard returned by [`FairMutexGuard::map`], which can point to a
75/// subfield of the protected data.
76///
77/// The main difference between `MappedFairMutexGuard` and `FairMutexGuard` is that the
78/// former doesn't support temporarily unlocking and re-locking, since that
79/// could introduce soundness issues if the locked object is modified by another
80/// thread.
81pub type MappedFairMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawFairMutex, T>;
82
83#[cfg(test)]
84mod tests {
85    use crate::FairMutex;
86    use std::sync::Arc;
87    use std::sync::atomic::{AtomicUsize, Ordering};
88    use std::sync::mpsc::channel;
89    use std::thread;
90
91    #[cfg(feature = "serde")]
92    use postcard::{from_bytes, to_stdvec};
93
94    #[derive(Eq, PartialEq, Debug)]
95    struct NonCopy(i32);
96
97    #[test]
98    fn smoke() {
99        let m = FairMutex::new(());
100        drop(m.lock());
101        drop(m.lock());
102    }
103
104    #[test]
105    fn lots_and_lots() {
106        const J: u32 = if cfg!(miri) { 200 } else { 1000 };
107        const K: u32 = 3;
108
109        let m = Arc::new(FairMutex::new(0));
110
111        fn inc(m: &FairMutex<u32>) {
112            for _ in 0..J {
113                *m.lock() += 1;
114            }
115        }
116
117        let (tx, rx) = channel();
118        for _ in 0..K {
119            let tx2 = tx.clone();
120            let m2 = m.clone();
121            thread::spawn(move || {
122                inc(&m2);
123                tx2.send(()).unwrap();
124            });
125            let tx2 = tx.clone();
126            let m2 = m.clone();
127            thread::spawn(move || {
128                inc(&m2);
129                tx2.send(()).unwrap();
130            });
131        }
132
133        drop(tx);
134        for _ in 0..2 * K {
135            rx.recv().unwrap();
136        }
137        assert_eq!(*m.lock(), J * K * 2);
138    }
139
140    #[test]
141    fn try_lock() {
142        let m = FairMutex::new(());
143        *m.try_lock().unwrap() = ();
144    }
145
146    #[test]
147    fn test_into_inner() {
148        let m = FairMutex::new(NonCopy(10));
149        assert_eq!(m.into_inner(), NonCopy(10));
150    }
151
152    #[test]
153    fn test_into_inner_drop() {
154        struct Foo(Arc<AtomicUsize>);
155        impl Drop for Foo {
156            fn drop(&mut self) {
157                self.0.fetch_add(1, Ordering::SeqCst);
158            }
159        }
160        let num_drops = Arc::new(AtomicUsize::new(0));
161        let m = FairMutex::new(Foo(num_drops.clone()));
162        assert_eq!(num_drops.load(Ordering::SeqCst), 0);
163        {
164            let _inner = m.into_inner();
165            assert_eq!(num_drops.load(Ordering::SeqCst), 0);
166        }
167        assert_eq!(num_drops.load(Ordering::SeqCst), 1);
168    }
169
170    #[test]
171    fn test_get_mut() {
172        let mut m = FairMutex::new(NonCopy(10));
173        *m.get_mut() = NonCopy(20);
174        assert_eq!(m.into_inner(), NonCopy(20));
175    }
176
177    #[test]
178    fn test_mutex_arc_nested() {
179        // Tests nested mutexes and access
180        // to underlying data.
181        let arc = Arc::new(FairMutex::new(1));
182        let arc2 = Arc::new(FairMutex::new(arc));
183        let (tx, rx) = channel();
184        let _t = thread::spawn(move || {
185            let lock = arc2.lock();
186            let lock2 = lock.lock();
187            assert_eq!(*lock2, 1);
188            tx.send(()).unwrap();
189        });
190        rx.recv().unwrap();
191    }
192
193    #[test]
194    fn test_mutex_arc_access_in_unwind() {
195        let arc = Arc::new(FairMutex::new(1));
196        let arc2 = arc.clone();
197        let _ = thread::spawn(move || {
198            struct Unwinder {
199                i: Arc<FairMutex<i32>>,
200            }
201            impl Drop for Unwinder {
202                fn drop(&mut self) {
203                    *self.i.lock() += 1;
204                }
205            }
206            let _u = Unwinder { i: arc2 };
207            panic!();
208        })
209        .join();
210        let lock = arc.lock();
211        assert_eq!(*lock, 2);
212    }
213
214    #[test]
215    fn test_mutex_unsized() {
216        let mutex: &FairMutex<[i32]> = &FairMutex::new([1, 2, 3]);
217        {
218            let b = &mut *mutex.lock();
219            b[0] = 4;
220            b[2] = 5;
221        }
222        let comp: &[i32] = &[4, 2, 5];
223        assert_eq!(&*mutex.lock(), comp);
224    }
225
226    #[test]
227    fn test_mutexguard_sync() {
228        fn sync<T: Sync>(_: T) {}
229
230        let mutex = FairMutex::new(());
231        sync(mutex.lock());
232    }
233
234    #[test]
235    fn test_mutex_debug() {
236        let mutex = FairMutex::new(vec![0u8, 10]);
237
238        assert_eq!(format!("{:?}", mutex), "Mutex { data: [0, 10] }");
239        let _lock = mutex.lock();
240        assert_eq!(format!("{:?}", mutex), "Mutex { data: <locked> }");
241    }
242
243    #[cfg(feature = "serde")]
244    #[test]
245    fn test_serde() {
246        let contents: Vec<u8> = vec![0, 1, 2];
247        let mutex = FairMutex::new(contents.clone());
248
249        let serialized = to_stdvec(&mutex).unwrap();
250        let deserialized: FairMutex<Vec<u8>> = from_bytes(&serialized).unwrap();
251
252        assert_eq!(*(mutex.lock()), *(deserialized.lock()));
253        assert_eq!(contents, *(deserialized.lock()));
254    }
255}