diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index f9d9321f5f2d8..c325edda38b4b 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -918,6 +918,24 @@ impl<'a, T: ?Sized> RwLockReadGuard<'a, T> { None => Err(orig), } } + + #[unstable(feature = "rwlock_try_upgrade", issue = "138559")] + pub fn try_upgrade(orig: Self) -> Result, RwLockReadGuard<'a, T>> { + let rwl = &RwLock { + data: UnsafeCell::new(orig.data.as_ptr().read()), + poison: poison::Flag::new(), + inner: *orig.inner_lock, + }; + + // don't call the destructor + forget(orig); + + // SAFETY: We have ownership of the read guard, so it must be in read mode already + match unsafe { rwl.inner.try_upgrade() } { + true => Ok(RwLockWriteGuard::new(rwl).unwrap_or_else(PoisonError::into_inner)), + false => Err(RwLockReadGuard::new(rwl).unwrap_or_else(PoisonError::into_inner)), + } + } } impl<'a, T: ?Sized> MappedRwLockReadGuard<'a, T> { diff --git a/library/std/src/sys/sync/rwlock/futex.rs b/library/std/src/sys/sync/rwlock/futex.rs index 961819cae8d6e..9479525c87a3d 100644 --- a/library/std/src/sys/sync/rwlock/futex.rs +++ b/library/std/src/sys/sync/rwlock/futex.rs @@ -33,6 +33,11 @@ fn is_write_locked(state: Primitive) -> bool { state & MASK == WRITE_LOCKED } +#[inline] +fn is_read_locked(state: Primitive) -> bool { + state & MASK == READ_LOCKED +} + #[inline] fn has_readers_waiting(state: Primitive) -> bool { state & READERS_WAITING != 0 @@ -205,6 +210,19 @@ impl RwLock { } } + /// # Safety + /// + /// The 'RwLock' must be read-locked in order to call this. Calling this function in a loop + /// in at least 2 threads will deadlock. + #[inline] + pub unsafe fn try_upgrade(&self) -> bool { + debg_assert!( + is_read_locked(self.state), + "RwLock must be read locked to call `try_upgrade`" + ); + self.state.compare_exchange(READ_LOCKED, WRITE_LOCKED, Acquire, Relaxed).is_ok() + } + #[cold] fn write_contended(&self) { let mut state = self.spin_write();