@@ -18,6 +18,7 @@ pub struct RwLock {
1818const READ_LOCKED : Primitive = 1 ;
1919const MASK : Primitive = ( 1 << 30 ) - 1 ;
2020const WRITE_LOCKED : Primitive = MASK ;
21+ const DOWNGRADE : Primitive = READ_LOCKED . wrapping_sub ( WRITE_LOCKED ) ; // READ_LOCKED - WRITE_LOCKED
2122const MAX_READERS : Primitive = MASK - 1 ;
2223const READERS_WAITING : Primitive = 1 << 30 ;
2324const WRITERS_WAITING : Primitive = 1 << 31 ;
@@ -53,6 +54,24 @@ fn is_read_lockable(state: Primitive) -> bool {
5354 state & MASK < MAX_READERS && !has_readers_waiting ( state) && !has_writers_waiting ( state)
5455}
5556
57+ #[ inline]
58+ fn is_read_lockable_after_wakeup ( state : Primitive ) -> bool {
59+ // We make a special case for checking if we can read-lock _after_ a reader thread that went to
60+ // sleep has been woken up by a call to `downgrade`.
61+ //
62+ // `downgrade` will wake up all readers and place the lock in read mode. Thus, there should be
63+ // no readers waiting and the lock should be read-locked (not write-locked or unlocked).
64+ //
65+ // Note that we do not check if any writers are waiting. This is because a call to `downgrade`
66+ // implies that the caller wants other readers to read the value protected by the lock. If we
67+ // did not allow readers to acquire the lock before writers after a `downgrade`, then only the
68+ // original writer would be able to read the value, thus defeating the purpose of `downgrade`.
69+ state & MASK < MAX_READERS
70+ && !has_readers_waiting ( state)
71+ && !is_write_locked ( state)
72+ && !is_unlocked ( state)
73+ }
74+
5675#[ inline]
5776fn has_reached_max_readers ( state : Primitive ) -> bool {
5877 state & MASK == MAX_READERS
@@ -84,6 +103,9 @@ impl RwLock {
84103 }
85104 }
86105
106+ /// # Safety
107+ ///
108+ /// The `RwLock` must be read-locked (N readers) in order to call this.
87109 #[ inline]
88110 pub unsafe fn read_unlock ( & self ) {
89111 let state = self . state . fetch_sub ( READ_LOCKED , Release ) - READ_LOCKED ;
@@ -100,11 +122,13 @@ impl RwLock {
100122
101123 #[ cold]
102124 fn read_contended ( & self ) {
125+ let mut has_slept = false ;
103126 let mut state = self . spin_read ( ) ;
104127
105128 loop {
106- // If we can lock it, lock it.
107- if is_read_lockable ( state) {
129+ // If we have just been woken up, first check for a `downgrade` call.
130+ // Otherwise, if we can read-lock it, lock it.
131+ if ( has_slept && is_read_lockable_after_wakeup ( state) ) || is_read_lockable ( state) {
108132 match self . state . compare_exchange_weak ( state, state + READ_LOCKED , Acquire , Relaxed )
109133 {
110134 Ok ( _) => return , // Locked!
@@ -116,9 +140,7 @@ impl RwLock {
116140 }
117141
118142 // Check for overflow.
119- if has_reached_max_readers ( state) {
120- panic ! ( "too many active read locks on RwLock" ) ;
121- }
143+ assert ! ( !has_reached_max_readers( state) , "too many active read locks on RwLock" ) ;
122144
123145 // Make sure the readers waiting bit is set before we go to sleep.
124146 if !has_readers_waiting ( state) {
@@ -132,6 +154,7 @@ impl RwLock {
132154
133155 // Wait for the state to change.
134156 futex_wait ( & self . state , state | READERS_WAITING , None ) ;
157+ has_slept = true ;
135158
136159 // Spin again after waking up.
137160 state = self . spin_read ( ) ;
@@ -152,6 +175,9 @@ impl RwLock {
152175 }
153176 }
154177
178+ /// # Safety
179+ ///
180+ /// The `RwLock` must be write-locked (single writer) in order to call this.
155181 #[ inline]
156182 pub unsafe fn write_unlock ( & self ) {
157183 let state = self . state . fetch_sub ( WRITE_LOCKED , Release ) - WRITE_LOCKED ;
@@ -163,6 +189,22 @@ impl RwLock {
163189 }
164190 }
165191
192+ /// # Safety
193+ ///
194+ /// The `RwLock` must be write-locked (single writer) in order to call this.
195+ #[ inline]
196+ pub unsafe fn downgrade ( & self ) {
197+ // Removes all write bits and adds a single read bit.
198+ let state = self . state . fetch_add ( DOWNGRADE , Relaxed ) ;
199+ debug_assert ! ( is_write_locked( state) , "RwLock must be write locked to call `downgrade`" ) ;
200+
201+ if has_readers_waiting ( state) {
202+ // Since we had the exclusive lock, nobody else can unset this bit.
203+ self . state . fetch_sub ( READERS_WAITING , Relaxed ) ;
204+ futex_wake_all ( & self . state ) ;
205+ }
206+ }
207+
166208 #[ cold]
167209 fn write_contended ( & self ) {
168210 let mut state = self . spin_write ( ) ;
0 commit comments