Skip to main content

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