obkrnl/lock/mutex/
mod.rs

1use super::MTX_UNOWNED;
2use crate::context::{BorrowedArc, current_thread};
3use alloc::rc::Rc;
4use core::cell::UnsafeCell;
5use core::marker::PhantomData;
6use core::ops::{Deref, DerefMut};
7use core::sync::atomic::{AtomicUsize, Ordering};
8
9/// Implementation of `mtx` structure.
10pub struct Mutex<T> {
11    data: UnsafeCell<T>,
12    owning: AtomicUsize,          // mtx_lock
13    phantom: PhantomData<Rc<()>>, // For !Send and !Sync.
14}
15
16impl<T> Mutex<T> {
17    /// See `mtx_init` on the PS4 for a reference.
18    pub const fn new(data: T) -> Self {
19        Self {
20            data: UnsafeCell::new(data),
21            owning: AtomicUsize::new(MTX_UNOWNED),
22            phantom: PhantomData,
23        }
24    }
25
26    /// See `_mtx_lock_flags` on the Orbis for a reference.
27    ///
28    /// # Reference offsets
29    /// | Version | Offset |
30    /// |---------|--------|
31    /// |PS4 11.00|0x10E6A0|
32    pub fn lock(&self) -> MutexGuard<'_, T> {
33        // Check if the current thread can sleep.
34        let td = current_thread();
35
36        if !td.can_sleep() {
37            panic!("locking a mutex in a non-sleeping context is not supported");
38        }
39
40        // Take ownership.
41        if self
42            .owning
43            .compare_exchange(
44                MTX_UNOWNED,
45                BorrowedArc::as_ptr(&td) as usize,
46                Ordering::Acquire,
47                Ordering::Relaxed,
48            )
49            .is_err()
50        {
51            todo!()
52        }
53
54        td.set_active_mutexes(td.active_mutexes() + 1);
55
56        MutexGuard {
57            data: self.data.get(),
58            lock: &self.owning,
59            phantom: PhantomData,
60        }
61    }
62
63    /// See `_mtx_unlock_flags` on the PS4 for a reference.
64    ///
65    /// # Safety
66    /// Must be called by the thread that own `lock`.
67    unsafe fn unlock(lock: &AtomicUsize) {
68        let td = current_thread();
69
70        td.set_active_mutexes(td.active_mutexes() - 1);
71
72        // TODO: There is a check for (m->lock_object).lo_data == 0 on the PS4.
73        if lock
74            .compare_exchange(
75                BorrowedArc::as_ptr(&td) as usize,
76                MTX_UNOWNED,
77                Ordering::Release,
78                Ordering::Relaxed,
79            )
80            .is_err()
81        {
82            todo!()
83        }
84    }
85}
86
87impl<T: Default> Default for Mutex<T> {
88    fn default() -> Self {
89        Self::new(T::default())
90    }
91}
92
93unsafe impl<T: Send> Send for Mutex<T> {}
94unsafe impl<T: Send> Sync for Mutex<T> {}
95
96/// An RAII implementation of a "scoped lock" of a mutex. When this structure is dropped (falls out
97/// of scope), the lock will be unlocked.
98///
99/// This struct must not implement [`Send`].
100pub struct MutexGuard<'a, T> {
101    data: *mut T,
102    lock: *const AtomicUsize,
103    phantom: PhantomData<&'a Mutex<T>>,
104}
105
106impl<'a, T> MutexGuard<'a, T> {
107    pub fn map<O, F>(this: Self, f: F) -> MappedMutex<'a, O>
108    where
109        F: FnOnce(&'a mut T) -> O + 'a,
110    {
111        let data = unsafe { f(&mut *this.data) };
112        let lock = this.lock;
113
114        core::mem::forget(this);
115
116        MappedMutex {
117            data,
118            lock,
119            phantom: PhantomData,
120        }
121    }
122}
123
124impl<T> Drop for MutexGuard<'_, T> {
125    fn drop(&mut self) {
126        // SAFETY: This struct does not implement Send.
127        unsafe { Mutex::<T>::unlock(&*self.lock) };
128    }
129}
130
131impl<T> Deref for MutexGuard<'_, T> {
132    type Target = T;
133
134    fn deref(&self) -> &Self::Target {
135        unsafe { &*self.data }
136    }
137}
138
139impl<T> DerefMut for MutexGuard<'_, T> {
140    fn deref_mut(&mut self) -> &mut Self::Target {
141        unsafe { &mut *self.data }
142    }
143}
144
145unsafe impl<T: Sync> Sync for MutexGuard<'_, T> {}
146
147/// An RAII mutex guard returned by [`MutexGuard::map()`].
148///
149/// This struct must not implement [`Send`].
150pub struct MappedMutex<'a, T> {
151    data: T,
152    lock: *const AtomicUsize,
153    phantom: PhantomData<&'a Mutex<T>>,
154}
155
156impl<T> Drop for MappedMutex<'_, T> {
157    fn drop(&mut self) {
158        // SAFETY: This struct does not implement Send.
159        unsafe { Mutex::<T>::unlock(&*self.lock) };
160    }
161}
162
163impl<T> Deref for MappedMutex<'_, T> {
164    type Target = T;
165
166    fn deref(&self) -> &Self::Target {
167        &self.data
168    }
169}
170
171impl<T> DerefMut for MappedMutex<'_, T> {
172    fn deref_mut(&mut self) -> &mut Self::Target {
173        &mut self.data
174    }
175}
176
177unsafe impl<T: Sync> Sync for MappedMutex<'_, T> {}