diff --git a/library/std/src/sync/nonpoison/mutex.rs b/library/std/src/sync/nonpoison/mutex.rs index ed3f8cfed821a..032ff59a4302d 100644 --- a/library/std/src/sync/nonpoison/mutex.rs +++ b/library/std/src/sync/nonpoison/mutex.rs @@ -98,6 +98,9 @@ unsafe impl Sync for Mutex {} #[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonMutexGuard")] pub struct MutexGuard<'a, T: ?Sized + 'a> { lock: &'a Mutex, + /// The unlocked state is used to prevent double unlocking of guards upon panicking in + /// unlocked scopes. + unlocked: bool, } /// A [`MutexGuard`] is not `Send` to maximize platform portability. @@ -447,7 +450,7 @@ impl fmt::Debug for Mutex { impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> { unsafe fn new(lock: &'mutex Mutex) -> MutexGuard<'mutex, T> { - return MutexGuard { lock }; + MutexGuard { lock, unlocked: false } } } @@ -471,8 +474,10 @@ impl DerefMut for MutexGuard<'_, T> { impl Drop for MutexGuard<'_, T> { #[inline] fn drop(&mut self) { - unsafe { - self.lock.inner.unlock(); + if !self.unlocked { + unsafe { + self.lock.inner.unlock(); + } } } } @@ -496,6 +501,48 @@ pub(super) fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::M &guard.lock.inner } +impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> { + /// Unlocks the [`MutexGuard`] for the scope of `func` and acquires it again after. + /// Panics won't lock the guard again. + /// + /// # Examples + /// + /// ``` + /// #[feature(unlockable_guards)] + /// + /// use std::sync::nonpoison::Mutex; + /// use std::sync::nonpoison::MutexGuard; + /// use std::sync::nonpoison::TryLockResult; + /// + /// let mutex = Mutex::new(1usize); + /// let mut guard = mutex.lock(); + /// + /// // guard is locked and can be used here + /// *guard = 5; + /// + /// MutexGuard::unlocked(&mut guard, || { + /// // guard is locked and can be acquired potentially from another thread + /// assert!(matches!(mutex.try_lock(), TryLockResult::Ok(_))); + /// }); + /// + /// // guard is locked again + /// assert_eq!(*guard, 5); + /// ``` + #[unstable(feature = "unlockable_guards", issue = "148568")] + pub fn unlocked(self: &mut Self, func: F) -> () + where + F: FnOnce() -> (), + { + self.unlocked = true; + unsafe { self.lock.inner.unlock() }; + + func(); + + self.lock.inner.lock(); + self.unlocked = false; + } +} + impl<'a, T: ?Sized> MutexGuard<'a, T> { /// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g. /// an enum variant. diff --git a/library/std/src/sync/nonpoison/rwlock.rs b/library/std/src/sync/nonpoison/rwlock.rs index dc5d9479ba5a9..eb013354f67ce 100644 --- a/library/std/src/sync/nonpoison/rwlock.rs +++ b/library/std/src/sync/nonpoison/rwlock.rs @@ -81,6 +81,9 @@ pub struct RwLockReadGuard<'rwlock, T: ?Sized + 'rwlock> { data: NonNull, /// A reference to the internal [`sys::RwLock`] that we have read-locked. inner_lock: &'rwlock sys::RwLock, + /// The unlocked state is used to prevent double unlocking of guards upon panicking in + /// unlocked scopes. + unlocked: bool, } #[unstable(feature = "nonpoison_rwlock", issue = "134645")] @@ -107,6 +110,9 @@ unsafe impl Sync for RwLockReadGuard<'_, T> {} pub struct RwLockWriteGuard<'rwlock, T: ?Sized + 'rwlock> { /// A reference to the [`RwLock`] that we have write-locked. lock: &'rwlock RwLock, + /// The unlocked state is used to prevent double unlocking of guards upon panicking in + /// unlocked scopes. + unlocked: bool, } #[unstable(feature = "nonpoison_rwlock", issue = "134645")] @@ -607,6 +613,7 @@ impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { RwLockReadGuard { data: unsafe { NonNull::new_unchecked(lock.data.get()) }, inner_lock: &lock.inner, + unlocked: false, } } @@ -684,7 +691,7 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { /// `lock.inner.write()`, `lock.inner.try_write()`, or `lock.inner.try_upgrade` before /// instantiating this object. unsafe fn new(lock: &'rwlock RwLock) -> RwLockWriteGuard<'rwlock, T> { - RwLockWriteGuard { lock } + RwLockWriteGuard { lock, unlocked: false } } /// Downgrades a write-locked `RwLockWriteGuard` into a read-locked [`RwLockReadGuard`]. @@ -976,9 +983,11 @@ impl<'rwlock, T: ?Sized> MappedRwLockWriteGuard<'rwlock, T> { #[unstable(feature = "nonpoison_rwlock", issue = "134645")] impl Drop for RwLockReadGuard<'_, T> { fn drop(&mut self) { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when created. - unsafe { - self.inner_lock.read_unlock(); + if !self.unlocked { + // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when created. + unsafe { + self.inner_lock.read_unlock(); + } } } } @@ -986,9 +995,11 @@ impl Drop for RwLockReadGuard<'_, T> { #[unstable(feature = "nonpoison_rwlock", issue = "134645")] impl Drop for RwLockWriteGuard<'_, T> { fn drop(&mut self) { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. - unsafe { - self.lock.inner.write_unlock(); + if !self.unlocked { + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. + unsafe { + self.lock.inner.write_unlock(); + } } } } @@ -1138,3 +1149,87 @@ impl fmt::Display for MappedRwLockWriteGuard<'_, T> { (**self).fmt(f) } } + +impl<'rw_lock, T: ?Sized> RwLockReadGuard<'rw_lock, T> { + /// Unlocks the [`RwLockReadGuard`] for the scope of `func` and acquires it again after. + /// Panics won't lock the guard again. + /// + /// # Examples + /// + /// ``` + /// #[feature(unlockable_guards)] + /// + /// use std::sync::nonpoison::RwLock; + /// use std::sync::nonpoison::RwLockReadGuard; + /// use std::sync::nonpoison::TryLockResult; + /// + /// let rw_lock = RwLock::new(1usize); + /// let mut read_guard = rw_lock.read(); + /// + /// // guard is locked and can be used here + /// assert_eq!(*read_guard, 1); + /// + /// RwLockReadGuard::unlocked(&mut read_guard, || { + /// // guard is locked and can be acquired potentially from another thread + /// assert!(matches!(rw_lock.try_write(), TryLockResult::Ok(_))); + /// }); + /// + /// // guard is locked again + /// assert_eq!(*read_guard, 1); + /// ``` + #[unstable(feature = "unlockable_guards", issue = "148568")] + pub fn unlocked(self: &mut Self, func: F) -> () + where + F: FnOnce() -> (), + { + self.unlocked = true; + unsafe { self.inner_lock.read_unlock() }; + + func(); + + self.inner_lock.read(); + self.unlocked = false; + } +} + +impl<'rw_lock, T: ?Sized> RwLockWriteGuard<'rw_lock, T> { + /// Unlocks the [`RwLockWriteGuard`] for the scope of `func` and acquires it again after. + /// Panics won't lock the guard again. + /// + /// # Examples + /// + /// ``` + /// #[feature(unlockable_guards)] + /// + /// use std::sync::nonpoison::RwLock; + /// use std::sync::nonpoison::RwLockWriteGuard; + /// use std::sync::nonpoison::TryLockResult; + /// + /// let rw_lock = RwLock::new(1usize); + /// let mut write_guard = rw_lock.write(); + /// + /// // guard is locked and can be used here + /// *write_guard = 5; + /// + /// RwLockWriteGuard::unlocked(&mut write_guard, || { + /// // guard is locked and can be acquired potentially from another thread + /// assert!(matches!(rw_lock.try_write(), TryLockResult::Ok(_))); + /// }); + /// + /// // guard is locked again + /// assert_eq!(*write_guard, 5); + /// ``` + #[unstable(feature = "unlockable_guards", issue = "148568")] + pub fn unlocked(self: &mut Self, func: F) -> () + where + F: FnOnce() -> (), + { + self.unlocked = true; + unsafe { self.lock.inner.write_unlock() }; + + func(); + + self.lock.inner.write(); + self.unlocked = false; + } +} diff --git a/library/std/tests/sync/mutex.rs b/library/std/tests/sync/mutex.rs index 75a6bf64607ef..373b37b78ed0d 100644 --- a/library/std/tests/sync/mutex.rs +++ b/library/std/tests/sync/mutex.rs @@ -531,3 +531,21 @@ fn test_mutex_with_mut() { assert_eq!(*mutex.lock(), 5); assert_eq!(result, 10); } + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Nonpoison Tests +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#[test] +fn test_mutex_guard_unlocked() { + let mutex = std::sync::nonpoison::Mutex::new(1usize); + let mut guard = mutex.lock(); + + assert!(matches!(mutex.try_lock(), std::sync::nonpoison::TryLockResult::Err(_))); + + MutexGuard::unlocked(&mut guard, || { + assert!(matches!(mutex.try_lock(), std::sync::nonpoison::TryLockResult::Ok(_))); + }); + + assert!(matches!(mutex.try_lock(), std::sync::nonpoison::TryLockResult::Err(_))); +} diff --git a/library/std/tests/sync/rwlock.rs b/library/std/tests/sync/rwlock.rs index 392c45c8ba05d..c743c95cfd4d3 100644 --- a/library/std/tests/sync/rwlock.rs +++ b/library/std/tests/sync/rwlock.rs @@ -883,3 +883,35 @@ fn test_rwlock_with_mut() { assert_eq!(*rwlock.read(), 5); assert_eq!(result, 10); } + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Non-poison Tests +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#[test] +fn test_read_guard_unlocked() { + let rw_lock = std::sync::nonpoison::RwLock::new(1usize); + let mut read_guard = rw_lock.read(); + + assert!(matches!(rw_lock.try_write(), std::sync::nonpoison::TryLockResult::Err(_))); + + RwLockReadGuard::unlocked(&mut read_guard, || { + assert!(matches!(rw_lock.try_write(), std::sync::nonpoison::TryLockResult::Ok(_))); + }); + + assert!(matches!(rw_lock.try_write(), std::sync::nonpoison::TryLockResult::Err(_))); +} + +#[test] +fn test_write_guard_unlocked() { + let rw_lock = std::sync::nonpoison::RwLock::new(1usize); + let mut write_guard = rw_lock.write(); + + assert!(matches!(rw_lock.try_write(), std::sync::nonpoison::TryLockResult::Err(_))); + + RwLockWriteGuard::unlocked(&mut write_guard, || { + assert!(matches!(rw_lock.try_write(), std::sync::nonpoison::TryLockResult::Ok(_))); + }); + + assert!(matches!(rw_lock.try_write(), std::sync::nonpoison::TryLockResult::Err(_))); +}