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