summaryrefslogtreecommitdiff
path: root/src/mutex.rs
blob: ae5efc8f326e80a5f17fe569f7a0e3776bbab6d6 (plain)
use std::cell::UnsafeCell;
use std::marker::PhantomData;

use lock_api::RawMutex;

use crate::key::Keyable;

mod guard;
mod mutex;

/// A spinning mutex
#[cfg(feature = "spin")]
pub type SpinLock<T> = Mutex<T, spin::Mutex<()>>;

/// A parking lot mutex
#[cfg(feature = "parking_lot")]
pub type ParkingMutex<T> = Mutex<T, parking_lot::RawMutex>;

/// A mutual exclusion primitive useful for protecting shared data, which
/// cannot deadlock.
///
/// This mutex will block threads waiting for the lock to become available.
/// Each mutex has a type parameter which represents the data that it is
/// protecting. The data can only be accessed through the [`MutexGuard`]s
/// returned from [`lock`] and [`try_lock`], which guarantees that the data is
/// only ever accessed when the mutex is locked.
///
/// Locking the mutex on a thread that already locked it is impossible, due to
/// the requirement of the [`ThreadKey`]. Therefore, this will never deadlock.
///
/// # Examples
///
/// ```
/// use std::sync::Arc;
/// use std::thread;
/// use std::sync::mpsc;
///
/// use happylock::{Mutex, ThreadKey};
///
/// // Spawn a few threads to increment a shared variable (non-atomically),
/// // and let the main thread know once all increments are done.
/// //
/// // Here we're using an Arc to share memory among threads, and the data
/// // inside the Arc is protected with a mutex.
/// const N: usize = 10;
///
/// let data = Arc::new(Mutex::new(0));
///
/// let (tx, rx) = mpsc::channel();
/// for _ in 0..N {
///     let (data, tx) = (Arc::clone(&data), tx.clone());
///     thread::spawn(move || {
///         let key = ThreadKey::get().unwrap();
///         let mut data = data.lock(key);
///         *data += 1;
///         if *data == N {
///             tx.send(()).unwrap();
///         }
///         // the lock is unlocked
///     });
/// }
///
/// rx.recv().unwrap();
/// ```
///
/// To unlock a mutex guard sooner than the end of the enclosing scope, either
/// create an inner scope, drop the guard manually, or call [`Mutex::unlock`].
///
/// ```
/// use std::sync::Arc;
/// use std::thread;
///
/// use happylock::{Mutex, ThreadKey};
///
/// const N: usize = 3;
///
/// let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4]));
/// let res_mutex = Arc::new(Mutex::new(0));
///
/// let mut threads = Vec::with_capacity(N);
/// (0..N).for_each(|_| {
///     let data_mutex_clone = Arc::clone(&data_mutex);
///     let res_mutex_clone = Arc::clone(&res_mutex);
///
///     threads.push(thread::spawn(move || {
///         let mut key = ThreadKey::get().unwrap();
///
///         // Here we use a block to limit the lifetime of the lock guard.
///         let result = {
///             let mut data = data_mutex_clone.lock(&mut key);
///             let result = data.iter().fold(0, |acc, x| acc + x * 2);
///             data.push(result);
///             result
///             // The mutex guard gets dropped here, so the lock is released
///         };
///         // The thread key is available again
///         *res_mutex_clone.lock(key) += result;
///     }));
/// });
///
/// let mut key = ThreadKey::get().unwrap();
/// let mut data = data_mutex.lock(&mut key);
/// let result = data.iter().fold(0, |acc, x| acc + x * 2);
/// data.push(result);
///
/// // We drop the `data` explicitly because it's not necessary anymore. This
/// // allows other threads to start working on the data immediately. Dropping
/// // the data also gives us access to the thread key, so we can lock
/// // another mutex.
/// drop(data);
///
/// // Here the mutex guard is not assigned to a variable and so, even if the
/// // scope does not end after this line, the mutex is still released: there is
/// // no deadlock.
/// *res_mutex.lock(&mut key) += result;
///
/// threads.into_iter().for_each(|thread| {
///     thread
///         .join()
///         .expect("The thread creating or execution failed !")
/// });
///
/// assert_eq!(*res_mutex.lock(key), 800);
/// ```
///
/// [`lock`]: `Mutex::lock`
/// [`try_lock`]: `Mutex::try_lock`
/// [`ThreadKey`]: `crate::ThreadKey`
pub struct Mutex<T: ?Sized, R> {
	raw: R,
	data: UnsafeCell<T>,
}

/// A reference to a mutex that unlocks it when dropped.
///
/// This is similar to [`MutexGuard`], except it does not hold a [`Keyable`].
pub struct MutexRef<'a, T: ?Sized + 'a, R: RawMutex>(
	&'a Mutex<T, R>,
	PhantomData<(&'a mut T, R::GuardMarker)>,
);

/// An RAII implementation of a “scoped lock” of a mutex.
///
/// When this structure is dropped (falls out of scope), the lock will be
/// unlocked.
///
/// This is created by calling the [`lock`] and [`try_lock`] methods on [`Mutex`]
///
/// [`lock`]: `Mutex::lock`
/// [`try_lock`]: `Mutex::try_lock`

// This is the most lifetime-intensive thing I've ever written. Can I graduate
// from borrow checker university now?
pub struct MutexGuard<'a, 'key: 'a, T: ?Sized + 'a, Key: Keyable + 'key, R: RawMutex> {
	mutex: MutexRef<'a, T, R>, // this way we don't need to re-implement Drop
	thread_key: Key,
	_phantom: PhantomData<&'key ()>,
}

#[cfg(test)]
mod tests {
	use crate::ThreadKey;

	use super::*;

	#[test]
	fn unlocked_when_initialized() {
		let lock: crate::Mutex<_> = Mutex::new("Hello, world!");

		assert!(!lock.is_locked());
	}

	#[test]
	fn locked_after_read() {
		let key = ThreadKey::get().unwrap();
		let lock: crate::Mutex<_> = Mutex::new("Hello, world!");

		let guard = lock.lock(key);

		assert!(lock.is_locked());
		drop(guard)
	}

	#[test]
	fn display_works_for_guard() {
		let key = ThreadKey::get().unwrap();
		let mutex: crate::Mutex<_> = Mutex::new("Hello, world!");
		let guard = mutex.lock(key);
		assert_eq!(guard.to_string(), "Hello, world!".to_string());
	}

	#[test]
	fn display_works_for_ref() {
		let mutex: crate::Mutex<_> = Mutex::new("Hello, world!");
		let guard = unsafe { mutex.try_lock_no_key().unwrap() }; // TODO lock_no_key
		assert_eq!(guard.to_string(), "Hello, world!".to_string());
	}

	#[test]
	fn dropping_guard_releases_mutex() {
		let mut key = ThreadKey::get().unwrap();
		let mutex: crate::Mutex<_> = Mutex::new("Hello, world!");

		let guard = mutex.lock(&mut key);
		drop(guard);

		assert!(!mutex.is_locked());
	}

	#[test]
	fn dropping_ref_releases_mutex() {
		let mutex: crate::Mutex<_> = Mutex::new("Hello, world!");

		let guard = unsafe { mutex.try_lock_no_key().unwrap() };
		drop(guard);

		assert!(!mutex.is_locked());
	}
}