ostd/sync/rwlock.rs
1// SPDX-License-Identifier: MPL-2.0
2
3use core::{
4 cell::UnsafeCell,
5 fmt,
6 marker::PhantomData,
7 ops::{Deref, DerefMut},
8 sync::atomic::{
9 AtomicUsize,
10 Ordering::{AcqRel, Acquire, Relaxed, Release},
11 },
12};
13
14use super::{
15 PreemptDisabled,
16 guard::{GuardTransfer, SpinGuardian},
17};
18use crate::task::atomic_mode::AsAtomicModeGuard;
19
20/// A spin lock that provides data access to either one writer or many readers.
21///
22/// # Overview
23///
24/// This lock allows for multiple readers, or at most one writer to access
25/// at any point in time. The writer of this lock has exclusive access to
26/// modify the underlying data, while the readers are allowed shared and
27/// read-only access.
28///
29/// The writing and reading portions cannot be active simultaneously, when
30/// one portion is in progress, the other portion will spin-wait. This is
31/// suitable for scenarios where the lock is expected to be held for short
32/// periods of time, and the overhead of context switching is higher than
33/// the cost of spinning.
34///
35/// In addition to traditional read and write locks, this implementation
36/// provides the upgradeable read lock (`upread lock`). The `upread lock`
37/// can be upgraded to write locks atomically, useful in scenarios
38/// where a decision to write is made after reading.
39///
40/// The type parameter `T` represents the data that this lock is protecting.
41/// It is necessary for `T` to satisfy [`Send`] to be shared across tasks and
42/// [`Sync`] to permit concurrent access via readers. The [`Deref`] method (and
43/// [`DerefMut`] for the writer) is implemented for the RAII guards returned
44/// by the locking methods, which allows for the access to the protected data
45/// while the lock is held.
46///
47/// # Usage
48///
49/// The lock can be used in scenarios where data needs to be read frequently
50/// but written to occasionally.
51///
52/// Use `upread lock` in scenarios where related checking is performed before
53/// modification to effectively avoid deadlocks and improve efficiency.
54///
55/// This lock should not be used in scenarios where lock-holding times are
56/// long as it can lead to CPU resource wastage due to spinning.
57///
58/// # About Guard
59///
60/// See the comments of [`SpinLock`].
61///
62/// # Examples
63///
64/// ```
65/// use ostd::sync::RwLock;
66///
67/// let lock = RwLock::new(5)
68///
69/// // many read locks can be held at once
70/// {
71/// let r1 = lock.read();
72/// let r2 = lock.read();
73/// assert_eq!(*r1, 5);
74/// assert_eq!(*r2, 5);
75///
76/// // Upgradeable read lock can share access to data with read locks
77/// let r3 = lock.upread();
78/// assert_eq!(*r3, 5);
79/// drop(r1);
80/// drop(r2);
81/// // read locks are dropped at this point
82///
83/// // An upread lock can only be upgraded successfully after all the
84/// // read locks are released, otherwise it will spin-wait.
85/// let mut w1 = r3.upgrade();
86/// *w1 += 1;
87/// assert_eq!(*w1, 6);
88/// } // upread lock are dropped at this point
89///
90/// {
91/// // Only one write lock can be held at a time
92/// let mut w2 = lock.write();
93/// *w2 += 1;
94/// assert_eq!(*w2, 7);
95/// } // write lock is dropped at this point
96/// ```
97///
98/// [`SpinLock`]: super::SpinLock
99pub struct RwLock<T: ?Sized, Guard = PreemptDisabled> {
100 guard: PhantomData<Guard>,
101 /// The internal representation of the lock state is as follows:
102 /// - **Bit 63:** Writer lock.
103 /// - **Bit 62:** Upgradeable reader lock.
104 /// - **Bit 61:** Indicates if an upgradeable reader is being upgraded.
105 /// - **Bits 60-0:** Reader lock count.
106 lock: AtomicUsize,
107 val: UnsafeCell<T>,
108}
109
110const READER: usize = 1;
111const WRITER: usize = 1 << (usize::BITS - 1);
112const UPGRADEABLE_READER: usize = 1 << (usize::BITS - 2);
113const BEING_UPGRADED: usize = 1 << (usize::BITS - 3);
114
115/// This bit is reserved as an overflow sentinel.
116/// We intentionally cap read guards before counter growth can affect
117/// `BEING_UPGRADED` / `UPGRADEABLE_READER` / `WRITER` bits.
118/// This is defense-in-depth with no extra runtime cost.
119///
120/// This follows the same strategy as Rust std's `Arc`,
121/// which uses `isize::MAX` as a sentinel to prevent its reference count
122/// from overflowing into values that could compromise safety.
123///
124/// On 64-bit platforms (the only targets Asterinas supports),
125/// a counter overflow is not a practical concern:
126/// incrementing one-by-one from zero to `MAX_READER` (2^60)
127/// would take hundreds of years even at billions of increments per second.
128/// Nevertheless, this sentinel provides an extra layer of safety at no runtime cost.
129const MAX_READER: usize = 1 << (usize::BITS - 4);
130
131impl<T, G> RwLock<T, G> {
132 /// Creates a new spin-based read-write lock with an initial value.
133 pub const fn new(val: T) -> Self {
134 Self {
135 guard: PhantomData,
136 lock: AtomicUsize::new(0),
137 val: UnsafeCell::new(val),
138 }
139 }
140}
141
142impl<T: ?Sized, G: SpinGuardian> RwLock<T, G> {
143 /// Acquires a read lock and spin-wait until it can be acquired.
144 ///
145 /// The calling thread will spin-wait until there are no writers or
146 /// upgrading upreaders present. There is no guarantee for the order
147 /// in which other readers or writers waiting simultaneously will
148 /// obtain the lock.
149 pub fn read(&self) -> RwLockReadGuard<'_, T, G> {
150 loop {
151 if let Some(readguard) = self.try_read() {
152 return readguard;
153 } else {
154 core::hint::spin_loop();
155 }
156 }
157 }
158
159 /// Acquires a write lock and spin-wait until it can be acquired.
160 ///
161 /// The calling thread will spin-wait until there are no other writers,
162 /// upreaders or readers present. There is no guarantee for the order
163 /// in which other readers or writers waiting simultaneously will
164 /// obtain the lock.
165 pub fn write(&self) -> RwLockWriteGuard<'_, T, G> {
166 loop {
167 if let Some(writeguard) = self.try_write() {
168 return writeguard;
169 } else {
170 core::hint::spin_loop();
171 }
172 }
173 }
174
175 /// Acquires an upreader and spin-wait until it can be acquired.
176 ///
177 /// The calling thread will spin-wait until there are no other writers,
178 /// or upreaders. There is no guarantee for the order in which other
179 /// readers or writers waiting simultaneously will obtain the lock.
180 ///
181 /// Upreader will not block new readers until it tries to upgrade. Upreader
182 /// and reader do not differ before invoking the upgrade method. However,
183 /// only one upreader can exist at any time to avoid deadlock in the
184 /// upgrade method.
185 pub fn upread(&self) -> RwLockUpgradeableGuard<'_, T, G> {
186 loop {
187 if let Some(guard) = self.try_upread() {
188 return guard;
189 } else {
190 core::hint::spin_loop();
191 }
192 }
193 }
194
195 /// Attempts to acquire a read lock.
196 ///
197 /// This function will never spin-wait and will return immediately.
198 pub fn try_read(&self) -> Option<RwLockReadGuard<'_, T, G>> {
199 let guard = G::read_guard();
200 let lock = self.lock.fetch_add(READER, Acquire);
201 if lock & (WRITER | MAX_READER | BEING_UPGRADED) == 0 {
202 Some(RwLockReadGuard { guard, inner: self })
203 } else {
204 self.lock.fetch_sub(READER, Release);
205 None
206 }
207 }
208
209 /// Attempts to acquire a write lock.
210 ///
211 /// This function will never spin-wait and will return immediately.
212 pub fn try_write(&self) -> Option<RwLockWriteGuard<'_, T, G>> {
213 let guard = G::guard();
214 if self
215 .lock
216 .compare_exchange(0, WRITER, Acquire, Relaxed)
217 .is_ok()
218 {
219 Some(RwLockWriteGuard { guard, inner: self })
220 } else {
221 None
222 }
223 }
224
225 /// Attempts to acquire an upread lock.
226 ///
227 /// This function will never spin-wait and will return immediately.
228 pub fn try_upread(&self) -> Option<RwLockUpgradeableGuard<'_, T, G>> {
229 let guard = G::guard();
230 let lock = self.lock.fetch_or(UPGRADEABLE_READER, Acquire) & (WRITER | UPGRADEABLE_READER);
231 if lock == 0 {
232 return Some(RwLockUpgradeableGuard { guard, inner: self });
233 } else if lock == WRITER {
234 self.lock.fetch_sub(UPGRADEABLE_READER, Release);
235 }
236 None
237 }
238
239 /// Returns a mutable reference to the underlying data.
240 ///
241 /// This method is zero-cost: By holding a mutable reference to the lock, the compiler has
242 /// already statically guaranteed that access to the data is exclusive.
243 pub fn get_mut(&mut self) -> &mut T {
244 self.val.get_mut()
245 }
246
247 /// Returns a raw pointer to the underlying data.
248 ///
249 /// This method is safe, but it's up to the caller to ensure that access to the data behind it
250 /// is still safe.
251 pub(super) fn as_ptr(&self) -> *mut T {
252 self.val.get()
253 }
254}
255
256impl<T: ?Sized + fmt::Debug, G> fmt::Debug for RwLock<T, G> {
257 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
258 fmt::Debug::fmt(&self.val, f)
259 }
260}
261
262// There can be more than one readers to get the `T`'s immutable reference,
263// so `T` must be `Sync` to guarantee the sharing safety.
264unsafe impl<T: ?Sized + Send, G> Send for RwLock<T, G> {}
265unsafe impl<T: ?Sized + Send + Sync, G> Sync for RwLock<T, G> {}
266
267// `G::Guard` and `G::ReadGuard` are `!Send` and `!Sync`, so are the guards.
268impl<T: ?Sized, G: SpinGuardian> !Send for RwLockWriteGuard<'_, T, G> {}
269impl<T: ?Sized, G: SpinGuardian> !Sync for RwLockWriteGuard<'_, T, G> {}
270
271impl<T: ?Sized, G: SpinGuardian> !Send for RwLockReadGuard<'_, T, G> {}
272impl<T: ?Sized, G: SpinGuardian> !Sync for RwLockReadGuard<'_, T, G> {}
273
274impl<T: ?Sized, G: SpinGuardian> !Send for RwLockUpgradeableGuard<'_, T, G> {}
275impl<T: ?Sized, G: SpinGuardian> !Sync for RwLockUpgradeableGuard<'_, T, G> {}
276
277/// A guard that provides immutable data access.
278#[clippy::has_significant_drop]
279#[must_use]
280pub struct RwLockReadGuard<'a, T: ?Sized, G: SpinGuardian> {
281 guard: G::ReadGuard,
282 inner: &'a RwLock<T, G>,
283}
284
285impl<T: ?Sized, G: SpinGuardian> AsAtomicModeGuard for RwLockReadGuard<'_, T, G> {
286 fn as_atomic_mode_guard(&self) -> &dyn crate::task::atomic_mode::InAtomicMode {
287 self.guard.as_atomic_mode_guard()
288 }
289}
290
291impl<T: ?Sized, G: SpinGuardian> Deref for RwLockReadGuard<'_, T, G> {
292 type Target = T;
293
294 fn deref(&self) -> &T {
295 // SAFETY: The mutex is locked by a reader, which provides shared access to the data.
296 unsafe { &*self.inner.val.get() }
297 }
298}
299
300impl<T: ?Sized, G: SpinGuardian> Drop for RwLockReadGuard<'_, T, G> {
301 fn drop(&mut self) {
302 self.inner.lock.fetch_sub(READER, Release);
303 }
304}
305
306impl<T: ?Sized + fmt::Debug, G: SpinGuardian> fmt::Debug for RwLockReadGuard<'_, T, G> {
307 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
308 fmt::Debug::fmt(&**self, f)
309 }
310}
311
312/// A guard that provides mutable data access.
313pub struct RwLockWriteGuard<'a, T: ?Sized, G: SpinGuardian> {
314 guard: G::Guard,
315 inner: &'a RwLock<T, G>,
316}
317
318impl<T: ?Sized, G: SpinGuardian> AsAtomicModeGuard for RwLockWriteGuard<'_, T, G> {
319 fn as_atomic_mode_guard(&self) -> &dyn crate::task::atomic_mode::InAtomicMode {
320 self.guard.as_atomic_mode_guard()
321 }
322}
323
324impl<T: ?Sized, G: SpinGuardian> Deref for RwLockWriteGuard<'_, T, G> {
325 type Target = T;
326
327 fn deref(&self) -> &T {
328 // SAFETY: The mutex is locked by a writer, which provides exclusive access to the data.
329 unsafe { &*self.inner.val.get() }
330 }
331}
332
333impl<T: ?Sized, G: SpinGuardian> DerefMut for RwLockWriteGuard<'_, T, G> {
334 fn deref_mut(&mut self) -> &mut Self::Target {
335 // SAFETY: The mutex is locked by a writer, which provides exclusive access to the data.
336 unsafe { &mut *self.inner.val.get() }
337 }
338}
339
340impl<T: ?Sized, G: SpinGuardian> Drop for RwLockWriteGuard<'_, T, G> {
341 fn drop(&mut self) {
342 self.inner.lock.fetch_and(!WRITER, Release);
343 }
344}
345
346impl<T: ?Sized + fmt::Debug, G: SpinGuardian> fmt::Debug for RwLockWriteGuard<'_, T, G> {
347 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
348 fmt::Debug::fmt(&**self, f)
349 }
350}
351
352/// A guard that provides immutable data access but can be atomically
353/// upgraded to [`RwLockWriteGuard`].
354pub struct RwLockUpgradeableGuard<'a, T: ?Sized, G: SpinGuardian> {
355 guard: G::Guard,
356 inner: &'a RwLock<T, G>,
357}
358
359impl<T: ?Sized, G: SpinGuardian> AsAtomicModeGuard for RwLockUpgradeableGuard<'_, T, G> {
360 fn as_atomic_mode_guard(&self) -> &dyn crate::task::atomic_mode::InAtomicMode {
361 self.guard.as_atomic_mode_guard()
362 }
363}
364
365impl<'a, T: ?Sized, G: SpinGuardian> RwLockUpgradeableGuard<'a, T, G> {
366 /// Upgrades this upread guard to a write guard atomically.
367 ///
368 /// After calling this method, subsequent readers will be blocked
369 /// while previous readers remain unaffected. The calling thread
370 /// will spin-wait until previous readers finish.
371 pub fn upgrade(mut self) -> RwLockWriteGuard<'a, T, G> {
372 self.inner.lock.fetch_or(BEING_UPGRADED, Acquire);
373 loop {
374 self = match self.try_upgrade() {
375 Ok(guard) => return guard,
376 Err(e) => e,
377 };
378 }
379 }
380
381 /// Attempts to upgrade this upread guard to a write guard atomically.
382 ///
383 /// This function will never spin-wait and will return immediately.
384 ///
385 /// This function is not exposed publicly because the `BEING_UPGRADED` bit
386 /// is set only in [`Self::upgrade`].
387 fn try_upgrade(mut self) -> Result<RwLockWriteGuard<'a, T, G>, Self> {
388 let res = self.inner.lock.compare_exchange(
389 UPGRADEABLE_READER | BEING_UPGRADED,
390 WRITER | UPGRADEABLE_READER,
391 AcqRel,
392 Relaxed,
393 );
394 if res.is_ok() {
395 let guard = self.guard.transfer_to();
396 let inner = self.inner;
397 drop(self);
398 Ok(RwLockWriteGuard { guard, inner })
399 } else {
400 Err(self)
401 }
402 }
403}
404
405impl<T: ?Sized, G: SpinGuardian> Deref for RwLockUpgradeableGuard<'_, T, G> {
406 type Target = T;
407
408 fn deref(&self) -> &T {
409 // SAFETY: The mutex is locked by a reader, which provides exclusive access to the data.
410 unsafe { &*self.inner.val.get() }
411 }
412}
413
414impl<T: ?Sized, G: SpinGuardian> Drop for RwLockUpgradeableGuard<'_, T, G> {
415 fn drop(&mut self) {
416 self.inner.lock.fetch_sub(UPGRADEABLE_READER, Release);
417 }
418}
419
420impl<T: ?Sized + fmt::Debug, G: SpinGuardian> fmt::Debug for RwLockUpgradeableGuard<'_, T, G> {
421 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
422 fmt::Debug::fmt(&**self, f)
423 }
424}