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}