summaryrefslogtreecommitdiffstats
path: root/library/std/src/sync/rwlock.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /library/std/src/sync/rwlock.rs
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/std/src/sync/rwlock.rs')
-rw-r--r--library/std/src/sync/rwlock.rs615
1 files changed, 615 insertions, 0 deletions
diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs
new file mode 100644
index 000000000..6e4a2cfc8
--- /dev/null
+++ b/library/std/src/sync/rwlock.rs
@@ -0,0 +1,615 @@
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
+use crate::cell::UnsafeCell;
+use crate::fmt;
+use crate::ops::{Deref, DerefMut};
+use crate::ptr::NonNull;
+use crate::sync::{poison, LockResult, TryLockError, TryLockResult};
+use crate::sys_common::rwlock as sys;
+
+/// A reader-writer lock
+///
+/// This type of lock allows a number of readers or at most one writer at any
+/// point in time. The write portion of this lock typically allows modification
+/// of the underlying data (exclusive access) and the read portion of this lock
+/// typically allows for read-only access (shared access).
+///
+/// In comparison, a [`Mutex`] does not distinguish between readers or writers
+/// that acquire the lock, therefore blocking any threads waiting for the lock to
+/// become available. An `RwLock` will allow any number of readers to acquire the
+/// lock as long as a writer is not holding the lock.
+///
+/// The priority policy of the lock is dependent on the underlying operating
+/// system's implementation, and this type does not guarantee that any
+/// particular policy will be used. In particular, a writer which is waiting to
+/// acquire the lock in `write` might or might not block concurrent calls to
+/// `read`, e.g.:
+///
+/// <details><summary>Potential deadlock example</summary>
+///
+/// ```text
+/// // Thread 1 | // Thread 2
+/// let _rg = lock.read(); |
+/// | // will block
+/// | let _wg = lock.write();
+/// // may deadlock |
+/// let _rg = lock.read(); |
+/// ```
+/// </details>
+///
+/// The type parameter `T` represents the data that this lock protects. It is
+/// required that `T` satisfies [`Send`] to be shared across threads and
+/// [`Sync`] to allow concurrent access through readers. The RAII guards
+/// returned from the locking methods implement [`Deref`] (and [`DerefMut`]
+/// for the `write` methods) to allow access to the content of the lock.
+///
+/// # Poisoning
+///
+/// An `RwLock`, like [`Mutex`], will become poisoned on a panic. Note, however,
+/// that an `RwLock` may only be poisoned if a panic occurs while it is locked
+/// exclusively (write mode). If a panic occurs in any reader, then the lock
+/// will not be poisoned.
+///
+/// # Examples
+///
+/// ```
+/// use std::sync::RwLock;
+///
+/// let lock = RwLock::new(5);
+///
+/// // many reader locks can be held at once
+/// {
+/// let r1 = lock.read().unwrap();
+/// let r2 = lock.read().unwrap();
+/// assert_eq!(*r1, 5);
+/// assert_eq!(*r2, 5);
+/// } // read locks are dropped at this point
+///
+/// // only one write lock may be held, however
+/// {
+/// let mut w = lock.write().unwrap();
+/// *w += 1;
+/// assert_eq!(*w, 6);
+/// } // write lock is dropped here
+/// ```
+///
+/// [`Mutex`]: super::Mutex
+#[stable(feature = "rust1", since = "1.0.0")]
+pub struct RwLock<T: ?Sized> {
+ inner: sys::MovableRwLock,
+ poison: poison::Flag,
+ data: UnsafeCell<T>,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+unsafe impl<T: ?Sized + Send> Send for RwLock<T> {}
+#[stable(feature = "rust1", since = "1.0.0")]
+unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {}
+
+/// RAII structure used to release the shared read access of a lock when
+/// dropped.
+///
+/// This structure is created by the [`read`] and [`try_read`] methods on
+/// [`RwLock`].
+///
+/// [`read`]: RwLock::read
+/// [`try_read`]: RwLock::try_read
+#[must_use = "if unused the RwLock will immediately unlock"]
+#[must_not_suspend = "holding a RwLockReadGuard across suspend \
+ points can cause deadlocks, delays, \
+ and cause Futures to not implement `Send`"]
+#[stable(feature = "rust1", since = "1.0.0")]
+#[clippy::has_significant_drop]
+pub struct RwLockReadGuard<'a, T: ?Sized + 'a> {
+ // NB: we use a pointer instead of `&'a T` to avoid `noalias` violations, because a
+ // `Ref` argument doesn't hold immutability for its whole scope, only until it drops.
+ // `NonNull` is also covariant over `T`, just like we would have with `&T`. `NonNull`
+ // is preferable over `const* T` to allow for niche optimization.
+ data: NonNull<T>,
+ inner_lock: &'a sys::MovableRwLock,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ?Sized> !Send for RwLockReadGuard<'_, T> {}
+
+#[stable(feature = "rwlock_guard_sync", since = "1.23.0")]
+unsafe impl<T: ?Sized + Sync> Sync for RwLockReadGuard<'_, T> {}
+
+/// RAII structure used to release the exclusive write access of a lock when
+/// dropped.
+///
+/// This structure is created by the [`write`] and [`try_write`] methods
+/// on [`RwLock`].
+///
+/// [`write`]: RwLock::write
+/// [`try_write`]: RwLock::try_write
+#[must_use = "if unused the RwLock will immediately unlock"]
+#[must_not_suspend = "holding a RwLockWriteGuard across suspend \
+ points can cause deadlocks, delays, \
+ and cause Future's to not implement `Send`"]
+#[stable(feature = "rust1", since = "1.0.0")]
+#[clippy::has_significant_drop]
+pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> {
+ lock: &'a RwLock<T>,
+ poison: poison::Guard,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ?Sized> !Send for RwLockWriteGuard<'_, T> {}
+
+#[stable(feature = "rwlock_guard_sync", since = "1.23.0")]
+unsafe impl<T: ?Sized + Sync> Sync for RwLockWriteGuard<'_, T> {}
+
+impl<T> RwLock<T> {
+ /// Creates a new instance of an `RwLock<T>` which is unlocked.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::sync::RwLock;
+ ///
+ /// let lock = RwLock::new(5);
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
+ #[inline]
+ pub const fn new(t: T) -> RwLock<T> {
+ RwLock {
+ inner: sys::MovableRwLock::new(),
+ poison: poison::Flag::new(),
+ data: UnsafeCell::new(t),
+ }
+ }
+}
+
+impl<T: ?Sized> RwLock<T> {
+ /// Locks this rwlock with shared read access, blocking the current thread
+ /// until it can be acquired.
+ ///
+ /// The calling thread will be blocked until there are no more writers which
+ /// hold the lock. There may be other readers currently inside the lock when
+ /// this method returns. This method does not provide any guarantees with
+ /// respect to the ordering of whether contentious readers or writers will
+ /// acquire the lock first.
+ ///
+ /// Returns an RAII guard which will release this thread's shared access
+ /// once it is dropped.
+ ///
+ /// # Errors
+ ///
+ /// This function will return an error if the RwLock is poisoned. An RwLock
+ /// is poisoned whenever a writer panics while holding an exclusive lock.
+ /// The failure will occur immediately after the lock has been acquired.
+ ///
+ /// # Panics
+ ///
+ /// This function might panic when called if the lock is already held by the current thread.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::sync::{Arc, RwLock};
+ /// use std::thread;
+ ///
+ /// let lock = Arc::new(RwLock::new(1));
+ /// let c_lock = Arc::clone(&lock);
+ ///
+ /// let n = lock.read().unwrap();
+ /// assert_eq!(*n, 1);
+ ///
+ /// thread::spawn(move || {
+ /// let r = c_lock.read();
+ /// assert!(r.is_ok());
+ /// }).join().unwrap();
+ /// ```
+ #[inline]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn read(&self) -> LockResult<RwLockReadGuard<'_, T>> {
+ unsafe {
+ self.inner.read();
+ RwLockReadGuard::new(self)
+ }
+ }
+
+ /// Attempts to acquire this rwlock with shared read access.
+ ///
+ /// If the access could not be granted at this time, then `Err` is returned.
+ /// Otherwise, an RAII guard is returned which will release the shared access
+ /// when it is dropped.
+ ///
+ /// This function does not block.
+ ///
+ /// This function does not provide any guarantees with respect to the ordering
+ /// of whether contentious readers or writers will acquire the lock first.
+ ///
+ /// # Errors
+ ///
+ /// This function will return the [`Poisoned`] error if the RwLock is poisoned.
+ /// An RwLock is poisoned whenever a writer panics while holding an exclusive
+ /// lock. `Poisoned` will only be returned if the lock would have otherwise been
+ /// acquired.
+ ///
+ /// This function will return the [`WouldBlock`] error if the RwLock could not
+ /// be acquired because it was already locked exclusively.
+ ///
+ /// [`Poisoned`]: TryLockError::Poisoned
+ /// [`WouldBlock`]: TryLockError::WouldBlock
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::sync::RwLock;
+ ///
+ /// let lock = RwLock::new(1);
+ ///
+ /// match lock.try_read() {
+ /// Ok(n) => assert_eq!(*n, 1),
+ /// Err(_) => unreachable!(),
+ /// };
+ /// ```
+ #[inline]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn try_read(&self) -> TryLockResult<RwLockReadGuard<'_, T>> {
+ unsafe {
+ if self.inner.try_read() {
+ Ok(RwLockReadGuard::new(self)?)
+ } else {
+ Err(TryLockError::WouldBlock)
+ }
+ }
+ }
+
+ /// Locks this rwlock with exclusive write access, blocking the current
+ /// thread until it can be acquired.
+ ///
+ /// This function will not return while other writers or other readers
+ /// currently have access to the lock.
+ ///
+ /// Returns an RAII guard which will drop the write access of this rwlock
+ /// when dropped.
+ ///
+ /// # Errors
+ ///
+ /// This function will return an error if the RwLock is poisoned. An RwLock
+ /// is poisoned whenever a writer panics while holding an exclusive lock.
+ /// An error will be returned when the lock is acquired.
+ ///
+ /// # Panics
+ ///
+ /// This function might panic when called if the lock is already held by the current thread.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::sync::RwLock;
+ ///
+ /// let lock = RwLock::new(1);
+ ///
+ /// let mut n = lock.write().unwrap();
+ /// *n = 2;
+ ///
+ /// assert!(lock.try_read().is_err());
+ /// ```
+ #[inline]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn write(&self) -> LockResult<RwLockWriteGuard<'_, T>> {
+ unsafe {
+ self.inner.write();
+ RwLockWriteGuard::new(self)
+ }
+ }
+
+ /// Attempts to lock this rwlock with exclusive write access.
+ ///
+ /// If the lock could not be acquired at this time, then `Err` is returned.
+ /// Otherwise, an RAII guard is returned which will release the lock when
+ /// it is dropped.
+ ///
+ /// This function does not block.
+ ///
+ /// This function does not provide any guarantees with respect to the ordering
+ /// of whether contentious readers or writers will acquire the lock first.
+ ///
+ /// # Errors
+ ///
+ /// This function will return the [`Poisoned`] error if the RwLock is
+ /// poisoned. An RwLock is poisoned whenever a writer panics while holding
+ /// an exclusive lock. `Poisoned` will only be returned if the lock would have
+ /// otherwise been acquired.
+ ///
+ /// This function will return the [`WouldBlock`] error if the RwLock could not
+ /// be acquired because it was already locked exclusively.
+ ///
+ /// [`Poisoned`]: TryLockError::Poisoned
+ /// [`WouldBlock`]: TryLockError::WouldBlock
+ ///
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::sync::RwLock;
+ ///
+ /// let lock = RwLock::new(1);
+ ///
+ /// let n = lock.read().unwrap();
+ /// assert_eq!(*n, 1);
+ ///
+ /// assert!(lock.try_write().is_err());
+ /// ```
+ #[inline]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn try_write(&self) -> TryLockResult<RwLockWriteGuard<'_, T>> {
+ unsafe {
+ if self.inner.try_write() {
+ Ok(RwLockWriteGuard::new(self)?)
+ } else {
+ Err(TryLockError::WouldBlock)
+ }
+ }
+ }
+
+ /// Determines whether the lock is poisoned.
+ ///
+ /// If another thread is active, the lock can still become poisoned at any
+ /// time. You should not trust a `false` value for program correctness
+ /// without additional synchronization.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::sync::{Arc, RwLock};
+ /// use std::thread;
+ ///
+ /// let lock = Arc::new(RwLock::new(0));
+ /// let c_lock = Arc::clone(&lock);
+ ///
+ /// let _ = thread::spawn(move || {
+ /// let _lock = c_lock.write().unwrap();
+ /// panic!(); // the lock gets poisoned
+ /// }).join();
+ /// assert_eq!(lock.is_poisoned(), true);
+ /// ```
+ #[inline]
+ #[stable(feature = "sync_poison", since = "1.2.0")]
+ pub fn is_poisoned(&self) -> bool {
+ self.poison.get()
+ }
+
+ /// Clear the poisoned state from a lock
+ ///
+ /// If the lock is poisoned, it will remain poisoned until this function is called. This allows
+ /// recovering from a poisoned state and marking that it has recovered. For example, if the
+ /// value is overwritten by a known-good value, then the mutex can be marked as un-poisoned. Or
+ /// possibly, the value could be inspected to determine if it is in a consistent state, and if
+ /// so the poison is removed.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(mutex_unpoison)]
+ ///
+ /// use std::sync::{Arc, RwLock};
+ /// use std::thread;
+ ///
+ /// let lock = Arc::new(RwLock::new(0));
+ /// let c_lock = Arc::clone(&lock);
+ ///
+ /// let _ = thread::spawn(move || {
+ /// let _lock = c_lock.write().unwrap();
+ /// panic!(); // the mutex gets poisoned
+ /// }).join();
+ ///
+ /// assert_eq!(lock.is_poisoned(), true);
+ /// let guard = lock.write().unwrap_or_else(|mut e| {
+ /// **e.get_mut() = 1;
+ /// lock.clear_poison();
+ /// e.into_inner()
+ /// });
+ /// assert_eq!(lock.is_poisoned(), false);
+ /// assert_eq!(*guard, 1);
+ /// ```
+ #[inline]
+ #[unstable(feature = "mutex_unpoison", issue = "96469")]
+ pub fn clear_poison(&self) {
+ self.poison.clear();
+ }
+
+ /// Consumes this `RwLock`, returning the underlying data.
+ ///
+ /// # Errors
+ ///
+ /// This function will return an error if the RwLock is poisoned. An RwLock
+ /// is poisoned whenever a writer panics while holding an exclusive lock. An
+ /// error will only be returned if the lock would have otherwise been
+ /// acquired.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::sync::RwLock;
+ ///
+ /// let lock = RwLock::new(String::new());
+ /// {
+ /// let mut s = lock.write().unwrap();
+ /// *s = "modified".to_owned();
+ /// }
+ /// assert_eq!(lock.into_inner().unwrap(), "modified");
+ /// ```
+ #[stable(feature = "rwlock_into_inner", since = "1.6.0")]
+ pub fn into_inner(self) -> LockResult<T>
+ where
+ T: Sized,
+ {
+ let data = self.data.into_inner();
+ poison::map_result(self.poison.borrow(), |()| data)
+ }
+
+ /// Returns a mutable reference to the underlying data.
+ ///
+ /// Since this call borrows the `RwLock` mutably, no actual locking needs to
+ /// take place -- the mutable borrow statically guarantees no locks exist.
+ ///
+ /// # Errors
+ ///
+ /// This function will return an error if the RwLock is poisoned. An RwLock
+ /// is poisoned whenever a writer panics while holding an exclusive lock. An
+ /// error will only be returned if the lock would have otherwise been
+ /// acquired.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::sync::RwLock;
+ ///
+ /// let mut lock = RwLock::new(0);
+ /// *lock.get_mut().unwrap() = 10;
+ /// assert_eq!(*lock.read().unwrap(), 10);
+ /// ```
+ #[stable(feature = "rwlock_get_mut", since = "1.6.0")]
+ pub fn get_mut(&mut self) -> LockResult<&mut T> {
+ let data = self.data.get_mut();
+ poison::map_result(self.poison.borrow(), |()| data)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLock<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut d = f.debug_struct("RwLock");
+ match self.try_read() {
+ Ok(guard) => {
+ d.field("data", &&*guard);
+ }
+ Err(TryLockError::Poisoned(err)) => {
+ d.field("data", &&**err.get_ref());
+ }
+ Err(TryLockError::WouldBlock) => {
+ struct LockedPlaceholder;
+ impl fmt::Debug for LockedPlaceholder {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("<locked>")
+ }
+ }
+ d.field("data", &LockedPlaceholder);
+ }
+ }
+ d.field("poisoned", &self.poison.get());
+ d.finish_non_exhaustive()
+ }
+}
+
+#[stable(feature = "rw_lock_default", since = "1.10.0")]
+impl<T: Default> Default for RwLock<T> {
+ /// Creates a new `RwLock<T>`, with the `Default` value for T.
+ fn default() -> RwLock<T> {
+ RwLock::new(Default::default())
+ }
+}
+
+#[stable(feature = "rw_lock_from", since = "1.24.0")]
+impl<T> From<T> for RwLock<T> {
+ /// Creates a new instance of an `RwLock<T>` which is unlocked.
+ /// This is equivalent to [`RwLock::new`].
+ fn from(t: T) -> Self {
+ RwLock::new(t)
+ }
+}
+
+impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> {
+ /// Create a new instance of `RwLockReadGuard<T>` from a `RwLock<T>`.
+ // SAFETY: if and only if `lock.inner.read()` (or `lock.inner.try_read()`) has been
+ // successfully called from the same thread before instantiating this object.
+ unsafe fn new(lock: &'rwlock RwLock<T>) -> LockResult<RwLockReadGuard<'rwlock, T>> {
+ poison::map_result(lock.poison.borrow(), |()| RwLockReadGuard {
+ data: NonNull::new_unchecked(lock.data.get()),
+ inner_lock: &lock.inner,
+ })
+ }
+}
+
+impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> {
+ /// Create a new instance of `RwLockWriteGuard<T>` from a `RwLock<T>`.
+ // SAFETY: if and only if `lock.inner.write()` (or `lock.inner.try_write()`) has been
+ // successfully called from the same thread before instantiating this object.
+ unsafe fn new(lock: &'rwlock RwLock<T>) -> LockResult<RwLockWriteGuard<'rwlock, T>> {
+ poison::map_result(lock.poison.guard(), |guard| RwLockWriteGuard { lock, poison: guard })
+ }
+}
+
+#[stable(feature = "std_debug", since = "1.16.0")]
+impl<T: fmt::Debug> fmt::Debug for RwLockReadGuard<'_, T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ (**self).fmt(f)
+ }
+}
+
+#[stable(feature = "std_guard_impls", since = "1.20.0")]
+impl<T: ?Sized + fmt::Display> fmt::Display for RwLockReadGuard<'_, T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ (**self).fmt(f)
+ }
+}
+
+#[stable(feature = "std_debug", since = "1.16.0")]
+impl<T: fmt::Debug> fmt::Debug for RwLockWriteGuard<'_, T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ (**self).fmt(f)
+ }
+}
+
+#[stable(feature = "std_guard_impls", since = "1.20.0")]
+impl<T: ?Sized + fmt::Display> fmt::Display for RwLockWriteGuard<'_, T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ (**self).fmt(f)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ?Sized> Deref for RwLockReadGuard<'_, T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ // SAFETY: the conditions of `RwLockGuard::new` were satisfied when created.
+ unsafe { self.data.as_ref() }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ?Sized> Deref for RwLockWriteGuard<'_, T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created.
+ unsafe { &*self.lock.data.get() }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ?Sized> DerefMut for RwLockWriteGuard<'_, T> {
+ fn deref_mut(&mut self) -> &mut T {
+ // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created.
+ unsafe { &mut *self.lock.data.get() }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ?Sized> Drop for RwLockReadGuard<'_, T> {
+ fn drop(&mut self) {
+ // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when created.
+ unsafe {
+ self.inner_lock.read_unlock();
+ }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ?Sized> Drop for RwLockWriteGuard<'_, T> {
+ fn drop(&mut self) {
+ self.lock.poison.done(&self.poison);
+ // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created.
+ unsafe {
+ self.lock.inner.write_unlock();
+ }
+ }
+}