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}