obkrnl/lock/gutex/
mod.rs

1pub use self::guard::*;
2
3use super::MTX_UNOWNED;
4use crate::context::{BorrowedArc, current_thread};
5use alloc::rc::Rc;
6use alloc::sync::Arc;
7use core::cell::UnsafeCell;
8use core::marker::PhantomData;
9use core::sync::atomic::{AtomicUsize, Ordering};
10
11mod guard;
12
13/// A mutex that grant exclusive access to a group of members.
14///
15/// The [`crate::lock::Mutex`] is prone to deadlock when using on a multiple struct fields like
16/// this:
17///
18/// ```
19/// use crate::lock::Mutex;
20///
21/// pub struct Foo {
22///     field1: Mutex<()>,
23///     field2: Mutex<()>,
24/// }
25/// ```
26///
27/// The order to acquire the lock must be the same everywhere otherwise the deadlock is possible.
28/// Maintaining the lock order manually are cumbersome task so we introduce this type to handle this
29/// instead.
30///
31/// How this type are working is simple. Any locks on any member will lock the same mutex in the
32/// group, which mean there are only one mutex in the group. It have the same effect as the
33/// following code:
34///
35/// ```
36/// use crate::lock::Mutex;
37///
38/// pub struct Foo {
39///     data: Mutex<Data>,
40/// }
41///
42/// struct Data {
43///     field1: (),
44///     field2: (),
45/// }
46/// ```
47///
48/// The bonus point of this type is it will allow recursive lock for read-only access so you will
49/// never end up deadlock yourself. It will panic if you try to acquire write access while the
50/// readers are still active the same as [`core::cell::RefCell`].
51pub struct Gutex<T> {
52    group: Arc<GutexGroup>,
53    active: UnsafeCell<usize>,
54    value: UnsafeCell<T>,
55}
56
57impl<T> Gutex<T> {
58    /// # Panics
59    /// If there are any active reader or writer.
60    pub fn write(&self) -> GutexWrite<'_, T> {
61        // Check if there are active reader or writer.
62        let lock = self.group.lock();
63        let active = self.active.get();
64
65        // SAFETY: This is safe because we own the lock that protect both active and value.
66        unsafe {
67            if *active != 0 {
68                panic!(
69                    "attempt to acquire the write lock while there are an active reader or writer"
70                );
71            }
72
73            *active = usize::MAX;
74
75            GutexWrite::new(lock, active, self.value.get())
76        }
77    }
78}
79
80unsafe impl<T: Send> Send for Gutex<T> {}
81unsafe impl<T: Send> Sync for Gutex<T> {}
82
83/// Group of [`Gutex`].
84pub struct GutexGroup {
85    owning: AtomicUsize,
86    active: UnsafeCell<usize>,
87}
88
89impl GutexGroup {
90    /// # Context safety
91    /// This function does not require a CPU context on **stage 1** heap.
92    pub fn new() -> Arc<Self> {
93        Arc::new(Self {
94            owning: AtomicUsize::new(MTX_UNOWNED),
95            active: UnsafeCell::new(0),
96        })
97    }
98
99    /// # Context safety
100    /// This function does not require a CPU context.
101    pub fn spawn<T>(self: Arc<Self>, value: T) -> Gutex<T> {
102        Gutex {
103            group: self,
104            active: UnsafeCell::new(0),
105            value: UnsafeCell::new(value),
106        }
107    }
108
109    fn lock(&self) -> GroupGuard<'_> {
110        // Acquire the lock.
111        let td = current_thread();
112        let id = BorrowedArc::as_ptr(&td) as usize;
113
114        loop {
115            let owning = match self.owning.compare_exchange(
116                MTX_UNOWNED,
117                id,
118                Ordering::Acquire,
119                Ordering::Relaxed,
120            ) {
121                Ok(_) => break,
122                Err(v) => v,
123            };
124
125            if owning == id {
126                break;
127            }
128
129            self.wait();
130        }
131
132        // SAFETY: This is safe because the current thread acquire the lock successfully by the
133        // above compare_exchange().
134        unsafe { GroupGuard::new(self) }
135    }
136
137    #[inline(never)]
138    fn wait(&self) {
139        todo!()
140    }
141}
142
143unsafe impl Send for GutexGroup {}
144unsafe impl Sync for GutexGroup {}
145
146/// An RAII object used to release the lock on [`GutexGroup`]. This type cannot be send because it
147/// will cause data race on the group when dropping if more than one [`GroupGuard`] are active.
148struct GroupGuard<'a> {
149    group: &'a GutexGroup,
150    phantom: PhantomData<Rc<()>>, // For !Send and !Sync.
151}
152
153impl<'a> GroupGuard<'a> {
154    /// # Safety
155    /// The group must be locked by the calling thread with no active references to any of its
156    /// field.
157    unsafe fn new(group: &'a GutexGroup) -> Self {
158        unsafe { *group.active.get() += 1 };
159
160        Self {
161            group,
162            phantom: PhantomData,
163        }
164    }
165
166    #[inline(never)]
167    fn release(&mut self) {
168        self.group.owning.store(MTX_UNOWNED, Ordering::Release);
169
170        todo!("wakeup waiting thread");
171    }
172}
173
174impl Drop for GroupGuard<'_> {
175    fn drop(&mut self) {
176        unsafe {
177            let active = self.group.active.get();
178
179            *active -= 1;
180
181            if *active != 0 {
182                return;
183            }
184        }
185
186        self.release();
187    }
188}