keos_project4/sync/mutex.rs
1//! # Mutex.
2//!
3//! Mutex is a synchronization primitive that allows **only one thread at a
4//! time** to access a critical section of code, protecting shared resources
5//! such as memory, files, or device state from concurrent modification.
6//! Unlike the spin lock, it **blocks** threads trying to acquire it if another
7//! thread already holds the lock.
8//!
9//! The [`Mutex`] maintains the list of threads sleeping on the mutex.
10//! When unlocking, the kernel **wakes up** one of the waiting threads.
11//! In KeOS, you can sleep the current thread by calling the
12//! [`Current::park_with`]. This function takes a closure to run before falling
13//! in sleep with a argument [`ParkHandle`], which is the handle to wake up the
14//! sleeping thread.
15//!
16//! Although mutex and spin lock provides similar synchronization guarantees,
17//! they are used in different circumstances. The following table compares the
18//! spin lock and mutex.
19//!
20//! | | SpinLock | Mutex |
21//! |----------------|-------------------------|--------------------------|
22//! | Waiting thread | Spins (busy-waits) | Sleeps |
23//! | CPU usage | High (wastes CPU cycles) | Low (no busy waiting) |
24//! | Overhead | Low (fast if uncontended)| Higher (due to sleep/wake)|
25//!
26//! These characterists lead that the spin lock is suitable when critical
27//! sections are extremely short and contention is rare, because spinning wastes
28//! CPU cycles. On the other side, the mutex is better for longer critical
29//! sections or when a lock may be held for a non-trivial amount of time, as
30//! sleeping threads do not waste CPU.
31//!
32//! ## Implementation Requirements
33//! You need to implement the followings:
34//! - [`Mutex`]
35//! - [`Mutex::new`]
36//! - [`Mutex::lock`]
37//! - [`MutexGuard::unlock`]
38//!
39//! After implement the functionalities, move on to the next [`section`].
40//!
41//! [`section`]: crate::sync::condition_variable
42//! [`Current::park_with`]: keos::thread::Current::park_with
43
44use alloc::collections::vec_deque::VecDeque;
45use core::{
46 cell::UnsafeCell,
47 ops::{Deref, DerefMut},
48};
49use keos::{
50 sync::{SpinLock, WouldBlock, atomic::AtomicBool},
51 thread::{Current, ParkHandle},
52};
53
54/// A mutual exclusion primitive useful for protecting shared data
55///
56/// This mutex will block threads waiting for the lock to become available.
57/// The mutex can be created via a [`new`] constructor. Each spinlock has a
58/// type parameter which represents the data that it is protecting. The data can
59/// only be accessed through the guards returned from [`lock`] and
60/// [`try_lock`], which guarantees that the data is only ever accessed when the
61/// mutex is locked.
62///
63/// [`new`]: Self::new
64/// [`lock`]: Self::lock
65/// [`try_lock`]: Self::try_lock
66/// [`unwrap()`]: Result::unwrap
67///
68/// # Examples
69///
70/// ```
71/// use alloc::sync::Arc;
72/// use keos::sync::Mutex;
73/// use keos::thread;
74///
75/// const N: usize = 10;
76///
77/// // Spawn a few threads to increment a shared variable (non-atomically), and
78/// // let the main thread know once all increments are done.
79/// //
80/// // Here we're using an Arc to share memory among threads, and the data inside
81/// // the Arc is protected with a mutex.
82/// let data = Arc::new(Mutex::new(0));
83///
84/// for _ in 0..N {
85/// let data = Arc::clone(&data);
86/// thread::ThreadBuilder::new("work").spawn(move || {
87/// // The shared state can only be accessed once the lock is held.
88/// // Our non-atomic increment is safe because we're the only thread
89/// // which can access the shared state when the lock is held.
90/// //
91/// // We unwrap() the return value to assert that we are not expecting
92/// // threads to ever fail while holding the lock.
93/// let mut data = data.lock().unwrap();
94/// *data += 1;
95/// // the lock must be "explicitly" unlocked.
96/// data.unlock();
97/// });
98/// }
99/// ```
100pub struct Mutex<T> {
101 // TODO: Define any member you need.
102 t: UnsafeCell<T>,
103 waiters: SpinLock<VecDeque<ParkHandle>>,
104}
105
106unsafe impl<T: Send> Send for Mutex<T> {}
107unsafe impl<T: Send> Sync for Mutex<T> {}
108
109impl<T> Mutex<T> {
110 /// Creates a new mutex in an unlocked state ready for use.
111 ///
112 /// # Examples
113 ///
114 /// ```
115 /// use keos::sync::Mutex;
116 ///
117 /// let mutex = Mutex::new(0);
118 /// ```
119 #[inline]
120 pub const fn new(t: T) -> Mutex<T> {
121 Mutex {
122 // TODO: Initialize the members you added.
123 t: UnsafeCell::new(t),
124 waiters: SpinLock::new(VecDeque::new()),
125 }
126 }
127}
128
129impl<T> Mutex<T> {
130 /// Acquires a mutex, blocking the current thread until it is able to do
131 /// so.
132 ///
133 /// This function will block the local thread until it is available to
134 /// acquire the mutex. Upon returning, the thread is the only thread
135 /// with the lock held. An guard is returned to allow scoped unlock
136 /// of the lock. When the guard goes out of scope, the mutex will be
137 /// unlocked.
138 ///
139 /// The exact behavior on locking a mutex in the thread which already
140 /// holds the lock is left unspecified. However, this function will not
141 /// return on the second call (it might panic or deadlock, for example).
142 ///
143 /// # Examples
144 ///
145 /// ```
146 /// use alloc::sync::Arc;
147 /// use keos::sync::Mutex;
148 /// use keos::thread;
149 ///
150 /// let mutex = Arc::new(Mutex::new(0));
151 /// let c_mutex = Arc::clone(&spinlock);
152 ///
153 /// thread::spawn(move || {
154 /// *c_mutex.lock().unwrap() = 10;
155 /// }).join().expect("thread::spawn failed");
156 /// assert_eq!(*mutex.lock().unwrap(), 10);
157 /// ```
158 pub fn lock(&self) -> MutexGuard<'_, T> {
159 todo!()
160 }
161 /// Attempts to acquire this lock.
162 ///
163 /// If the lock could not be acquired at this time, then [`Err`] is
164 /// returned. Otherwise, an guard is returned.
165 ///
166 /// This function does not block.
167 ///
168 /// # Errors
169 ///
170 /// If the mutex could not be acquired because it is already locked, then
171 /// this call will return the [`WouldBlock`] error.
172 ///
173 /// # Examples
174 ///
175 /// ```
176 /// use keos::sync::Mutex;
177 /// use alloc::sync::Arc;
178 /// use keos::thread;
179 ///
180 /// let mutex = Arc::new(Mutex::new(0));
181 /// let c_mutex = Arc::clone(&spinlock);
182 ///
183 /// thread::spawn(move || {
184 /// let mut lock = c_mutex.try_lock();
185 /// if let Ok(ref mut mutex) = lock {
186 /// **mutex = 10;
187 /// } else {
188 /// println!("try_lock failed");
189 /// }
190 /// }).join().expect("thread::spawn failed");
191 /// assert_eq!(*mutex.lock().unwrap(), 10);
192 /// ```
193 pub fn try_lock(&self) -> Result<MutexGuard<'_, T>, WouldBlock> {
194 todo!();
195 // Incomplete implementation
196 Ok(MutexGuard {
197 t: unsafe { &mut *self.t.get() },
198 lock: self,
199 })
200 }
201
202 /// Consumes this mutex, returning the underlying data.
203 ///
204 /// # Examples
205 ///
206 /// ```
207 /// use keos::sync::Mutex;
208 ///
209 /// let mutex = Mutex::new(0);
210 /// assert_eq!(mutex.into_inner().unwrap(), 0);
211 /// ```
212 pub fn into_inner(self) -> T
213 where
214 T: Sized,
215 {
216 self.t.into_inner()
217 }
218}
219
220impl<T: Default> Default for Mutex<T> {
221 /// Creates a `Mutex<T>`, with the `Default` value for T.
222 fn default() -> Mutex<T> {
223 Mutex::new(Default::default())
224 }
225}
226
227impl<T> Deref for MutexGuard<'_, T> {
228 type Target = T;
229
230 fn deref(&self) -> &T {
231 self.t
232 }
233}
234
235impl<T> DerefMut for MutexGuard<'_, T> {
236 fn deref_mut(&mut self) -> &mut T {
237 self.t
238 }
239}
240
241/// An implementation of a "scoped lock" of a mutex. When this structure
242/// is dropped (falls out of scope) without unlocking, the panic occurs.
243///
244/// The lock must be explicitly unlocked by [`unlock`] method.
245///
246/// The data protected by the mutex can be accessed through this guard.
247///
248/// This structure is created by the [`lock`] and [`try_lock`] methods on
249/// [`Mutex`].
250///
251/// [`lock`]: Mutex::lock
252/// [`try_lock`]: Mutex::try_lock
253/// [`unlock`]: MutexGuard::unlock
254pub struct MutexGuard<'a, T: 'a> {
255 // Define any member you need.
256 t: &'a mut T,
257 lock: &'a Mutex<T>,
258}
259
260impl<T> !Send for MutexGuard<'_, T> {}
261unsafe impl<T: Sync> Sync for MutexGuard<'_, T> {}
262
263impl<T> MutexGuard<'_, T> {
264 /// Releases the underlying [`Mutex`].
265 ///
266 /// As the guard does **not** automatically release the lock on drop,
267 /// the caller must explicitly invoke [`unlock`] to mark the lock
268 /// as available again.
269 ///
270 /// # Example
271 /// ```
272 /// let lock = Mutex::new(123);
273 /// let guard = lock.lock();
274 ///
275 /// // Work with the locked data...
276 ///
277 /// // Explicitly release the lock.
278 /// guard.unlock();
279 /// ```
280 /// [`unlock`]: MutexGuard::unlock
281 pub fn unlock(mut self) {
282 todo!()
283 }
284}
285
286impl<T> Drop for MutexGuard<'_, T> {
287 fn drop(&mut self) {
288 panic!("`.unlock()` must be explicitly called for MutexGuard.");
289 }
290}