Skip to main content

happylock/
mutex.rs

1use std::cell::UnsafeCell;
2use std::marker::PhantomData;
3
4use lock_api::RawMutex;
5
6use crate::poisonable::PoisonFlag;
7use crate::ThreadKey;
8
9mod guard;
10mod mutex;
11
12/// A spinning mutex
13#[cfg(feature = "spin")]
14pub type SpinLock<T> = Mutex<T, spin::Mutex<()>>;
15
16/// A parking lot mutex
17#[cfg(feature = "parking_lot")]
18pub type ParkingMutex<T> = Mutex<T, parking_lot::RawMutex>;
19
20/// A mutual exclusion primitive useful for protecting shared data, which
21/// cannot deadlock.
22///
23/// This mutex will block threads waiting for the lock to become available. The
24/// mutex can be created via a `new` constructor. Each mutex has a type
25/// parameter which represents the data that it is protecting. The data can
26/// only be accessed through the [`MutexGuard`]s returned from [`lock`] and
27/// [`try_lock`], which guarantees that the data is only ever accessed when
28/// the mutex is locked.
29///
30/// Locking the mutex on a thread that already locked it is impossible, due to
31/// the requirement of the [`ThreadKey`]. Therefore, this will never deadlock.
32///
33/// # Examples
34///
35/// ```
36/// use std::sync::Arc;
37/// use std::thread;
38/// use std::sync::mpsc;
39///
40/// use happylock::{Mutex, ThreadKey};
41///
42/// // Spawn a few threads to increment a shared variable (non-atomically),
43/// // and let the main thread know once all increments are done.
44/// //
45/// // Here we're using an Arc to share memory among threads, and the data
46/// // inside the Arc is protected with a mutex.
47/// const N: usize = 10;
48///
49/// let data = Arc::new(Mutex::new(0));
50///
51/// let (tx, rx) = mpsc::channel();
52/// for _ in 0..N {
53///     let (data, tx) = (Arc::clone(&data), tx.clone());
54///     thread::spawn(move || {
55///         let key = ThreadKey::get().unwrap();
56///         let mut data = data.lock(key);
57///         *data += 1;
58///         if *data == N {
59///             tx.send(()).unwrap();
60///         }
61///         // the lock is unlocked
62///     });
63/// }
64///
65/// rx.recv().unwrap();
66/// ```
67///
68/// To unlock a mutex guard sooner than the end of the enclosing scope, either
69/// create an inner scope, drop the guard manually, or call [`Mutex::unlock`].
70///
71/// ```
72/// use std::sync::Arc;
73/// use std::thread;
74///
75/// use happylock::{Mutex, ThreadKey};
76///
77/// const N: usize = 3;
78///
79/// let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4]));
80/// let res_mutex = Arc::new(Mutex::new(0));
81///
82/// let mut threads = Vec::with_capacity(N);
83/// (0..N).for_each(|_| {
84///     let data_mutex_clone = Arc::clone(&data_mutex);
85///     let res_mutex_clone = Arc::clone(&res_mutex);
86///
87///     threads.push(thread::spawn(move || {
88///         let mut key = ThreadKey::get().unwrap();
89///
90///         // Here we use a block to limit the lifetime of the lock guard.
91///         let result = data_mutex_clone.scoped_lock(&mut key, |data| {
92///             let result = data.iter().fold(0, |acc, x| acc + x * 2);
93///             data.push(result);
94///             result
95///             // The mutex guard gets dropped here, so the lock is released
96///         });
97///         // The thread key is available again
98///         *res_mutex_clone.lock(key) += result;
99///     }));
100/// });
101///
102/// let key = ThreadKey::get().unwrap();
103/// let mut data = data_mutex.lock(key);
104/// let result = data.iter().fold(0, |acc, x| acc + x * 2);
105/// data.push(result);
106///
107/// // We drop the `data` explicitly because it's not necessary anymore. This
108/// // allows other threads to start working on the data immediately. Dropping
109/// // the data also gives us access to the thread key, so we can lock
110/// // another mutex.
111/// let key = Mutex::unlock(data);
112///
113/// // Here the mutex guard is not assigned to a variable and so, even if the
114/// // scope does not end after this line, the mutex is still released: there is
115/// // no deadlock.
116/// *res_mutex.lock(key) += result;
117///
118/// threads.into_iter().for_each(|thread| {
119///     thread
120///         .join()
121///         .expect("The thread creating or execution failed !")
122/// });
123///
124/// let key = ThreadKey::get().unwrap();
125/// assert_eq!(*res_mutex.lock(key), 800);
126/// ```
127///
128/// [`lock`]: `Mutex::lock`
129/// [`try_lock`]: `Mutex::try_lock`
130/// [`ThreadKey`]: `crate::ThreadKey`
131pub struct Mutex<T: ?Sized, R> {
132	raw: R,
133	poison: PoisonFlag,
134	data: UnsafeCell<T>,
135}
136
137/// An RAII implementation of a “scoped lock” of a mutex. When this structure
138/// is dropped (falls out of scope), the lock will be unlocked.
139///
140/// The data protected by the mutex can be accessed through this guard via its
141/// [`Deref`] and [`DerefMut`] implementations.
142///
143/// This is created by calling the [`lock`] and [`try_lock`] methods on [`Mutex`]
144///
145/// This is similar to the [`MutexGuard`] type, except it does not hold a
146/// [`ThreadKey`].
147///
148/// [`lock`]: `Mutex::lock`
149/// [`try_lock`]: `Mutex::try_lock`
150/// [`Deref`]: `std::ops::Deref`
151/// [`DerefMut`]: `std::ops::DerefMut`
152pub struct MutexRef<'a, T: ?Sized + 'a, R: RawMutex>(&'a Mutex<T, R>, PhantomData<R::GuardMarker>);
153
154/// An RAII implementation of a “scoped lock” of a mutex. When this structure
155/// is dropped (falls out of scope), the lock will be unlocked.
156///
157/// The data protected by the mutex can be accessed through this guard via its
158/// [`Deref`] and [`DerefMut`] implementations.
159///
160/// This is created by calling the [`lock`] and [`try_lock`] methods on [`Mutex`]
161///
162/// This guard holds on to a [`ThreadKey`], which ensures that nothing else is
163/// locked until this guard is dropped. The [`ThreadKey`] can be reacquired
164/// using [`Mutex::unlock`].
165///
166/// [`Deref`]: `std::ops::Deref`
167/// [`DerefMut`]: `std::ops::DerefMut`
168/// [`lock`]: `Mutex::lock`
169/// [`try_lock`]: `Mutex::try_lock`
170//
171// This is the most lifetime-intensive thing I've ever written. Can I graduate
172// from borrow checker university now?
173pub struct MutexGuard<'a, T: ?Sized + 'a, R: RawMutex> {
174	mutex: MutexRef<'a, T, R>, // this way we don't need to re-implement Drop
175	thread_key: ThreadKey,
176}
177
178#[cfg(test)]
179mod tests {
180	use crate::{LockCollection, ThreadKey};
181
182	use super::*;
183
184	#[test]
185	fn unlocked_when_initialized() {
186		let lock: crate::Mutex<_> = Mutex::new("Hello, world!");
187
188		assert!(!lock.is_locked());
189	}
190
191	#[test]
192	fn locked_after_read() {
193		let key = ThreadKey::get().unwrap();
194		let lock: crate::Mutex<_> = Mutex::new("Hello, world!");
195
196		let guard = lock.lock(key);
197
198		assert!(lock.is_locked());
199		drop(guard)
200	}
201
202	#[test]
203	fn from_works() {
204		let key = ThreadKey::get().unwrap();
205		let mutex: crate::Mutex<_> = Mutex::from("Hello, world!");
206
207		let guard = mutex.lock(key);
208		assert_eq!(*guard, "Hello, world!");
209	}
210
211	#[test]
212	fn as_mut_works() {
213		let key = ThreadKey::get().unwrap();
214		let mut mutex = crate::Mutex::from(42);
215
216		let mut_ref = mutex.as_mut();
217		*mut_ref = 24;
218
219		mutex.scoped_lock(key, |guard| assert_eq!(*guard, 24))
220	}
221
222	#[test]
223	fn display_works_for_guard() {
224		let key = ThreadKey::get().unwrap();
225		let mutex: crate::Mutex<_> = Mutex::new("Hello, world!");
226		let guard = mutex.lock(key);
227		assert_eq!(guard.to_string(), "Hello, world!".to_string());
228	}
229
230	#[test]
231	fn display_works_for_ref() {
232		let mutex: crate::Mutex<_> = Mutex::new("Hello, world!");
233		let guard = unsafe { mutex.try_lock_no_key().unwrap() };
234		assert_eq!(guard.to_string(), "Hello, world!".to_string());
235	}
236
237	#[test]
238	fn ref_as_mut() {
239		let key = ThreadKey::get().unwrap();
240		let collection = LockCollection::new(crate::Mutex::new(0));
241		let mut guard = collection.lock(key);
242		let guard_mut = guard.as_mut().as_mut();
243
244		*guard_mut = 3;
245		let key = LockCollection::<crate::Mutex<_>>::unlock(guard);
246
247		let guard = collection.lock(key);
248
249		assert_eq!(guard.as_ref().as_ref(), &3);
250	}
251
252	#[test]
253	fn guard_as_mut() {
254		let key = ThreadKey::get().unwrap();
255		let mutex = crate::Mutex::new(0);
256		let mut guard = mutex.lock(key);
257		let guard_mut = guard.as_mut();
258
259		*guard_mut = 3;
260		let key = Mutex::unlock(guard);
261
262		let guard = mutex.lock(key);
263
264		assert_eq!(guard.as_ref(), &3);
265	}
266
267	#[test]
268	fn dropping_guard_releases_mutex() {
269		let key = ThreadKey::get().unwrap();
270		let mutex: crate::Mutex<_> = Mutex::new("Hello, world!");
271
272		let guard = mutex.lock(key);
273		drop(guard);
274
275		assert!(!mutex.is_locked());
276	}
277
278	#[test]
279	fn dropping_ref_releases_mutex() {
280		let mutex: crate::Mutex<_> = Mutex::new("Hello, world!");
281
282		let guard = unsafe { mutex.try_lock_no_key().unwrap() };
283		drop(guard);
284
285		assert!(!mutex.is_locked());
286	}
287}