@@ -18,6 +18,7 @@ pub struct RwLock {
18
18
const READ_LOCKED : Primitive = 1 ;
19
19
const MASK : Primitive = ( 1 << 30 ) - 1 ;
20
20
const WRITE_LOCKED : Primitive = MASK ;
21
+ const DOWNGRADE : Primitive = READ_LOCKED . wrapping_sub ( WRITE_LOCKED ) ; // READ_LOCKED - WRITE_LOCKED
21
22
const MAX_READERS : Primitive = MASK - 1 ;
22
23
const READERS_WAITING : Primitive = 1 << 30 ;
23
24
const WRITERS_WAITING : Primitive = 1 << 31 ;
@@ -53,6 +54,24 @@ fn is_read_lockable(state: Primitive) -> bool {
53
54
state & MASK < MAX_READERS && !has_readers_waiting ( state) && !has_writers_waiting ( state)
54
55
}
55
56
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
+
56
75
#[ inline]
57
76
fn has_reached_max_readers ( state : Primitive ) -> bool {
58
77
state & MASK == MAX_READERS
@@ -84,6 +103,9 @@ impl RwLock {
84
103
}
85
104
}
86
105
106
+ /// # Safety
107
+ ///
108
+ /// The `RwLock` must be read-locked (N readers) in order to call this.
87
109
#[ inline]
88
110
pub unsafe fn read_unlock ( & self ) {
89
111
let state = self . state . fetch_sub ( READ_LOCKED , Release ) - READ_LOCKED ;
@@ -100,11 +122,13 @@ impl RwLock {
100
122
101
123
#[ cold]
102
124
fn read_contended ( & self ) {
125
+ let mut has_slept = false ;
103
126
let mut state = self . spin_read ( ) ;
104
127
105
128
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) {
108
132
match self . state . compare_exchange_weak ( state, state + READ_LOCKED , Acquire , Relaxed )
109
133
{
110
134
Ok ( _) => return , // Locked!
@@ -116,9 +140,7 @@ impl RwLock {
116
140
}
117
141
118
142
// 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" ) ;
122
144
123
145
// Make sure the readers waiting bit is set before we go to sleep.
124
146
if !has_readers_waiting ( state) {
@@ -132,6 +154,7 @@ impl RwLock {
132
154
133
155
// Wait for the state to change.
134
156
futex_wait ( & self . state , state | READERS_WAITING , None ) ;
157
+ has_slept = true ;
135
158
136
159
// Spin again after waking up.
137
160
state = self . spin_read ( ) ;
@@ -152,6 +175,9 @@ impl RwLock {
152
175
}
153
176
}
154
177
178
+ /// # Safety
179
+ ///
180
+ /// The `RwLock` must be write-locked (single writer) in order to call this.
155
181
#[ inline]
156
182
pub unsafe fn write_unlock ( & self ) {
157
183
let state = self . state . fetch_sub ( WRITE_LOCKED , Release ) - WRITE_LOCKED ;
@@ -163,6 +189,22 @@ impl RwLock {
163
189
}
164
190
}
165
191
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
+
166
208
#[ cold]
167
209
fn write_contended ( & self ) {
168
210
let mut state = self . spin_write ( ) ;
0 commit comments