@@ -59,10 +59,8 @@ impl RwLock {
59
59
60
60
#[ inline]
61
61
pub unsafe fn read ( & self ) {
62
- if let Err ( s) =
63
- self . state . fetch_update ( Acquire , Relaxed , |s| read_lockable ( s) . then ( || s + READ_LOCKED ) )
64
- {
65
- self . read_contended ( s) ;
62
+ if !self . try_read ( ) {
63
+ self . read_contended ( ) ;
66
64
}
67
65
}
68
66
@@ -102,7 +100,9 @@ impl RwLock {
102
100
}
103
101
104
102
#[ cold]
105
- fn read_contended ( & self , mut state : i32 ) {
103
+ fn read_contended ( & self ) {
104
+ let mut state = self . spin_read ( ) ;
105
+
106
106
loop {
107
107
// If we can lock it, lock it.
108
108
if read_lockable ( state) {
@@ -133,12 +133,15 @@ impl RwLock {
133
133
// Wait for the state to change.
134
134
futex_wait ( & self . state , state | READERS_WAITING , None ) ;
135
135
136
- state = self . state . load ( Relaxed ) ;
136
+ // Spin again after waking up.
137
+ state = self . spin_read ( ) ;
137
138
}
138
139
}
139
140
140
141
#[ cold]
141
- fn write_contended ( & self , mut state : i32 ) {
142
+ fn write_contended ( & self ) {
143
+ let mut state = self . spin_write ( ) ;
144
+
142
145
loop {
143
146
// If it's unlocked, we try to lock it.
144
147
if readers ( state) == 0 {
@@ -182,8 +185,8 @@ impl RwLock {
182
185
// Wait for the state to change.
183
186
futex_wait ( & self . writer_notify , seq, None ) ;
184
187
185
- // Check out the new state .
186
- state = self . state . load ( Relaxed ) ;
188
+ // Spin again after waking up .
189
+ state = self . spin_write ( ) ;
187
190
}
188
191
}
189
192
@@ -231,4 +234,32 @@ impl RwLock {
231
234
futex_wake_all ( & self . state ) ;
232
235
}
233
236
}
237
+
238
+ /// Spin for a while, but stop directly at the given condition.
239
+ fn spin_until ( & self , f : impl Fn ( i32 ) -> bool ) -> i32 {
240
+ let mut spin = 100 ; // Chosen by fair dice roll.
241
+ loop {
242
+ let state = self . state . load ( Relaxed ) ;
243
+ if f ( state) || spin == 0 {
244
+ return state;
245
+ }
246
+ crate :: hint:: spin_loop ( ) ;
247
+ spin -= 1 ;
248
+ }
249
+ }
250
+
251
+ fn spin_write ( & self ) -> i32 {
252
+ self . spin_until ( |state| {
253
+ // Stop spinning when we can lock it, or when there's waiting
254
+ // writers, to keep things somewhat fair.
255
+ readers ( state) == 0 || writers_waiting ( state)
256
+ } )
257
+ }
258
+
259
+ fn spin_read ( & self ) -> i32 {
260
+ self . spin_until ( |state| {
261
+ // Stop spinning when it's unlocked or read locked, or when there's waiting threads.
262
+ readers ( state) != WRITE_LOCKED || readers_waiting ( state) || writers_waiting ( state)
263
+ } )
264
+ }
234
265
}
0 commit comments