safa_api/sync/
locks.rs

1//! Provides various locking mechanisms for synchronization such as Mutex
2//!
3//! uses Futexes internally
4
5use core::{
6    marker::PhantomData,
7    ops::{Deref, DerefMut},
8    sync::atomic::{AtomicU32, Ordering},
9    time::Duration,
10};
11
12use crate::syscalls::futex::{futex_wait, futex_wake};
13
14const M_AVAILABLE: u32 = 0;
15const M_LOCKED: u32 = 1;
16const M_WAITED_ON: u32 = 2;
17
18#[must_use = "if unused the Mutex will immediately unlock"]
19pub struct MutexGuard<'a, T> {
20    mutex: &'a Mutex<T>,
21    marker: PhantomData<&'a mut T>,
22}
23
24impl<'a, T> Drop for MutexGuard<'a, T> {
25    fn drop(&mut self) {
26        unsafe {
27            self.mutex.force_unlock();
28        }
29    }
30}
31
32impl<'a, T> Deref for MutexGuard<'a, T> {
33    type Target = T;
34
35    fn deref(&self) -> &Self::Target {
36        unsafe { &*self.mutex.get() }
37    }
38}
39
40impl<'a, T> DerefMut for MutexGuard<'a, T> {
41    fn deref_mut(&mut self) -> &mut Self::Target {
42        unsafe { &mut *self.mutex.get() }
43    }
44}
45
46#[derive(Debug)]
47pub struct Mutex<T> {
48    state: AtomicU32,
49    inner: T,
50}
51
52impl<T> Mutex<T> {
53    /// Constructs a new free Mutex.
54    pub const fn new(inner: T) -> Self {
55        Self {
56            state: AtomicU32::new(M_AVAILABLE),
57            inner,
58        }
59    }
60    /// Gets a mutable reference to the inner value.
61    pub const fn get_mut(&mut self) -> &mut T {
62        &mut self.inner
63    }
64    /// Gets a mutable pointer to the inner value.
65    pub const fn get(&self) -> *mut T {
66        &self.inner as *const T as *mut T
67    }
68    /// Locks the mutex, blocking the current thread until it can be acquired.
69    ///
70    /// the Mutex is locked until the returned MutexGuard is dropped.
71    pub fn lock(&self) -> MutexGuard<'_, T> {
72        if let Err(mut s) = self.state.compare_exchange_weak(
73            M_AVAILABLE,
74            M_LOCKED,
75            Ordering::Acquire,
76            Ordering::Relaxed,
77        ) {
78            if s != M_WAITED_ON {
79                s = self.state.swap(M_WAITED_ON, Ordering::Acquire);
80            }
81
82            while s != M_AVAILABLE {
83                futex_wait(&self.state, M_WAITED_ON, Duration::MAX)
84                    .expect("System error while waiting for a Futex");
85
86                s = self.state.swap(M_WAITED_ON, Ordering::Acquire);
87            }
88        }
89        MutexGuard {
90            mutex: self,
91            marker: PhantomData,
92        }
93    }
94    /// Attempts to acquire the mutex without blocking, returning `None` if the mutex is currently locked.
95    pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
96        if self
97            .state
98            .compare_exchange(
99                M_AVAILABLE,
100                M_LOCKED,
101                core::sync::atomic::Ordering::Acquire,
102                core::sync::atomic::Ordering::Relaxed,
103            )
104            .is_ok()
105        {
106            Some(MutexGuard {
107                mutex: self,
108                marker: PhantomData,
109            })
110        } else {
111            None
112        }
113    }
114    /// Forces the mutex to be unlocked, even if it is currently locked.
115    pub unsafe fn force_unlock(&self) {
116        if self.state.fetch_sub(1, Ordering::Acquire) != M_LOCKED {
117            // will also handle the case where the mutex is already unlocked
118            self.state.store(M_AVAILABLE, Ordering::Release);
119            futex_wake(&self.state, 1).expect("System error while waking 1 Futex");
120        }
121    }
122}
123
124impl<T: Clone> Clone for Mutex<T> {
125    fn clone(&self) -> Self {
126        Mutex {
127            state: AtomicU32::new(M_AVAILABLE),
128            inner: self.inner.clone(),
129        }
130    }
131}