maitake_sync/rwlock/owned.rs
1use super::*;
2use crate::semaphore;
3use alloc::sync::Arc;
4
5/// Owned [RAII] structure used to release the shared read access of a
6/// [`RwLock`] when dropped.
7///
8/// This type is similar to the [`RwLockReadGuard`] type, but it is only
9/// returned by an [`RwLock`] that is wrapped in an an [`Arc`]. Instead
10/// of borrowing the [`RwLock`], this guard holds an [`Arc`] clone of
11/// the [`RwLock`], incrementing its reference count. Therefore, this
12/// type can outlive the [`RwLock`] that created it, and it is valid for
13/// the `'static` lifetime. Beyond this, it is identical to the
14/// [`RwLockReadGuard`] type.
15///
16/// The data protected by the [`RwLock`] can be accessed through this
17/// guard via its [`Deref`](#impl-Deref) implementation.
18///
19/// This guard can be held across any `.await` point, as it implements
20/// [`Send`].
21///
22/// This structure is created by the [`read_owned`] and
23/// [`try_read_owned`] methods on [`Arc`]`<`[`RwLock`]`>`.
24///
25/// [RAII]: https://rust-unofficial.github.io/patterns/patterns/behavioural/RAII.html
26/// [`read_owned`]: RwLock::read_owned
27/// [`try_read_owned`]: RwLock::try_read_owned
28#[must_use = "if unused, the `RwLock` will immediately unlock"]
29pub struct OwnedRwLockReadGuard<T: ?Sized> {
30 /// /!\ WARNING: semi-load-bearing drop order /!\
31 ///
32 /// This struct's field ordering is important for Loom tests; the `ConstPtr`
33 /// must be dropped before the semaphore permit is released back to the
34 /// semaphore, as this may wake another task that wants to mutably access
35 /// the cell. However, Loom will still consider the data to be "immutably
36 /// accessed" until the ConstPtr` is dropped, so we must drop the `ConstPtr`
37 /// first.
38 ///
39 /// This isn't actually a bug in "real life", because we're not going to
40 /// actually *read* the data through the `ConstPtr` in the guard's `Drop`
41 /// impl, but Loom considers us to be "accessing" it as long as the
42 /// `ConstPtr` exists.
43 data: cell::ConstPtr<T>,
44 _lock: AddPermits<1, T>,
45}
46
47/// Owned [RAII] structure used to release the exclusive write access of a
48/// [`RwLock`] when dropped.
49///
50/// This type is similar to the [`RwLockWriteGuard`] type, but it is
51/// only returned by an [`RwLock`] that is wrapped in an an [`Arc`].
52/// Instead of borrowing the [`RwLock`], this guard holds an [`Arc`]
53/// clone of the [`RwLock`], incrementing its reference count.
54/// Therefore, this type can outlive the [`RwLock`] that created it, and
55/// it is valid for the `'static` lifetime. Beyond this, is identical to
56/// the [`RwLockWriteGuard`] type.
57///
58/// The data protected by the [`RwLock`] can be accessed through this
59/// guard via its [`Deref`](#impl-Deref) and [`DerefMut`](#impl-Deref)
60/// implementations.
61///
62/// This guard can be held across any `.await` point, as it implements
63/// [`Send`].
64///
65/// This structure is created by the [`read_owned`] and
66/// [`try_read_owned`] methods on [`Arc`]`<`[`RwLock`]`>`.
67///
68/// [RAII]: https://rust-unofficial.github.io/patterns/patterns/behavioural/RAII.html
69/// [`read_owned`]: RwLock::read_owned
70/// [`try_read_owned`]: RwLock::try_read_owned
71#[must_use = "if unused, the `RwLock` will immediately unlock"]
72pub struct OwnedRwLockWriteGuard<T: ?Sized> {
73 /// /!\ WARNING: semi-load-bearing drop order /!\
74 ///
75 /// This struct's field ordering is important for Loom tests; the `MutPtr`
76 /// must be dropped before the semaphore permits are released back to the
77 /// semaphore, as this may wake another task that wants to access the cell.
78 /// However, Loom will still consider the data to be "mutably accessed"
79 /// until the `MutPtr` is dropped, so we must drop the `MutPtr` first.
80 ///
81 /// This isn't actually a bug in "real life", because we're not going to
82 /// actually read or write the data through the `MutPtr` in the guard's
83 /// `Drop` impl, but Loom considers us to be "accessing" it as long as the
84 /// `MutPtr` exists.
85 data: cell::MutPtr<T>,
86 _lock: AddPermits<{ semaphore::MAX_PERMITS }, T>,
87}
88
89/// A wrapper around an `RwLock` `Arc` clone that releases a fixed number of
90/// permits when it's dropped.
91///
92/// This is factored out to a separate type to ensure that it's dropped *after*
93/// the `MutPtr`/`ConstPtr`s are dropped, to placate `loom`.
94struct AddPermits<const PERMITS: usize, T: ?Sized>(Arc<RwLock<T>>);
95
96// === impl RwLock ===
97
98impl<T: ?Sized> RwLock<T> {
99 /// Locks this `RwLock` with shared read access, returning an [owned RAII
100 /// guard][guard].
101 ///
102 /// This method is identical to [`RwLock::read`], execept that it requires
103 /// the `RwLock` to be wrapped in an [`Arc`], and returns an
104 /// [`OwnedRwLockReadGuard`] that clones the [`Arc`] rather than
105 /// borrowing the lock. Therefore, the returned guard is valid for the
106 /// `'static` lifetime.
107 ///
108 /// If the lock is locked for write access, the calling task will yield and
109 /// wait until there are no writers which hold the lock. There may be other
110 /// readers inside the lock when the task resumes.
111 ///
112 /// Note that under the [priority policy] of [`RwLock`], read locks are not
113 /// granted until prior write locks, to prevent starvation. Therefore
114 /// deadlock may occur if a read lock is held by the current task, a write
115 /// lock attempt is made, and then a subsequent read lock attempt is made
116 /// by the current task.
117 ///
118 /// Returns [an RAII guard][guard] which will release this read access of the
119 /// `RwLock` when dropped.
120 ///
121 /// # Cancellation
122 ///
123 /// This method [uses a queue to fairly distribute locks][priority policy]
124 /// in the order they were requested. Cancelling a call to `read` results
125 /// in the calling task losing its place in the queue.
126 ///
127 /// # Examples
128 ///
129 /// ```
130 /// # use tokio::task;
131 /// # #[tokio::main(flavor = "current_thread")]
132 /// # async fn test() {
133 /// # // since we are targeting no-std, it makes more sense to use `alloc`
134 /// # // in these examples, rather than `std`...but i don't want to make
135 /// # // the tests actually `#![no_std]`...
136 /// # use std as alloc;
137 /// use maitake_sync::RwLock;
138 /// use alloc::sync::Arc;
139 ///
140 /// let lock = Arc::new(RwLock::new(1));
141 /// // hold the lock for reading in `main`.
142 /// let n = lock
143 /// .try_read()
144 /// .expect("read lock must be acquired, as the lock is unlocked");
145 /// assert_eq!(*n, 1);
146 ///
147 /// # let task =
148 /// task::spawn({
149 /// let lock = lock.clone();
150 /// async move {
151 /// // While main has an active read lock, this task can acquire
152 /// // one too.
153 /// let n = lock.read_owned().await;
154 /// assert_eq!(*n, 1);
155 /// }
156 /// });
157 /// # task.await.unwrap();
158 /// # }
159 /// # test();
160 /// ```
161 ///
162 /// [priority policy]: Self#priority-policy
163 /// [guard]: OwnedRwLockReadGuard
164 pub async fn read_owned(self: &Arc<Self>) -> OwnedRwLockReadGuard<T> {
165 let guard = self.read().await;
166 OwnedRwLockReadGuard::from_borrowed(self.clone(), guard)
167 }
168
169 /// Locks this `RwLock` with exclusive write access,returning an [owned RAII
170 /// guard][guard].
171 ///
172 /// This method is identical to [`RwLock::write`], execept that it requires
173 /// the `RwLock` to be wrapped in an [`Arc`], and returns an
174 /// [`OwnedRwLockWriteGuard`] that clones the [`Arc`] rather than
175 /// borrowing the lock. Therefore, the returned guard is valid for the
176 /// `'static` lifetime.
177 ///
178 /// # Returns
179 ///
180 /// If other tasks are holding a read or write lock, the calling task will
181 /// wait until the write lock or all read locks are released.
182 ///
183 /// Returns [an RAII guard][guard] which will release the write access of this
184 /// `RwLock` when dropped.
185 ///
186 /// # Cancellation
187 ///
188 /// This method [uses a queue to fairly distribute
189 /// locks](Self#priority-policy) in the order they were requested.
190 /// Cancelling a call to `write` results in the calling task losing its place
191 /// in the queue.
192 ///
193 /// # Examples
194 ///
195 /// ```
196 /// # use tokio::task;
197 /// # #[tokio::main(flavor = "current_thread")]
198 /// # async fn test() {
199 /// # // since we are targeting no-std, it makes more sense to use `alloc`
200 /// # // in these examples, rather than `std`...but i don't want to make
201 /// # // the tests actually `#![no_std]`...
202 /// # use std as alloc;
203 /// use maitake_sync::RwLock;
204 /// use alloc::sync::Arc;
205 ///
206 /// let lock = Arc::new(RwLock::new(1));
207 ///
208 /// # let task =
209 /// task::spawn(async move {
210 /// let mut guard = lock.write_owned().await;
211 /// *guard += 1;
212 /// });
213 /// # task.await.unwrap();
214 /// # }
215 /// # test();
216 /// ```
217 ///
218 /// [guard]: OwnedRwLockWriteGuard
219 pub async fn write_owned(self: &Arc<Self>) -> OwnedRwLockWriteGuard<T> {
220 let guard = self.write().await;
221 OwnedRwLockWriteGuard::from_borrowed(self.clone(), guard)
222 }
223
224 /// Attempts to acquire this `RwLock` for shared read access, without
225 /// waiting, and returning an [owned RAII guard][guard].
226 ///
227 /// This method is identical to [`RwLock::try_read`], execept that it requires
228 /// the `RwLock` to be wrapped in an [`Arc`], and returns an
229 /// [`OwnedRwLockReadGuard`] that clones the [`Arc`] rather than
230 /// borrowing the lock. Therefore, the returned guard is valid for the
231 /// `'static` lifetime.
232 ///
233 /// # Returns
234 ///
235 /// If the access couldn't be acquired immediately, this method returns
236 /// [`None`] rather than waiting.
237 ///
238 /// Otherwise, [an RAII guard][guard] is returned, which allows read access to the
239 /// protected data and will release that access when dropped.
240 ///
241 /// # Examples
242 ///
243 /// ```
244 /// # fn main() {
245 /// # // since we are targeting no-std, it makes more sense to use `alloc`
246 /// # // in these examples, rather than `std`...but i don't want to make
247 /// # // the tests actually `#![no_std]`...
248 /// # use std as alloc;
249 /// use maitake_sync::RwLock;
250 /// use alloc::sync::Arc;
251 ///
252 /// let lock = Arc::new(RwLock::new(1));
253 ///
254 /// let mut write_guard = lock
255 /// .try_write()
256 /// .expect("lock is unlocked, so write access should be acquired");
257 /// *write_guard += 1;
258 ///
259 /// // because a write guard is held, we cannot acquire the read lock, so
260 /// // this will return `None`.
261 /// assert!(lock.try_read_owned().is_none());
262 /// # }
263 /// ```
264 ///
265 /// [guard]: OwnedRwLockReadGuard
266 pub fn try_read_owned(self: &Arc<Self>) -> Option<OwnedRwLockReadGuard<T>> {
267 self.try_read()
268 .map(|guard| OwnedRwLockReadGuard::from_borrowed(self.clone(), guard))
269 }
270
271 /// Attempts to acquire this `RwLock` for exclusive write access, without
272 /// waiting, and returning an [owned RAII guard][guard].
273 ///
274 /// This method is identical to [`RwLock::try_write`], execept that it requires
275 /// the `RwLock` to be wrapped in an [`Arc`], and returns an
276 /// [`OwnedRwLockWriteGuard`] that clones the [`Arc`] rather than
277 /// borrowing the lock. Therefore, the returned guard is valid for the
278 /// `'static` lifetime.
279 ///
280 /// # Returns
281 ///
282 /// If the access couldn't be acquired immediately, this method returns
283 /// [`None`] rather than waiting.
284 ///
285 /// Otherwise, [an RAII guard][guard] is returned, which allows write access to the
286 /// protected data and will release that access when dropped.
287 ///
288 /// # Examples
289 ///
290 /// ```
291 /// # fn main() {
292 /// # // since we are targeting no-std, it makes more sense to use `alloc`
293 /// # // in these examples, rather than `std`...but i don't want to make
294 /// # // the tests actually `#![no_std]`...
295 /// # use std as alloc;
296 /// use maitake_sync::RwLock;
297 /// use alloc::sync::Arc;
298 ///
299 /// let lock = Arc::new(RwLock::new(1));
300 ///
301 /// let read_guard = lock
302 /// .try_read()
303 /// .expect("lock is unlocked, so read access should be acquired");
304 /// assert_eq!(*read_guard, 1);
305 ///
306 /// // because a read guard is held, we cannot acquire the write lock, so
307 /// // this will return `None`.
308 /// assert!(lock.try_write_owned().is_none());
309 /// # }
310 /// ```
311 ///
312 /// [guard]: OwnedRwLockWriteGuard
313 pub fn try_write_owned(self: &Arc<Self>) -> Option<OwnedRwLockWriteGuard<T>> {
314 self.try_write()
315 .map(|guard| OwnedRwLockWriteGuard::from_borrowed(self.clone(), guard))
316 }
317}
318
319// === impl OwnedRwLockReadGuard ===
320
321impl<T: ?Sized> OwnedRwLockReadGuard<T> {
322 fn from_borrowed(
323 lock: Arc<RwLock<T>>,
324 RwLockReadGuard { data, _permit }: RwLockReadGuard<'_, T>,
325 ) -> Self {
326 // forget the semaphore permit, as it has a lifetime tied to the
327 // borrowed semaphore. we'll manually release the permit in
328 // `OwnedRwLockReadGuard`'s `Drop` impl.
329 _permit.forget();
330 Self {
331 _lock: AddPermits(lock),
332 data,
333 }
334 }
335}
336
337impl<T: ?Sized> Deref for OwnedRwLockReadGuard<T> {
338 type Target = T;
339
340 #[inline]
341 fn deref(&self) -> &Self::Target {
342 unsafe {
343 // safety: we are holding the semaphore permit that ensures the lock
344 // cannot be accessed mutably.
345 self.data.deref()
346 }
347 }
348}
349
350impl<T: ?Sized + fmt::Debug> fmt::Debug for OwnedRwLockReadGuard<T> {
351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352 self.deref().fmt(f)
353 }
354}
355
356// Safety: A read guard can be shared or sent between threads as long as `T` is
357// `Sync`. It can implement `Send` even if `T` does not implement `Send`, as
358// long as `T` is `Sync`, because the read guard only permits borrowing the `T`.
359unsafe impl<T> Send for OwnedRwLockReadGuard<T> where T: ?Sized + Sync {}
360unsafe impl<T> Sync for OwnedRwLockReadGuard<T> where T: ?Sized + Send + Sync {}
361
362// === impl OwnedRwLockWriteGuard ===
363
364impl<T: ?Sized> OwnedRwLockWriteGuard<T> {
365 fn from_borrowed(
366 lock: Arc<RwLock<T>>,
367 RwLockWriteGuard { data, _permit }: RwLockWriteGuard<'_, T>,
368 ) -> Self {
369 // forget the semaphore permit, as it has a lifetime tied to the
370 // borrowed semaphore. we'll manually release the permit in
371 // `OwnedRwLockWriteGuard`'s `Drop` impl.
372 _permit.forget();
373 Self {
374 _lock: AddPermits(lock),
375 data,
376 }
377 }
378}
379
380impl<T: ?Sized> Deref for OwnedRwLockWriteGuard<T> {
381 type Target = T;
382
383 #[inline]
384 fn deref(&self) -> &Self::Target {
385 unsafe {
386 // safety: we are holding all the semaphore permits, so the data
387 // inside the lock cannot be accessed by another thread.
388 self.data.deref()
389 }
390 }
391}
392
393impl<T: ?Sized> DerefMut for OwnedRwLockWriteGuard<T> {
394 #[inline]
395 fn deref_mut(&mut self) -> &mut Self::Target {
396 unsafe {
397 // safety: we are holding all the semaphore permits, so the data
398 // inside the lock cannot be accessed by another thread.
399 self.data.deref()
400 }
401 }
402}
403
404impl<T: ?Sized + fmt::Debug> fmt::Debug for OwnedRwLockWriteGuard<T> {
405 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406 self.deref().fmt(f)
407 }
408}
409
410// Safety: Unlike the read guard, `T` must be both `Send` and `Sync` for the
411// write guard to be `Send`, because the mutable access provided by the write
412// guard can be used to `mem::replace` or `mem::take` the value, transferring
413// ownership of it across threads.
414unsafe impl<T> Send for OwnedRwLockWriteGuard<T> where T: ?Sized + Send + Sync {}
415unsafe impl<T> Sync for OwnedRwLockWriteGuard<T> where T: ?Sized + Send + Sync {}
416
417// === impl AddPermits ===
418
419impl<const PERMITS: usize, T: ?Sized> Drop for AddPermits<PERMITS, T> {
420 fn drop(&mut self) {
421 self.0.sem.add_permits(PERMITS);
422 }
423}