mc_sgx_sync/
rwlock.rs

1// Copyright (c) The Rust Foundation
2// Copyright (c) 2023 The MobileCoin Foundation
3
4//! rwlock.rs implementation more or less copied from
5//! [rust source](https://github.com/rust-lang/rust.git) at
6//! [606c3907](https://github.com/rust-lang/rust/commit/606c3907251397a42e23d3e60de31be9d32525d5)
7//!
8//! Differences:
9//! - The imports were changed to work with the `mc-sgx` crates.
10//! - The stable attributes have been removed
11//! - The unstable attributes have been removed
12//! - Removed examples that were not possible in an SGX enclave have been omitted
13//! - Ran `cargo fmt`
14//! - Removed unnecessary unsafe blocks
15
16use crate::sys::locks as sys;
17use crate::{poison, LockResult, TryLockError, TryLockResult};
18use core::cell::UnsafeCell;
19use core::fmt;
20use core::ops::{Deref, DerefMut};
21use core::ptr::NonNull;
22
23/// A reader-writer lock
24///
25/// This type of lock allows a number of readers or at most one writer at any
26/// point in time. The write portion of this lock typically allows modification
27/// of the underlying data (exclusive access) and the read portion of this lock
28/// typically allows for read-only access (shared access).
29///
30/// In comparison, a [`Mutex`] does not distinguish between readers or writers
31/// that acquire the lock, therefore blocking any threads waiting for the lock to
32/// become available. An `RwLock` will allow any number of readers to acquire the
33/// lock as long as a writer is not holding the lock.
34///
35/// The priority policy of the lock is dependent on the underlying operating
36/// system's implementation, and this type does not guarantee that any
37/// particular policy will be used. In particular, a writer which is waiting to
38/// acquire the lock in `write` might or might not block concurrent calls to
39/// `read`, e.g.:
40///
41/// <details><summary>Potential deadlock example</summary>
42///
43/// ```text
44/// // Thread 1             |  // Thread 2
45/// let _rg = lock.read();  |
46///                         |  // will block
47///                         |  let _wg = lock.write();
48/// // may deadlock         |
49/// let _rg = lock.read();  |
50/// ```
51/// </details>
52///
53/// The type parameter `T` represents the data that this lock protects. It is
54/// required that `T` satisfies [`Send`] to be shared across threads and
55/// [`Sync`] to allow concurrent access through readers. The RAII guards
56/// returned from the locking methods implement [`Deref`] (and [`DerefMut`]
57/// for the `write` methods) to allow access to the content of the lock.
58///
59/// # Poisoning
60///
61/// An `RwLock`, like [`Mutex`], will become poisoned on a panic. Note, however,
62/// that an `RwLock` may only be poisoned if a panic occurs while it is locked
63/// exclusively (write mode). If a panic occurs in any reader, then the lock
64/// will not be poisoned.
65///
66/// # Examples
67///
68/// ```
69/// use mc_sgx_sync::RwLock;
70///
71/// let lock = RwLock::new(5);
72///
73/// // many reader locks can be held at once
74/// {
75///     let r1 = lock.read().unwrap();
76///     let r2 = lock.read().unwrap();
77///     assert_eq!(*r1, 5);
78///     assert_eq!(*r2, 5);
79/// } // read locks are dropped at this point
80///
81/// // only one write lock may be held, however
82/// {
83///     let mut w = lock.write().unwrap();
84///     *w += 1;
85///     assert_eq!(*w, 6);
86/// } // write lock is dropped here
87/// ```
88///
89/// [`Mutex`]: super::Mutex
90pub struct RwLock<T: ?Sized> {
91    inner: sys::RwLock,
92    poison: poison::Flag,
93    data: UnsafeCell<T>,
94}
95
96unsafe impl<T: ?Sized + Send> Send for RwLock<T> {}
97unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {}
98
99/// RAII structure used to release the shared read access of a lock when
100/// dropped.
101///
102/// This structure is created by the [`read`] and [`try_read`] methods on
103/// [`RwLock`].
104///
105/// [`read`]: RwLock::read
106/// [`try_read`]: RwLock::try_read
107#[must_use = "if unused the RwLock will immediately unlock"]
108#[clippy::has_significant_drop]
109pub struct RwLockReadGuard<'a, T: ?Sized + 'a> {
110    // NB: we use a pointer instead of `&'a T` to avoid `noalias` violations, because a
111    // `Ref` argument doesn't hold immutability for its whole scope, only until it drops.
112    // `NonNull` is also covariant over `T`, just like we would have with `&T`. `NonNull`
113    // is preferable over `const* T` to allow for niche optimization.
114    data: NonNull<T>,
115    inner_lock: &'a sys::RwLock,
116}
117
118impl<T: ?Sized> !Send for RwLockReadGuard<'_, T> {}
119unsafe impl<T: ?Sized + Sync> Sync for RwLockReadGuard<'_, T> {}
120
121/// RAII structure used to release the exclusive write access of a lock when
122/// dropped.
123///
124/// This structure is created by the [`write`] and [`try_write`] methods
125/// on [`RwLock`].
126///
127/// [`write`]: RwLock::write
128/// [`try_write`]: RwLock::try_write
129#[must_use = "if unused the RwLock will immediately unlock"]
130#[clippy::has_significant_drop]
131pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> {
132    lock: &'a RwLock<T>,
133    poison: poison::Guard,
134}
135
136impl<T: ?Sized> !Send for RwLockWriteGuard<'_, T> {}
137unsafe impl<T: ?Sized + Sync> Sync for RwLockWriteGuard<'_, T> {}
138
139impl<T> RwLock<T> {
140    /// Creates a new instance of an `RwLock<T>` which is unlocked.
141    ///
142    /// # Examples
143    ///
144    /// ```
145    /// use mc_sgx_sync::RwLock;
146    ///
147    /// let lock = RwLock::new(5);
148    /// ```
149    pub const fn new(t: T) -> RwLock<T> {
150        RwLock {
151            inner: sys::RwLock::new(),
152            poison: poison::Flag::new(),
153            data: UnsafeCell::new(t),
154        }
155    }
156}
157
158impl<T: ?Sized> RwLock<T> {
159    /// Locks this `RwLock` with shared read access, blocking the current thread
160    /// until it can be acquired.
161    ///
162    /// The calling thread will be blocked until there are no more writers which
163    /// hold the lock. There may be other readers currently inside the lock when
164    /// this method returns. This method does not provide any guarantees with
165    /// respect to the ordering of whether contentious readers or writers will
166    /// acquire the lock first.
167    ///
168    /// Returns an RAII guard which will release this thread's shared access
169    /// once it is dropped.
170    ///
171    /// # Errors
172    ///
173    /// This function will return an error if the `RwLock` is poisoned. An
174    /// `RwLock` is poisoned whenever a writer panics while holding an exclusive
175    /// lock. The failure will occur immediately after the lock has been
176    /// acquired.
177    ///
178    /// # Panics
179    ///
180    /// This function might panic when called if the lock is already held by the current thread.
181    ///
182    /// # Examples
183    ///
184    /// ```
185    /// use mc_sgx_sync::RwLock;
186    ///
187    /// let lock = RwLock::new(1);
188    ///
189    /// let n = lock.read().unwrap();
190    /// assert_eq!(*n, 1);
191    /// ```
192    pub fn read(&self) -> LockResult<RwLockReadGuard<'_, T>> {
193        unsafe {
194            self.inner.read();
195            RwLockReadGuard::new(self)
196        }
197    }
198
199    /// Attempts to acquire this `RwLock` with shared read access.
200    ///
201    /// If the access could not be granted at this time, then `Err` is returned.
202    /// Otherwise, an RAII guard is returned which will release the shared access
203    /// when it is dropped.
204    ///
205    /// This function does not block.
206    ///
207    /// This function does not provide any guarantees with respect to the ordering
208    /// of whether contentious readers or writers will acquire the lock first.
209    ///
210    /// # Errors
211    ///
212    /// This function will return the [`Poisoned`] error if the `RwLock` is
213    /// poisoned. An `RwLock` is poisoned whenever a writer panics while holding
214    /// an exclusive lock. `Poisoned` will only be returned if the lock would
215    /// have otherwise been acquired.
216    ///
217    /// This function will return the [`WouldBlock`] error if the `RwLock` could
218    /// not be acquired because it was already locked exclusively.
219    ///
220    /// [`Poisoned`]: TryLockError::Poisoned
221    /// [`WouldBlock`]: TryLockError::WouldBlock
222    ///
223    /// # Examples
224    ///
225    /// ```
226    /// use mc_sgx_sync::RwLock;
227    ///
228    /// let lock = RwLock::new(1);
229    ///
230    /// match lock.try_read() {
231    ///     Ok(n) => assert_eq!(*n, 1),
232    ///     Err(_) => unreachable!(),
233    /// };
234    /// ```
235    pub fn try_read(&self) -> TryLockResult<RwLockReadGuard<'_, T>> {
236        unsafe {
237            if self.inner.try_read() {
238                Ok(RwLockReadGuard::new(self)?)
239            } else {
240                Err(TryLockError::WouldBlock)
241            }
242        }
243    }
244
245    /// Locks this `RwLock` with exclusive write access, blocking the current
246    /// thread until it can be acquired.
247    ///
248    /// This function will not return while other writers or other readers
249    /// currently have access to the lock.
250    ///
251    /// Returns an RAII guard which will drop the write access of this `RwLock`
252    /// when dropped.
253    ///
254    /// # Errors
255    ///
256    /// This function will return an error if the `RwLock` is poisoned. An
257    /// `RwLock` is poisoned whenever a writer panics while holding an exclusive
258    /// lock. An error will be returned when the lock is acquired.
259    ///
260    /// # Panics
261    ///
262    /// This function might panic when called if the lock is already held by the current thread.
263    ///
264    /// # Examples
265    ///
266    /// ```
267    /// use mc_sgx_sync::RwLock;
268    ///
269    /// let lock = RwLock::new(1);
270    ///
271    /// let mut n = lock.write().unwrap();
272    /// *n = 2;
273    ///
274    /// assert!(lock.try_read().is_err());
275    /// ```
276    pub fn write(&self) -> LockResult<RwLockWriteGuard<'_, T>> {
277        unsafe {
278            self.inner.write();
279            RwLockWriteGuard::new(self)
280        }
281    }
282
283    /// Attempts to lock this `RwLock` with exclusive write access.
284    ///
285    /// If the lock could not be acquired at this time, then `Err` is returned.
286    /// Otherwise, an RAII guard is returned which will release the lock when
287    /// it is dropped.
288    ///
289    /// This function does not block.
290    ///
291    /// This function does not provide any guarantees with respect to the ordering
292    /// of whether contentious readers or writers will acquire the lock first.
293    ///
294    /// # Errors
295    ///
296    /// This function will return the [`Poisoned`] error if the `RwLock` is
297    /// poisoned. An `RwLock` is poisoned whenever a writer panics while holding
298    /// an exclusive lock. `Poisoned` will only be returned if the lock would
299    /// have otherwise been acquired.
300    ///
301    /// This function will return the [`WouldBlock`] error if the `RwLock` could
302    /// not be acquired because it was already locked exclusively.
303    ///
304    /// [`Poisoned`]: TryLockError::Poisoned
305    /// [`WouldBlock`]: TryLockError::WouldBlock
306    ///
307    ///
308    /// # Examples
309    ///
310    /// ```
311    /// use mc_sgx_sync::RwLock;
312    ///
313    /// let lock = RwLock::new(1);
314    ///
315    /// let n = lock.read().unwrap();
316    /// assert_eq!(*n, 1);
317    ///
318    /// assert!(lock.try_write().is_err());
319    /// ```
320    pub fn try_write(&self) -> TryLockResult<RwLockWriteGuard<'_, T>> {
321        unsafe {
322            if self.inner.try_write() {
323                Ok(RwLockWriteGuard::new(self)?)
324            } else {
325                Err(TryLockError::WouldBlock)
326            }
327        }
328    }
329
330    /// Determines whether the lock is poisoned.
331    ///
332    /// If another thread is active, the lock can still become poisoned at any
333    /// time. You should not trust a `false` value for program correctness
334    /// without additional synchronization.
335    pub fn is_poisoned(&self) -> bool {
336        self.poison.get()
337    }
338
339    /// Clear the poisoned state from a lock
340    ///
341    /// If the lock is poisoned, it will remain poisoned until this function is called. This allows
342    /// recovering from a poisoned state and marking that it has recovered. For example, if the
343    /// value is overwritten by a known-good value, then the mutex can be marked as un-poisoned. Or
344    /// possibly, the value could be inspected to determine if it is in a consistent state, and if
345    /// so the poison is removed.
346    pub fn clear_poison(&self) {
347        self.poison.clear();
348    }
349
350    /// Consumes this `RwLock`, returning the underlying data.
351    ///
352    /// # Errors
353    ///
354    /// This function will return an error if the `RwLock` is poisoned. An
355    /// `RwLock` is poisoned whenever a writer panics while holding an exclusive
356    /// lock. An error will only be returned if the lock would have otherwise
357    /// been acquired.
358    ///
359    /// # Examples
360    ///
361    /// ```
362    /// use mc_sgx_sync::RwLock;
363    ///
364    /// let lock = RwLock::new(String::new());
365    /// {
366    ///     let mut s = lock.write().unwrap();
367    ///     *s = "modified".to_owned();
368    /// }
369    /// assert_eq!(lock.into_inner().unwrap(), "modified");
370    /// ```
371    pub fn into_inner(self) -> LockResult<T>
372    where
373        T: Sized,
374    {
375        let data = self.data.into_inner();
376        poison::map_result(self.poison.borrow(), |()| data)
377    }
378
379    /// Returns a mutable reference to the underlying data.
380    ///
381    /// Since this call borrows the `RwLock` mutably, no actual locking needs to
382    /// take place -- the mutable borrow statically guarantees no locks exist.
383    ///
384    /// # Errors
385    ///
386    /// This function will return an error if the `RwLock` is poisoned. An
387    /// `RwLock` is poisoned whenever a writer panics while holding an exclusive
388    /// lock. An error will only be returned if the lock would have otherwise
389    /// been acquired.
390    ///
391    /// # Examples
392    ///
393    /// ```
394    /// use mc_sgx_sync::RwLock;
395    ///
396    /// let mut lock = RwLock::new(0);
397    /// *lock.get_mut().unwrap() = 10;
398    /// assert_eq!(*lock.read().unwrap(), 10);
399    /// ```
400    pub fn get_mut(&mut self) -> LockResult<&mut T> {
401        let data = self.data.get_mut();
402        poison::map_result(self.poison.borrow(), |()| data)
403    }
404}
405
406impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLock<T> {
407    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408        let mut d = f.debug_struct("RwLock");
409        match self.try_read() {
410            Ok(guard) => {
411                d.field("data", &&*guard);
412            }
413            Err(TryLockError::Poisoned(err)) => {
414                d.field("data", &&**err.get_ref());
415            }
416            Err(TryLockError::WouldBlock) => {
417                struct LockedPlaceholder;
418                impl fmt::Debug for LockedPlaceholder {
419                    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
420                        f.write_str("<locked>")
421                    }
422                }
423                d.field("data", &LockedPlaceholder);
424            }
425        }
426        d.field("poisoned", &self.poison.get());
427        d.finish_non_exhaustive()
428    }
429}
430
431impl<T: Default> Default for RwLock<T> {
432    /// Creates a new `RwLock<T>`, with the `Default` value for T.
433    fn default() -> RwLock<T> {
434        RwLock::new(Default::default())
435    }
436}
437
438impl<T> From<T> for RwLock<T> {
439    /// Creates a new instance of an `RwLock<T>` which is unlocked.
440    /// This is equivalent to [`RwLock::new`].
441    fn from(t: T) -> Self {
442        RwLock::new(t)
443    }
444}
445
446impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> {
447    /// Create a new instance of `RwLockReadGuard<T>` from a `RwLock<T>`.
448    // SAFETY: if and only if `lock.inner.read()` (or `lock.inner.try_read()`) has been
449    // successfully called from the same thread before instantiating this object.
450    unsafe fn new(lock: &'rwlock RwLock<T>) -> LockResult<RwLockReadGuard<'rwlock, T>> {
451        poison::map_result(lock.poison.borrow(), |()| RwLockReadGuard {
452            data: NonNull::new_unchecked(lock.data.get()),
453            inner_lock: &lock.inner,
454        })
455    }
456}
457
458impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> {
459    /// Create a new instance of `RwLockWriteGuard<T>` from a `RwLock<T>`.
460    // SAFETY: if and only if `lock.inner.write()` (or `lock.inner.try_write()`) has been
461    // successfully called from the same thread before instantiating this object.
462    unsafe fn new(lock: &'rwlock RwLock<T>) -> LockResult<RwLockWriteGuard<'rwlock, T>> {
463        poison::map_result(lock.poison.guard(), |guard| RwLockWriteGuard {
464            lock,
465            poison: guard,
466        })
467    }
468}
469
470impl<T: fmt::Debug> fmt::Debug for RwLockReadGuard<'_, T> {
471    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
472        (**self).fmt(f)
473    }
474}
475
476impl<T: ?Sized + fmt::Display> fmt::Display for RwLockReadGuard<'_, T> {
477    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
478        (**self).fmt(f)
479    }
480}
481
482impl<T: fmt::Debug> fmt::Debug for RwLockWriteGuard<'_, T> {
483    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
484        (**self).fmt(f)
485    }
486}
487
488impl<T: ?Sized + fmt::Display> fmt::Display for RwLockWriteGuard<'_, T> {
489    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490        (**self).fmt(f)
491    }
492}
493
494impl<T: ?Sized> Deref for RwLockReadGuard<'_, T> {
495    type Target = T;
496
497    fn deref(&self) -> &T {
498        // SAFETY: the conditions of `RwLockGuard::new` were satisfied when created.
499        unsafe { self.data.as_ref() }
500    }
501}
502
503impl<T: ?Sized> Deref for RwLockWriteGuard<'_, T> {
504    type Target = T;
505
506    fn deref(&self) -> &T {
507        // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created.
508        unsafe { &*self.lock.data.get() }
509    }
510}
511
512impl<T: ?Sized> DerefMut for RwLockWriteGuard<'_, T> {
513    fn deref_mut(&mut self) -> &mut T {
514        // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created.
515        unsafe { &mut *self.lock.data.get() }
516    }
517}
518
519impl<T: ?Sized> Drop for RwLockReadGuard<'_, T> {
520    fn drop(&mut self) {
521        self.inner_lock.read_unlock();
522    }
523}
524
525impl<T: ?Sized> Drop for RwLockWriteGuard<'_, T> {
526    fn drop(&mut self) {
527        self.lock.poison.done(&self.poison);
528        self.lock.inner.write_unlock();
529    }
530}