@@ -4,10 +4,10 @@ mod tests;
4
4
use crate :: cell:: UnsafeCell ;
5
5
use crate :: fmt;
6
6
use crate :: marker:: PhantomData ;
7
- use crate :: mem:: ManuallyDrop ;
7
+ use crate :: mem:: { ManuallyDrop , forget } ;
8
8
use crate :: ops:: { Deref , DerefMut } ;
9
9
use crate :: ptr:: NonNull ;
10
- use crate :: sync:: { LockResult , TryLockError , TryLockResult , poison} ;
10
+ use crate :: sync:: { LockResult , PoisonError , TryLockError , TryLockResult , poison} ;
11
11
use crate :: sys:: sync as sys;
12
12
13
13
/// A reader-writer lock
@@ -574,8 +574,12 @@ impl<T> From<T> for RwLock<T> {
574
574
575
575
impl < ' rwlock , T : ?Sized > RwLockReadGuard < ' rwlock , T > {
576
576
/// 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.
579
583
unsafe fn new ( lock : & ' rwlock RwLock < T > ) -> LockResult < RwLockReadGuard < ' rwlock , T > > {
580
584
poison:: map_result ( lock. poison . borrow ( ) , |( ) | RwLockReadGuard {
581
585
data : unsafe { NonNull :: new_unchecked ( lock. data . get ( ) ) } ,
@@ -957,6 +961,68 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> {
957
961
None => Err ( orig) ,
958
962
}
959
963
}
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
+ }
960
1026
}
961
1027
962
1028
impl < ' a , T : ?Sized > MappedRwLockWriteGuard < ' a , T > {
0 commit comments