Skip to content

Commit de4a290

Browse files
committed
Spin in futex rwlock.
1 parent faa9279 commit de4a290

File tree

1 file changed

+40
-9
lines changed

1 file changed

+40
-9
lines changed

library/std/src/sys/unix/locks/futex_rwlock.rs

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,8 @@ impl RwLock {
5959

6060
#[inline]
6161
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();
6664
}
6765
}
6866

@@ -102,7 +100,9 @@ impl RwLock {
102100
}
103101

104102
#[cold]
105-
fn read_contended(&self, mut state: i32) {
103+
fn read_contended(&self) {
104+
let mut state = self.spin_read();
105+
106106
loop {
107107
// If we can lock it, lock it.
108108
if read_lockable(state) {
@@ -133,12 +133,15 @@ impl RwLock {
133133
// Wait for the state to change.
134134
futex_wait(&self.state, state | READERS_WAITING, None);
135135

136-
state = self.state.load(Relaxed);
136+
// Spin again after waking up.
137+
state = self.spin_read();
137138
}
138139
}
139140

140141
#[cold]
141-
fn write_contended(&self, mut state: i32) {
142+
fn write_contended(&self) {
143+
let mut state = self.spin_write();
144+
142145
loop {
143146
// If it's unlocked, we try to lock it.
144147
if readers(state) == 0 {
@@ -182,8 +185,8 @@ impl RwLock {
182185
// Wait for the state to change.
183186
futex_wait(&self.writer_notify, seq, None);
184187

185-
// Check out the new state.
186-
state = self.state.load(Relaxed);
188+
// Spin again after waking up.
189+
state = self.spin_write();
187190
}
188191
}
189192

@@ -231,4 +234,32 @@ impl RwLock {
231234
futex_wake_all(&self.state);
232235
}
233236
}
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+
}
234265
}

0 commit comments

Comments
 (0)