Skip to main content

relibc/sync/
mutex.rs

1use super::{AtomicLock, AttemptStatus};
2use crate::platform::types::c_int;
3use core::{
4    cell::UnsafeCell,
5    ops::{Deref, DerefMut},
6    sync::atomic::{AtomicI32 as AtomicInt, Ordering},
7};
8
9pub(crate) const UNLOCKED: c_int = 0;
10pub(crate) const LOCKED: c_int = 1;
11pub(crate) const WAITING: c_int = 2;
12
13pub struct Mutex<T> {
14    pub(crate) lock: AtomicLock,
15    content: UnsafeCell<T>,
16}
17unsafe impl<T: Send> Send for Mutex<T> {}
18unsafe impl<T: Send> Sync for Mutex<T> {}
19
20pub(crate) unsafe fn manual_try_lock_generic(word: &AtomicInt) -> bool {
21    word.compare_exchange(UNLOCKED, LOCKED, Ordering::Acquire, Ordering::Relaxed)
22        .is_ok()
23}
24pub(crate) unsafe fn manual_lock_generic(word: &AtomicInt) {
25    crate::sync::wait_until_generic(
26        word,
27        |lock| {
28            lock.compare_exchange_weak(UNLOCKED, LOCKED, Ordering::Acquire, Ordering::Relaxed)
29                .map(|_| AttemptStatus::Desired)
30                .unwrap_or_else(|e| match e {
31                    WAITING => AttemptStatus::Waiting,
32                    _ => AttemptStatus::Other,
33                })
34        },
35        |lock| match lock
36            // TODO: Ordering
37            .compare_exchange_weak(LOCKED, WAITING, Ordering::SeqCst, Ordering::SeqCst)
38            .unwrap_or_else(|e| e)
39        {
40            UNLOCKED => AttemptStatus::Desired,
41            WAITING => AttemptStatus::Waiting,
42            _ => AttemptStatus::Other,
43        },
44        WAITING,
45    );
46}
47pub(crate) unsafe fn manual_unlock_generic(word: &AtomicInt) {
48    if word.swap(UNLOCKED, Ordering::Release) == WAITING {
49        crate::sync::futex_wake(word, i32::MAX);
50    }
51}
52
53impl<T> Mutex<T> {
54    /// Create a new mutex
55    pub const fn new(content: T) -> Self {
56        Self {
57            lock: AtomicLock::new(UNLOCKED),
58            content: UnsafeCell::new(content),
59        }
60    }
61    /// Create a new mutex that is already locked. This is a more
62    /// efficient way to do the following:
63    /// ```rust
64    /// let mut mutex = Mutex::new(());
65    /// mutex.manual_lock();
66    /// ```
67    pub unsafe fn locked(content: T) -> Self {
68        Self {
69            lock: AtomicLock::new(LOCKED),
70            content: UnsafeCell::new(content),
71        }
72    }
73
74    /// Tries to lock the mutex, fails if it's already locked. Manual means
75    /// it's up to you to unlock it after mutex. Returns the last atomic value
76    /// on failure. You should probably not worry about this, it's used for
77    /// internal optimizations.
78    pub unsafe fn manual_try_lock(&self) -> Result<&mut T, c_int> {
79        if unsafe { manual_try_lock_generic(&self.lock) } {
80            Ok(unsafe { &mut *self.content.get() })
81        } else {
82            Err(0)
83        }
84    }
85    /// Lock the mutex, returning the inner content. After doing this, it's
86    /// your responsibility to unlock it after usage. Mostly useful for FFI:
87    /// Prefer normal .lock() where possible.
88    pub unsafe fn manual_lock(&self) -> &mut T {
89        unsafe { manual_lock_generic(&self.lock) };
90        unsafe { &mut *self.content.get() }
91    }
92    /// Unlock the mutex, if it's locked.
93    pub unsafe fn manual_unlock(&self) {
94        unsafe { manual_unlock_generic(&self.lock) }
95    }
96    pub fn as_ptr(&self) -> *mut T {
97        self.content.get()
98    }
99
100    /// Tries to lock the mutex and returns a guard that automatically unlocks
101    /// the mutex when it falls out of scope.
102    pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
103        unsafe {
104            self.manual_try_lock().ok().map(|content| MutexGuard {
105                mutex: self,
106                content,
107            })
108        }
109    }
110    /// Locks the mutex and returns a guard that automatically unlocks the
111    /// mutex when it falls out of scope.
112    pub fn lock(&self) -> MutexGuard<'_, T> {
113        MutexGuard {
114            mutex: self,
115            content: unsafe { self.manual_lock() },
116        }
117    }
118}
119
120pub struct MutexGuard<'a, T: 'a> {
121    pub(crate) mutex: &'a Mutex<T>,
122    content: &'a mut T,
123}
124impl<'a, T> Deref for MutexGuard<'a, T> {
125    type Target = T;
126
127    fn deref(&self) -> &Self::Target {
128        self.content
129    }
130}
131impl<'a, T> DerefMut for MutexGuard<'a, T> {
132    fn deref_mut(&mut self) -> &mut Self::Target {
133        self.content
134    }
135}
136impl<'a, T> Drop for MutexGuard<'a, T> {
137    fn drop(&mut self) {
138        unsafe {
139            self.mutex.manual_unlock();
140        }
141    }
142}