parking_lot 0.13.0

More compact and efficient implementations of the standard synchronization primitives.
Documentation
use crate::raw_mutex::RawMutex;

/// A mutual exclusion primitive useful for protecting shared data.
///
/// This mutex will block threads waiting for the lock to become available. The
/// mutex can be statically initialized or created by the `new`
/// constructor. Each mutex has a type parameter which represents the data that
/// it is protecting. The data can only be accessed through the RAII guards
/// returned from `lock` and `try_lock`, which guarantees that the data is only
/// ever accessed when the mutex is locked.
///
/// # Fairness
///
/// This mutex uses unfair unlocking by default, which allows the unlocking
/// thread to re-acquire the mutex before a waiting thread and generally
/// improves throughput. This can starve waiting threads.
///
/// Fair unlocking can be requested explicitly by calling
/// `MutexGuard::unlock_fair` instead of simply dropping the `MutexGuard`. A
/// fair unlock hands the mutex directly to a waiting thread.
///
/// # Differences from the standard library `Mutex`
///
/// - No poisoning, the lock is released normally on panic.
/// - Only requires 1 byte of lock state.
/// - Supports locking with a timeout.
/// - Allows raw locking & unlocking without a guard.
/// - Supports explicit fair unlocking through `MutexGuard::unlock_fair`.
///
/// # Examples
///
/// ```
/// use parking_lot::Mutex;
/// use std::sync::{Arc, mpsc::channel};
/// use std::thread;
///
/// const N: usize = 10;
///
/// // 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.
/// let data = Arc::new(Mutex::new(0));
///
/// let (tx, rx) = channel();
/// for _ in 0..N {
///     let (data, tx) = (Arc::clone(&data), tx.clone());
///     thread::spawn(move || {
///         // The shared state can only be accessed once the lock is held.
///         // Our non-atomic increment is safe because we're the only thread
///         // which can access the shared state when the lock is held.
///         let mut data = data.lock();
///         *data += 1;
///         if *data == N {
///             tx.send(()).unwrap();
///         }
///         // the lock is unlocked here when `data` goes out of scope.
///     });
/// }
///
/// rx.recv().unwrap();
/// ```
pub type Mutex<T> = lock_api::Mutex<RawMutex, T>;

/// An RAII guard which unlocks the mutex when dropped.
///
/// The data protected by the mutex can be accessed through this guard via its
/// [`Deref`](core::ops::Deref) and [`DerefMut`](core::ops::DerefMut)
/// implementations.
pub type MutexGuard<'a, T> = lock_api::MutexGuard<'a, RawMutex, T>;

/// An RAII mutex guard returned by [`MutexGuard::map`], which can point to a
/// subfield of the protected data.
///
/// The main difference between `MappedMutexGuard` and `MutexGuard` is that the
/// former doesn't support temporarily unlocking and re-locking, since that
/// could introduce soundness issues if the locked object is modified by another
/// thread.
pub type MappedMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawMutex, T>;

#[cfg(test)]
mod tests {
    use crate::{Condvar, MappedMutexGuard, Mutex, MutexGuard};
    use std::collections::HashMap;
    use std::ops::Deref;
    use std::sync::Arc;
    use std::sync::atomic::{AtomicUsize, Ordering};
    use std::sync::mpsc::channel;
    use std::thread;

    #[cfg(feature = "serde")]
    use postcard::{from_bytes, to_stdvec};

    struct Packet<T>(Arc<(Mutex<T>, Condvar)>);

    #[derive(Eq, PartialEq, Debug)]
    struct NonCopy(i32);

    #[test]
    fn smoke() {
        let m = Mutex::new(());
        drop(m.lock());
        drop(m.lock());
    }

    #[test]
    fn lots_and_lots() {
        const J: u32 = if cfg!(miri) { 200 } else { 1000 };
        const K: u32 = 3;

        let m = Arc::new(Mutex::new(0));

        fn inc(m: &Mutex<u32>) {
            for _ in 0..J {
                *m.lock() += 1;
            }
        }

        let (tx, rx) = channel();
        for _ in 0..K {
            let tx2 = tx.clone();
            let m2 = m.clone();
            thread::spawn(move || {
                inc(&m2);
                tx2.send(()).unwrap();
            });
            let tx2 = tx.clone();
            let m2 = m.clone();
            thread::spawn(move || {
                inc(&m2);
                tx2.send(()).unwrap();
            });
        }

        drop(tx);
        for _ in 0..2 * K {
            rx.recv().unwrap();
        }
        assert_eq!(*m.lock(), J * K * 2);
    }

    #[test]
    fn try_lock() {
        let m = Mutex::new(());
        let guard = m.try_lock().unwrap();
        assert!(m.try_lock().is_none());
        drop(guard);
        assert!(m.try_lock().is_some());
    }

    #[test]
    fn test_into_inner() {
        let m = Mutex::new(NonCopy(10));
        assert_eq!(m.into_inner(), NonCopy(10));
    }

    #[test]
    fn test_into_inner_drop() {
        struct Foo(Arc<AtomicUsize>);
        impl Drop for Foo {
            fn drop(&mut self) {
                self.0.fetch_add(1, Ordering::SeqCst);
            }
        }
        let num_drops = Arc::new(AtomicUsize::new(0));
        let m = Mutex::new(Foo(num_drops.clone()));
        assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        {
            let _inner = m.into_inner();
            assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        }
        assert_eq!(num_drops.load(Ordering::SeqCst), 1);
    }

