std/sys/sync/mutex/
pthread.rs

1#![forbid(unsafe_op_in_unsafe_fn)]
2
3use crate::mem::forget;
4use crate::pin::Pin;
5use crate::sys::pal::sync as pal;
6use crate::sys::sync::OnceBox;
7
8pub struct Mutex {
9    pub pal: OnceBox<pal::Mutex>,
10}
11
12impl Mutex {
13    #[inline]
14    pub const fn new() -> Mutex {
15        Mutex { pal: OnceBox::new() }
16    }
17
18    #[inline]
19    fn get(&self) -> Pin<&pal::Mutex> {
20        // If the initialization race is lost, the new mutex is destroyed.
21        // This is sound however, as it cannot have been locked.
22        self.pal.get_or_init(|| {
23            let mut pal = Box::pin(pal::Mutex::new());
24            // SAFETY: we only call `init` once per `pal::Mutex`, namely here.
25            unsafe { pal.as_mut().init() };
26            pal
27        })
28    }
29
30    #[inline]
31    pub fn lock(&self) {
32        // SAFETY: we call `init` above, therefore reentrant locking is safe.
33        // In `drop` we ensure that the mutex is not destroyed while locked.
34        unsafe { self.get().lock() }
35    }
36
37    #[inline]
38    pub unsafe fn unlock(&self) {
39        // SAFETY: the mutex can only be locked if it is already initialized
40        // and we observed this initialization since we observed the locking.
41        unsafe { self.pal.get_unchecked().unlock() }
42    }
43
44    #[inline]
45    pub fn try_lock(&self) -> bool {
46        // SAFETY: we call `init` above, therefore reentrant locking is safe.
47        // In `drop` we ensure that the mutex is not destroyed while locked.
48        unsafe { self.get().try_lock() }
49    }
50}
51
52impl Drop for Mutex {
53    fn drop(&mut self) {
54        let Some(pal) = self.pal.take() else { return };
55        // We're not allowed to pthread_mutex_destroy a locked mutex,
56        // so check first if it's unlocked.
57        if unsafe { pal.as_ref().try_lock() } {
58            unsafe { pal.as_ref().unlock() };
59            drop(pal)
60        } else {
61            // The mutex is locked. This happens if a MutexGuard is leaked.
62            // In this case, we just leak the Mutex too.
63            forget(pal)
64        }
65    }
66}