Skip to content

Commit 48bcf09

Browse files
committed
add downgrade method onto RwLockWriteGuard
1 parent 5416aef commit 48bcf09

File tree

1 file changed

+70
-4
lines changed

1 file changed

+70
-4
lines changed

std/src/sync/rwlock.rs

+70-4
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ mod tests;
44
use crate::cell::UnsafeCell;
55
use crate::fmt;
66
use crate::marker::PhantomData;
7-
use crate::mem::ManuallyDrop;
7+
use crate::mem::{ManuallyDrop, forget};
88
use crate::ops::{Deref, DerefMut};
99
use crate::ptr::NonNull;
10-
use crate::sync::{LockResult, TryLockError, TryLockResult, poison};
10+
use crate::sync::{LockResult, PoisonError, TryLockError, TryLockResult, poison};
1111
use crate::sys::sync as sys;
1212

1313
/// A reader-writer lock
@@ -574,8 +574,12 @@ impl<T> From<T> for RwLock<T> {
574574

575575
impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> {
576576
/// Creates a new instance of `RwLockReadGuard<T>` from a `RwLock<T>`.
577-
// SAFETY: if and only if `lock.inner.read()` (or `lock.inner.try_read()`) has been
578-
// successfully called from the same thread before instantiating this object.
577+
///
578+
/// # Safety
579+
///
580+
/// This function is safe if and only if the same thread has successfully and safely called
581+
/// `lock.inner.read()`, `lock.inner.try_read()`, or `lock.inner.downgrade()` before
582+
/// instantiating this object.
579583
unsafe fn new(lock: &'rwlock RwLock<T>) -> LockResult<RwLockReadGuard<'rwlock, T>> {
580584
poison::map_result(lock.poison.borrow(), |()| RwLockReadGuard {
581585
data: unsafe { NonNull::new_unchecked(lock.data.get()) },
@@ -957,6 +961,68 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> {
957961
None => Err(orig),
958962
}
959963
}
964+
965+
/// Downgrades a write-locked `RwLockWriteGuard` into a read-locked [`RwLockReadGuard`].
966+
///
967+
/// This method will atomically change the state of the [`RwLock`] from exclusive mode into
968+
/// shared mode. This means that it is impossible for a writing thread to get in between a
969+
/// thread calling `downgrade` and the same thread reading whatever it wrote while it had the
970+
/// [`RwLock`] in write mode.
971+
///
972+
/// Note that since we have the `RwLockWriteGuard`, we know that the [`RwLock`] is already
973+
/// locked for writing, so this method cannot fail.
974+
///
975+
/// # Example
976+
///
977+
/// ```
978+
/// #![feature(rwlock_downgrade)]
979+
/// use std::sync::{Arc, RwLock, RwLockWriteGuard};
980+
///
981+
/// // The inner value starts as 0.
982+
/// let rw = Arc::new(RwLock::new(0));
983+
///
984+
/// // Put the lock in write mode.
985+
/// let mut main_write_guard = rw.write().unwrap();
986+
///
987+
/// let evil = rw.clone();
988+
/// let handle = std::thread::spawn(move || {
989+
/// // This will not return until the main thread drops the `main_read_guard`.
990+
/// let mut evil_guard = evil.write().unwrap();
991+
///
992+
/// assert_eq!(*evil_guard, 1);
993+
/// *evil_guard = 2;
994+
/// });
995+
///
996+
/// // After spawning the writer thread, set the inner value to 1.
997+
/// *main_write_guard = 1;
998+
///
999+
/// // Atomically downgrade the write guard into a read guard.
1000+
/// let main_read_guard = RwLockWriteGuard::downgrade(main_write_guard);
1001+
///
1002+
/// // Since `downgrade` is atomic, the writer thread cannot have set the inner value to 2.
1003+
/// assert_eq!(*main_read_guard, 1, "`downgrade` was not atomic");
1004+
///
1005+
/// // Clean up everything now
1006+
/// drop(main_read_guard);
1007+
/// handle.join().unwrap();
1008+
///
1009+
/// let final_check = rw.read().unwrap();
1010+
/// assert_eq!(*final_check, 2);
1011+
/// ```
1012+
#[unstable(feature = "rwlock_downgrade", issue = "128203")]
1013+
pub fn downgrade(s: Self) -> RwLockReadGuard<'a, T> {
1014+
let lock = s.lock;
1015+
1016+
// We don't want to call the destructor since that calls `write_unlock`.
1017+
forget(s);
1018+
1019+
// SAFETY: We take ownership of a write guard, so we must already have the `RwLock` in write
1020+
// mode, satisfying the `downgrade` contract.
1021+
unsafe { lock.inner.downgrade() };
1022+
1023+
// SAFETY: We have just successfully called `downgrade`, so we fulfill the safety contract.
1024+
unsafe { RwLockReadGuard::new(lock).unwrap_or_else(PoisonError::into_inner) }
1025+
}
9601026
}
9611027

9621028
impl<'a, T: ?Sized> MappedRwLockWriteGuard<'a, T> {

0 commit comments

Comments
 (0)