Skip to main content

ostd/sync/
rwmutex.rs

1// SPDX-License-Identifier: MPL-2.0
2
3use core::{
4    cell::UnsafeCell,
5    fmt,
6    ops::{Deref, DerefMut},
7    sync::atomic::{
8        AtomicUsize,
9        Ordering::{AcqRel, Acquire, Relaxed, Release},
10    },
11};
12
13use super::WaitQueue;
14
15/// A mutex that provides data access to either one writer or many readers.
16///
17/// # Overview
18///
19/// This mutex allows for multiple readers, or at most one writer to access
20/// at any point in time. The writer of this mutex has exclusive access to
21/// modify the underlying data, while the readers are allowed shared and
22/// read-only access.
23///
24/// The writing and reading portions cannot be active simultaneously, when
25/// one portion is in progress, the other portion will sleep. This is
26/// suitable for scenarios where the mutex is expected to be held for a
27/// period of time, which can avoid wasting CPU resources.
28///
29/// This implementation provides the upgradeable read mutex (`upread mutex`).
30/// The `upread mutex` can be upgraded to write mutex atomically, useful in
31/// scenarios where a decision to write is made after reading.
32///
33/// The type parameter `T` represents the data that this mutex is protecting.
34/// It is necessary for `T` to satisfy [`Send`] to be shared across tasks and
35/// [`Sync`] to permit concurrent access via readers. The [`Deref`] method (and
36/// [`DerefMut`] for the writer) is implemented for the RAII guards returned
37/// by the locking methods, which allows for the access to the protected data
38/// while the mutex is held.
39///
40/// # Usage
41///
42/// The mutex can be used in scenarios where data needs to be read frequently
43/// but written to occasionally.
44///
45/// Use `upread mutex` in scenarios where related checking is performed before
46/// modification to effectively avoid deadlocks and improve efficiency.
47///
48/// # Safety
49///
50/// Avoid using `RwMutex` in an interrupt context, as it may result in sleeping
51/// and never being awakened.
52///
53/// # Examples
54///
55/// ```
56/// use ostd::sync::RwMutex;
57///
58/// let mutex = RwMutex::new(5)
59///
60/// // many read mutexes can be held at once
61/// {
62///     let r1 = mutex.read();
63///     let r2 = mutex.read();
64///     assert_eq!(*r1, 5);
65///     assert_eq!(*r2, 5);
66///
67///     // Upgradeable read mutex can share access to data with read mutexes
68///     let r3 = mutex.upread();
69///     assert_eq!(*r3, 5);
70///     drop(r1);
71///     drop(r2);
72///     // read mutexes are dropped at this point
73///
74///     // An upread mutex can only be upgraded successfully after all the
75///     // read mutexes are released, otherwise it will spin-wait.
76///     let mut w1 = r3.upgrade();
77///     *w1 += 1;
78///     assert_eq!(*w1, 6);
79/// }   // upread mutex are dropped at this point
80///
81/// {
82///     // Only one write mutex can be held at a time
83///     let mut w2 = mutex.write();
84///     *w2 += 1;
85///     assert_eq!(*w2, 7);
86/// }   // write mutex is dropped at this point
87/// ```
88pub struct RwMutex<T: ?Sized> {
89    /// The internal representation of the mutex state is as follows:
90    /// - **Bit 63:** Writer mutex.
91    /// - **Bit 62:** Upgradeable reader mutex.
92    /// - **Bit 61:** Indicates if an upgradeable reader is being upgraded.
93    /// - **Bits 60-0:** Reader mutex count.
94    lock: AtomicUsize,
95    /// Threads that fail to acquire the mutex will sleep on this waitqueue.
96    queue: WaitQueue,
97    val: UnsafeCell<T>,
98}
99
100const READER: usize = 1;
101const WRITER: usize = 1 << (usize::BITS - 1);
102const UPGRADEABLE_READER: usize = 1 << (usize::BITS - 2);
103const BEING_UPGRADED: usize = 1 << (usize::BITS - 3);
104
105/// This bit is reserved as an overflow sentinel.
106/// For more details, see comments on the `MAX_READER` constant
107/// in the [`super::rwlock`] module.
108const MAX_READER: usize = 1 << (usize::BITS - 4);
109
110impl<T> RwMutex<T> {
111    /// Creates a new read-write mutex with an initial value.
112    pub const fn new(val: T) -> Self {
113        Self {
114            lock: AtomicUsize::new(0),
115            queue: WaitQueue::new(),
116            val: UnsafeCell::new(val),
117        }
118    }
119}
120
121impl<T: ?Sized> RwMutex<T> {
122    /// Acquires a read mutex and sleep until it can be acquired.
123    ///
124    /// The calling thread will sleep until there are no writers or upgrading
125    /// upreaders present. The implementation of [`WaitQueue`] guarantees the
126    /// order in which other concurrent readers or writers waiting simultaneously
127    /// will acquire the mutex.
128    #[track_caller]
129    pub fn read(&self) -> RwMutexReadGuard<'_, T> {
130        self.queue.wait_until(|| self.try_read())
131    }
132
133    /// Acquires a write mutex and sleep until it can be acquired.
134    ///
135    /// The calling thread will sleep until there are no writers, upreaders,
136    /// or readers present. The implementation of [`WaitQueue`] guarantees the
137    /// order in which other concurrent readers or writers waiting simultaneously
138    /// will acquire the mutex.
139    #[track_caller]
140    pub fn write(&self) -> RwMutexWriteGuard<'_, T> {
141        self.queue.wait_until(|| self.try_write())
142    }
143
144    /// Acquires a upread mutex and sleep until it can be acquired.
145    ///
146    /// The calling thread will sleep until there are no writers or upreaders present.
147    /// The implementation of [`WaitQueue`] guarantees the order in which other concurrent
148    /// readers or writers waiting simultaneously will acquire the mutex.
149    ///
150    /// Upreader will not block new readers until it tries to upgrade. Upreader
151    /// and reader do not differ before invoking the upgrade method. However,
152    /// only one upreader can exist at any time to avoid deadlock in the
153    /// upgrade method.
154    #[track_caller]
155    pub fn upread(&self) -> RwMutexUpgradeableGuard<'_, T> {
156        self.queue.wait_until(|| self.try_upread())
157    }
158
159    /// Attempts to acquire a read mutex.
160    ///
161    /// This function will never sleep and will return immediately.
162    pub fn try_read(&self) -> Option<RwMutexReadGuard<'_, T>> {
163        let lock = self.lock.fetch_add(READER, Acquire);
164        if lock & (WRITER | BEING_UPGRADED | MAX_READER) == 0 {
165            Some(RwMutexReadGuard { inner: self })
166        } else {
167            self.lock.fetch_sub(READER, Release);
168            None
169        }
170    }
171
172    /// Attempts to acquire a write mutex.
173    ///
174    /// This function will never sleep and will return immediately.
175    pub fn try_write(&self) -> Option<RwMutexWriteGuard<'_, T>> {
176        if self
177            .lock
178            .compare_exchange(0, WRITER, Acquire, Relaxed)
179            .is_ok()
180        {
181            Some(RwMutexWriteGuard { inner: self })
182        } else {
183            None
184        }
185    }
186
187    /// Attempts to acquire a upread mutex.
188    ///
189    /// This function will never sleep and will return immediately.
190    pub fn try_upread(&self) -> Option<RwMutexUpgradeableGuard<'_, T>> {
191        let lock = self.lock.fetch_or(UPGRADEABLE_READER, Acquire) & (WRITER | UPGRADEABLE_READER);
192        if lock == 0 {
193            return Some(RwMutexUpgradeableGuard { inner: self });
194        } else if lock == WRITER {
195            self.lock.fetch_sub(UPGRADEABLE_READER, Release);
196        }
197        None
198    }
199
200    /// Returns a mutable reference to the underlying data.
201    ///
202    /// This method is zero-cost: By holding a mutable reference to the lock, the compiler has
203    /// already statically guaranteed that access to the data is exclusive.
204    pub fn get_mut(&mut self) -> &mut T {
205        self.val.get_mut()
206    }
207}
208
209impl<T: ?Sized + fmt::Debug> fmt::Debug for RwMutex<T> {
210    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
211        fmt::Debug::fmt(&self.val, f)
212    }
213}
214
215// There can be more than one readers to get the `T`'s immutable reference,
216// so `T` must be `Sync` to guarantee the sharing safety.
217unsafe impl<T: ?Sized + Send> Send for RwMutex<T> {}
218unsafe impl<T: ?Sized + Send + Sync> Sync for RwMutex<T> {}
219
220impl<T: ?Sized> !Send for RwMutexWriteGuard<'_, T> {}
221unsafe impl<T: ?Sized + Sync> Sync for RwMutexWriteGuard<'_, T> {}
222
223impl<T: ?Sized> !Send for RwMutexReadGuard<'_, T> {}
224unsafe impl<T: ?Sized + Sync> Sync for RwMutexReadGuard<'_, T> {}
225
226impl<T: ?Sized> !Send for RwMutexUpgradeableGuard<'_, T> {}
227unsafe impl<T: ?Sized + Sync> Sync for RwMutexUpgradeableGuard<'_, T> {}
228
229/// A guard that provides immutable data access.
230pub struct RwMutexReadGuard<'a, T: ?Sized> {
231    inner: &'a RwMutex<T>,
232}
233
234impl<T: ?Sized> Deref for RwMutexReadGuard<'_, T> {
235    type Target = T;
236
237    fn deref(&self) -> &T {
238        // SAFETY: The mutex is locked by a reader, which provides shared access to the data.
239        unsafe { &*self.inner.val.get() }
240    }
241}
242
243impl<T: ?Sized> Drop for RwMutexReadGuard<'_, T> {
244    fn drop(&mut self) {
245        // When there are no readers, wake up a waiting writer.
246        if self.inner.lock.fetch_sub(READER, Release) == READER {
247            self.inner.queue.wake_one();
248        }
249    }
250}
251
252impl<T: ?Sized + fmt::Debug> fmt::Debug for RwMutexReadGuard<'_, T> {
253    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
254        fmt::Debug::fmt(&**self, f)
255    }
256}
257
258/// A guard that provides mutable data access.
259#[clippy::has_significant_drop]
260#[must_use]
261pub struct RwMutexWriteGuard<'a, T: ?Sized> {
262    inner: &'a RwMutex<T>,
263}
264
265impl<'a, T: ?Sized> RwMutexWriteGuard<'a, T> {
266    /// Atomically downgrades a write guard to an upgradeable reader guard.
267    ///
268    /// This method always succeeds because the lock is exclusively held by the writer.
269    pub fn downgrade(mut self) -> RwMutexUpgradeableGuard<'a, T> {
270        loop {
271            self = match self.try_downgrade() {
272                Ok(guard) => return guard,
273                Err(e) => e,
274            };
275        }
276    }
277
278    /// This is not exposed as a public method to prevent intermediate lock states from affecting the
279    /// downgrade process.
280    fn try_downgrade(self) -> Result<RwMutexUpgradeableGuard<'a, T>, Self> {
281        let inner = self.inner;
282        let res = self
283            .inner
284            .lock
285            .compare_exchange(WRITER, UPGRADEABLE_READER, AcqRel, Relaxed);
286        if res.is_ok() {
287            drop(self);
288            Ok(RwMutexUpgradeableGuard { inner })
289        } else {
290            Err(self)
291        }
292    }
293}
294
295impl<T: ?Sized> Deref for RwMutexWriteGuard<'_, T> {
296    type Target = T;
297
298    fn deref(&self) -> &T {
299        // SAFETY: The mutex is locked by a writer, which provides exclusive access to the data.
300        unsafe { &*self.inner.val.get() }
301    }
302}
303
304impl<T: ?Sized> DerefMut for RwMutexWriteGuard<'_, T> {
305    fn deref_mut(&mut self) -> &mut Self::Target {
306        // SAFETY: The mutex is locked by a writer, which provides exclusive access to the data.
307        unsafe { &mut *self.inner.val.get() }
308    }
309}
310
311impl<T: ?Sized> Drop for RwMutexWriteGuard<'_, T> {
312    fn drop(&mut self) {
313        self.inner.lock.fetch_and(!WRITER, Release);
314
315        // When the current writer releases, wake up all the sleeping threads.
316        // All awakened threads may include readers and writers.
317        // Thanks to the `wait_until` method, either all readers
318        // continue to execute or one writer continues to execute.
319        self.inner.queue.wake_all();
320    }
321}
322
323impl<T: ?Sized + fmt::Debug> fmt::Debug for RwMutexWriteGuard<'_, T> {
324    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
325        fmt::Debug::fmt(&**self, f)
326    }
327}
328
329/// A guard that provides immutable data access but can be atomically
330/// upgraded to [`RwMutexWriteGuard`].
331pub struct RwMutexUpgradeableGuard<'a, T: ?Sized> {
332    inner: &'a RwMutex<T>,
333}
334
335impl<'a, T: ?Sized> RwMutexUpgradeableGuard<'a, T> {
336    /// Upgrades this upread guard to a write guard atomically.
337    ///
338    /// After calling this method, subsequent readers will be blocked
339    /// while previous readers remain unaffected.
340    ///
341    /// The calling thread will not sleep, but spin to wait for the existing
342    /// reader to be released. There are two main reasons.
343    /// - First, it needs to sleep in an extra waiting queue and needs extra
344    ///   wake-up logic and overhead.
345    /// - Second, upgrading method usually requires a high response time
346    ///   (because the mutex is being used now).
347    pub fn upgrade(mut self) -> RwMutexWriteGuard<'a, T> {
348        self.inner.lock.fetch_or(BEING_UPGRADED, Acquire);
349        loop {
350            self = match self.try_upgrade() {
351                Ok(guard) => return guard,
352                Err(e) => e,
353            };
354        }
355    }
356
357    /// Attempts to upgrade this upread guard to a write guard atomically.
358    ///
359    /// This function will return immediately.
360    ///
361    /// This function is not exposed publicly because the `BEING_UPGRADED` bit
362    /// is set only in [`Self::upgrade`].
363    fn try_upgrade(self) -> Result<RwMutexWriteGuard<'a, T>, Self> {
364        let res = self.inner.lock.compare_exchange(
365            UPGRADEABLE_READER | BEING_UPGRADED,
366            WRITER | UPGRADEABLE_READER,
367            AcqRel,
368            Relaxed,
369        );
370        if res.is_ok() {
371            let inner = self.inner;
372            drop(self);
373            Ok(RwMutexWriteGuard { inner })
374        } else {
375            Err(self)
376        }
377    }
378}
379
380impl<T: ?Sized> Deref for RwMutexUpgradeableGuard<'_, T> {
381    type Target = T;
382
383    fn deref(&self) -> &T {
384        // SAFETY: The mutex is locked by a reader, which provides shared access to the data.
385        unsafe { &*self.inner.val.get() }
386    }
387}
388
389impl<T: ?Sized> Drop for RwMutexUpgradeableGuard<'_, T> {
390    fn drop(&mut self) {
391        let res = self.inner.lock.fetch_sub(UPGRADEABLE_READER, Release);
392        if res == UPGRADEABLE_READER {
393            self.inner.queue.wake_all();
394        }
395    }
396}
397
398impl<T: ?Sized + fmt::Debug> fmt::Debug for RwMutexUpgradeableGuard<'_, T> {
399    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
400        fmt::Debug::fmt(&**self, f)
401    }
402}