    #[test]
    fn test_get_mut() {
        let mut m = Mutex::new(NonCopy(10));
        *m.get_mut() = NonCopy(20);
        assert_eq!(m.into_inner(), NonCopy(20));
    }

    #[test]
    fn test_mutex_arc_condvar() {
        let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
        let packet2 = Packet(packet.0.clone());
        let (tx, rx) = channel();
        let _t = thread::spawn(move || {
            // wait until parent gets in
            rx.recv().unwrap();
            let (lock, cvar) = &*packet2.0;
            let mut lock = lock.lock();
            *lock = true;
            cvar.notify_one();
        });

        let (lock, cvar) = &*packet.0;
        let mut lock = lock.lock();
        tx.send(()).unwrap();
        assert!(!*lock);
        while !*lock {
            cvar.wait(&mut lock);
        }
    }

    #[test]
    fn test_mutex_arc_nested() {
        // Tests nested mutexes and access
        // to underlying data.
        let arc = Arc::new(Mutex::new(1));
        let arc2 = Arc::new(Mutex::new(arc));
        let (tx, rx) = channel();
        let _t = thread::spawn(move || {
            let lock = arc2.lock();
            let lock2 = lock.lock();
            assert_eq!(*lock2, 1);
            tx.send(()).unwrap();
        });
        rx.recv().unwrap();
    }

    #[test]
    fn test_mutex_arc_access_in_unwind() {
        let arc = Arc::new(Mutex::new(1));
        let arc2 = arc.clone();
        let _ = thread::spawn(move || {
            struct Unwinder {
                i: Arc<Mutex<i32>>,
            }
            impl Drop for Unwinder {
                fn drop(&mut self) {
                    *self.i.lock() += 1;
                }
            }
            let _u = Unwinder { i: arc2 };
            panic!();
        })
        .join();
        let lock = arc.lock();
        assert_eq!(*lock, 2);
    }

    #[test]
    fn test_mutex_unsized() {
        let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]);
        {
            let b = &mut *mutex.lock();
            b[0] = 4;
            b[2] = 5;
        }
        let comp: &[i32] = &[4, 2, 5];
        assert_eq!(&*mutex.lock(), comp);
    }

    #[test]
    fn test_mutexguard_sync() {
        fn sync<T: Sync>(_: T) {}

        let mutex = Mutex::new(());
        sync(mutex.lock());
    }

    #[test]
    fn test_mutex_debug() {
        let mutex = Mutex::new(vec![0u8, 10]);

        assert_eq!(format!("{:?}", mutex), "Mutex { data: [0, 10] }");
        let _lock = mutex.lock();
        assert_eq!(format!("{:?}", mutex), "Mutex { data: <locked> }");
    }

    #[cfg(feature = "serde")]
    #[test]
    fn test_serde() {
        let contents: Vec<u8> = vec![0, 1, 2];
        let mutex = Mutex::new(contents.clone());

        let serialized = to_stdvec(&mutex).unwrap();
        let deserialized: Mutex<Vec<u8>> = from_bytes(&serialized).unwrap();

        assert_eq!(*(mutex.lock()), *(deserialized.lock()));
        assert_eq!(contents, *(deserialized.lock()));
    }

    #[test]
    fn test_map_or_err_not_mapped() {
        let mut map = HashMap::new();
        map.insert("hello".to_string(), "world".to_string());

        let mutex = Mutex::new(map);
        let guard = mutex.lock();
        let guard = match MutexGuard::try_map_or_err(guard, |the_map| {
            the_map.get_mut("hello2").ok_or(12345i32)
        }) {
            Ok(_) => unreachable!(),
            Err((guard, data)) => {
                assert_eq!(data, 12345i32);
                assert_eq!(guard.get("hello"), Some(&"world".to_string()));
                guard
            }
        };

        // Lets try again
        let mapped_guard = match MutexGuard::try_map_or_err(guard, |the_map| {
            the_map.get_mut("hello").ok_or("unreachable")
        }) {
            Ok(mapped_guard) => mapped_guard,
            Err((_, _)) => unreachable!(),
        };

        assert_eq!(mapped_guard.as_str(), "world");

        match MappedMutexGuard::try_map_or_err(mapped_guard, |the_string| {
            if the_string != "world" {
                //unreachable
                Ok(the_string.as_mut_str())
            } else {
                Err(45678i32)
            }
        }) {
            Ok(_) => unreachable!(),
            Err((guard, err)) => {
                assert_eq!(guard.as_str(), "world");
                assert_eq!(err, 45678i32);
            }
        };
    }

    #[test]
    fn test_map_or_err_mapped() {
        let mut map = HashMap::new();
        map.insert("hello".to_string(), "world".to_string());

        let mutex = Mutex::new(map);
        let guard = mutex.lock();
        let mapped_guard = match MutexGuard::try_map_or_err(guard, |the_map| {
            the_map.get_mut("hello").ok_or("unreachable")
        }) {
            Ok(mapped_guard) => mapped_guard,
            Err((_, _)) => unreachable!(),
        };

        assert_eq!(mapped_guard.as_str(), "world");

        match MappedMutexGuard::try_map_or_err(mapped_guard, |the_string| {
            if the_string == "world" {
                Ok(the_string.as_mut_str())
            } else {
                Err("unreachable")
            }
        }) {
            Ok(mapped_guard) => assert_eq!(mapped_guard.deref(), "world"),
            Err((_, _)) => unreachable!(),
        };
    }
}