Skip to main content

parking_lot/
remutex.rs

1use crate::raw_mutex::RawMutex;
2use core::num::NonZeroUsize;
3use lock_api::{self, GetThreadId};
4
5/// Implementation of the `GetThreadId` trait for `lock_api::ReentrantMutex`.
6pub struct RawThreadId;
7
8unsafe impl GetThreadId for RawThreadId {
9    const INIT: RawThreadId = RawThreadId;
10
11    fn nonzero_thread_id(&self) -> NonZeroUsize {
12        // The address of a thread-local variable is guaranteed to be unique to the
13        // current thread, and is also guaranteed to be non-zero. The variable has to have a
14        // non-zero size to guarantee it has a unique address for each thread.
15        const KEY: ::std::thread::LocalKey<u8> =
    {
        const __RUST_STD_INTERNAL_INIT: u8 = { 0 };
        unsafe {
            ::std::thread::LocalKey::new(const {
                        if ::std::mem::needs_drop::<u8>() {
                            |__rust_std_internal_init|
                                {
                                    #[thread_local]
                                    static __RUST_STD_INTERNAL_VAL:
                                        ::std::thread::local_impl::EagerStorage<u8> =
                                        ::std::thread::local_impl::EagerStorage::new(__RUST_STD_INTERNAL_INIT);
                                    __RUST_STD_INTERNAL_VAL.get()
                                }
                        } else {
                            |__rust_std_internal_init|
                                {
                                    #[thread_local]
                                    static __RUST_STD_INTERNAL_VAL: u8 =
                                        __RUST_STD_INTERNAL_INIT;
                                    &__RUST_STD_INTERNAL_VAL
                                }
                        }
                    })
        }
    };thread_local!(static KEY: u8 = const { 0 });
16        KEY.with(|x| {
17            NonZeroUsize::new(core::ptr::from_ref(x).addr())
18                .expect("thread-local variable address is null")
19        })
20    }
21}
22
23/// A reentrant mutual exclusion lock.
24///
25/// This lock blocks other threads waiting for it to become available. A thread
26/// which already holds the lock can acquire it additional times without
27/// blocking.
28///
29/// Unlike [`Mutex`](crate::Mutex), [`ReentrantMutexGuard`] does not provide
30/// mutable references to the locked data, because multiple guards can coexist
31/// on the same thread. Use interior mutability, such as [`RefCell`](core::cell::RefCell),
32/// to mutate the guarded data.
33///
34/// See [`Mutex`](crate::Mutex) for more details about the underlying mutex
35/// primitive.
36///
37/// # Examples
38///
39/// ```
40/// use parking_lot::ReentrantMutex;
41/// use std::cell::RefCell;
42///
43/// let lock = ReentrantMutex::new(RefCell::new(0));
44/// let first = lock.lock();
45/// let second = lock.lock();
46/// *first.borrow_mut() += 1;
47/// *second.borrow_mut() += 1;
48/// assert_eq!(*lock.lock().borrow(), 2);
49/// ```
50pub type ReentrantMutex<T> = lock_api::ReentrantMutex<RawMutex, RawThreadId, T>;
51
52/// An RAII guard which releases one level of recursive locking when dropped.
53///
54/// The data protected by the mutex can be accessed through this guard via its
55/// [`Deref`](core::ops::Deref) implementation.
56pub type ReentrantMutexGuard<'a, T> = lock_api::ReentrantMutexGuard<'a, RawMutex, RawThreadId, T>;
57
58/// An RAII mutex guard returned by [`ReentrantMutexGuard::map`], which can point to a
59/// subfield of the protected data.
60///
61/// The main difference between `MappedReentrantMutexGuard` and `ReentrantMutexGuard` is that the
62/// former doesn't support temporarily unlocking and re-locking, since that
63/// could introduce soundness issues if the locked object is modified by another
64/// thread.
65pub type MappedReentrantMutexGuard<'a, T> =
66    lock_api::MappedReentrantMutexGuard<'a, RawMutex, RawThreadId, T>;
67
68#[cfg(test)]
69mod tests {
70    use crate::ReentrantMutex;
71    use crate::ReentrantMutexGuard;
72    use std::cell::RefCell;
73    use std::sync::Arc;
74    use std::sync::mpsc::channel;
75    use std::thread;
76
77    #[cfg(feature = "serde")]
78    use postcard::{from_bytes, to_stdvec};
79
80    #[test]
81    fn smoke() {
82        let m = ReentrantMutex::new(2);
83        {
84            let a = m.lock();
85            {
86                let b = m.lock();
87                {
88                    let c = m.lock();
89                    assert_eq!(*c, 2);
90                }
91                assert_eq!(*b, 2);
92            }
93            assert_eq!(*a, 2);
94        }
95    }
96
97    #[test]
98    fn is_mutex() {
99        let m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
100        let m2 = m.clone();
101        let lock = m.lock();
102        let child = thread::spawn(move || {
103            let lock = m2.lock();
104            assert_eq!(*lock.borrow(), 4950);
105        });
106        for i in 0..100 {
107            let lock = m.lock();
108            *lock.borrow_mut() += i;
109        }
110        drop(lock);
111        child.join().unwrap();
112    }
113
114    #[test]
115    fn trylock_works() {
116        let m = Arc::new(ReentrantMutex::new(()));
117        let m2 = m.clone();
118        let _lock = m.try_lock().unwrap();
119        let _lock2 = m.try_lock().unwrap();
120        thread::spawn(move || {
121            let lock = m2.try_lock();
122            assert!(lock.is_none());
123        })
124        .join()
125        .unwrap();
126        let _lock3 = m.try_lock().unwrap();
127    }
128
129    #[test]
130    fn test_reentrant_mutex_debug() {
131        let mutex = ReentrantMutex::new(vec![0u8, 10]);
132
133        assert_eq!(format!("{:?}", mutex), "ReentrantMutex { data: [0, 10] }");
134    }
135
136    #[test]
137    fn test_reentrant_mutex_bump() {
138        let mutex = Arc::new(ReentrantMutex::new(()));
139        let mutex2 = mutex.clone();
140
141        let mut guard = mutex.lock();
142
143        let (tx, rx) = channel();
144
145        thread::spawn(move || {
146            let _guard = mutex2.lock();
147            tx.send(()).unwrap();
148        });
149
150        // `bump()` repeatedly until the thread starts up and requests the lock
151        while rx.try_recv().is_err() {
152            ReentrantMutexGuard::bump(&mut guard);
153        }
154    }
155
156    #[cfg(feature = "serde")]
157    #[test]
158    fn test_serde() {
159        let contents: Vec<u8> = vec![0, 1, 2];
160        let mutex = ReentrantMutex::new(contents.clone());
161
162        let serialized = to_stdvec(&mutex).unwrap();
163        let deserialized: ReentrantMutex<Vec<u8>> = from_bytes(&serialized).unwrap();
164
165        assert_eq!(*(mutex.lock()), *(deserialized.lock()));
166        assert_eq!(contents, *(deserialized.lock()));
167    }
168}