axstd/sync/
mutex.rs

1//! A naïve sleeping mutex.
2
3use core::cell::UnsafeCell;
4use core::fmt;
5use core::ops::{Deref, DerefMut};
6use core::sync::atomic::{AtomicU64, Ordering};
7
8use arceos_api::task::{self as api, AxWaitQueueHandle};
9
10/// A mutual exclusion primitive useful for protecting shared data, similar to
11/// [`std::sync::Mutex`](https://doc.rust-lang.org/std/sync/struct.Mutex.html).
12///
13/// When the mutex is locked, the current task will block and be put into the
14/// wait queue. When the mutex is unlocked, all tasks waiting on the queue
15/// will be woken up.
16pub struct Mutex<T: ?Sized> {
17    wq: AxWaitQueueHandle,
18    owner_id: AtomicU64,
19    data: UnsafeCell<T>,
20}
21
22/// A guard that provides mutable data access.
23///
24/// When the guard falls out of scope it will release the lock.
25pub struct MutexGuard<'a, T: ?Sized + 'a> {
26    lock: &'a Mutex<T>,
27    data: *mut T,
28}
29
30// Same unsafe impls as `std::sync::Mutex`
31unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
32unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
33
34impl<T> Mutex<T> {
35    /// Creates a new [`Mutex`] wrapping the supplied data.
36    #[inline(always)]
37    pub const fn new(data: T) -> Self {
38        Self {
39            wq: AxWaitQueueHandle::new(),
40            owner_id: AtomicU64::new(0),
41            data: UnsafeCell::new(data),
42        }
43    }
44
45    /// Consumes this [`Mutex`] and unwraps the underlying data.
46    #[inline(always)]
47    pub fn into_inner(self) -> T {
48        // We know statically that there are no outstanding references to
49        // `self` so there's no need to lock.
50        let Mutex { data, .. } = self;
51        data.into_inner()
52    }
53}
54
55impl<T: ?Sized> Mutex<T> {
56    /// Returns `true` if the lock is currently held.
57    ///
58    /// # Safety
59    ///
60    /// This function provides no synchronization guarantees and so its result should be considered 'out of date'
61    /// the instant it is called. Do not use it for synchronization purposes. However, it may be useful as a heuristic.
62    #[inline(always)]
63    pub fn is_locked(&self) -> bool {
64        self.owner_id.load(Ordering::Relaxed) != 0
65    }
66
67    /// Locks the [`Mutex`] and returns a guard that permits access to the inner data.
68    ///
69    /// The returned value may be dereferenced for data access
70    /// and the lock will be dropped when the guard falls out of scope.
71    pub fn lock(&self) -> MutexGuard<T> {
72        let current_id = api::ax_current_task_id();
73        loop {
74            // Can fail to lock even if the spinlock is not locked. May be more efficient than `try_lock`
75            // when called in a loop.
76            match self.owner_id.compare_exchange_weak(
77                0,
78                current_id,
79                Ordering::Acquire,
80                Ordering::Relaxed,
81            ) {
82                Ok(_) => break,
83                Err(owner_id) => {
84                    assert_ne!(
85                        owner_id, current_id,
86                        "Thread({current_id}) tried to acquire mutex it already owns.",
87                    );
88                    // Wait until the lock looks unlocked before retrying
89                    api::ax_wait_queue_wait_until(&self.wq, || !self.is_locked(), None);
90                }
91            }
92        }
93        MutexGuard {
94            lock: self,
95            data: unsafe { &mut *self.data.get() },
96        }
97    }
98
99    /// Try to lock this [`Mutex`], returning a lock guard if successful.
100    #[inline(always)]
101    pub fn try_lock(&self) -> Option<MutexGuard<T>> {
102        let current_id = api::ax_current_task_id();
103        // The reason for using a strong compare_exchange is explained here:
104        // https://github.com/Amanieu/parking_lot/pull/207#issuecomment-575869107
105        if self
106            .owner_id
107            .compare_exchange(0, current_id, Ordering::Acquire, Ordering::Relaxed)
108            .is_ok()
109        {
110            Some(MutexGuard {
111                lock: self,
112                data: unsafe { &mut *self.data.get() },
113            })
114        } else {
115            None
116        }
117    }
118
119    /// Force unlock the [`Mutex`].
120    ///
121    /// # Safety
122    ///
123    /// This is *extremely* unsafe if the lock is not held by the current
124    /// thread. However, this can be useful in some instances for exposing
125    /// the lock to FFI that doesn’t know how to deal with RAII.
126    pub unsafe fn force_unlock(&self) {
127        let owner_id = self.owner_id.swap(0, Ordering::Release);
128        let current_id = api::ax_current_task_id();
129        assert_eq!(
130            owner_id, current_id,
131            "Thread({current_id}) tried to release mutex it doesn't own",
132        );
133        // wake up one waiting thread.
134        api::ax_wait_queue_wake(&self.wq, 1);
135    }
136
137    /// Returns a mutable reference to the underlying data.
138    ///
139    /// Since this call borrows the [`Mutex`] mutably, and a mutable reference is guaranteed to be exclusive in
140    /// Rust, no actual locking needs to take place -- the mutable borrow statically guarantees no locks exist. As
141    /// such, this is a 'zero-cost' operation.
142    #[inline(always)]
143    pub fn get_mut(&mut self) -> &mut T {
144        // We know statically that there are no other references to `self`, so
145        // there's no need to lock the inner mutex.
146        unsafe { &mut *self.data.get() }
147    }
148}
149
150impl<T: Default> Default for Mutex<T> {
151    #[inline(always)]
152    fn default() -> Self {
153        Self::new(Default::default())
154    }
155}
156
157impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
158    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159        match self.try_lock() {
160            Some(guard) => write!(f, "Mutex {{ data: ")
161                .and_then(|()| (*guard).fmt(f))
162                .and_then(|()| write!(f, "}}")),
163            None => write!(f, "Mutex {{ <locked> }}"),
164        }
165    }
166}
167
168impl<T: ?Sized> Deref for MutexGuard<'_, T> {
169    type Target = T;
170    #[inline(always)]
171    fn deref(&self) -> &T {
172        // We know statically that only we are referencing data
173        unsafe { &*self.data }
174    }
175}
176
177impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
178    #[inline(always)]
179    fn deref_mut(&mut self) -> &mut T {
180        // We know statically that only we are referencing data
181        unsafe { &mut *self.data }
182    }
183}
184
185impl<T: ?Sized + fmt::Debug> fmt::Debug for MutexGuard<'_, T> {
186    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187        fmt::Debug::fmt(&**self, f)
188    }
189}
190
191impl<T: ?Sized> Drop for MutexGuard<'_, T> {
192    /// The dropping of the [`MutexGuard`] will release the lock it was created from.
193    fn drop(&mut self) {
194        unsafe { self.lock.force_unlock() }
195    }
196}