Skip to content

Commit 74914f5

Browse files
committed
potentially working protocol for futex rwlock
1 parent d1b96a7 commit 74914f5

File tree

1 file changed

+23
-17
lines changed

1 file changed

+23
-17
lines changed

library/std/src/sys/sync/rwlock/futex.rs

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,22 @@ fn is_read_lockable(state: u32) -> bool {
5454
state & MASK < MAX_READERS && !has_readers_waiting(state) && !has_writers_waiting(state)
5555
}
5656

57+
#[inline]
58+
fn is_read_lockable_after_wakeup(state: u32) -> 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 not be write-locked.
64+
//
65+
// If the lock happens to be unlocked, then we defer to the normal `is_read_lockable`
66+
// check that will prioritize any waiting writers first.
67+
state & MASK < MAX_READERS
68+
&& !has_readers_waiting(state)
69+
&& !is_write_locked(state)
70+
&& !is_unlocked(state)
71+
}
72+
5773
#[inline]
5874
fn has_reached_max_readers(state: u32) -> bool {
5975
state & MASK == MAX_READERS
@@ -101,11 +117,12 @@ impl RwLock {
101117

102118
#[cold]
103119
fn read_contended(&self) {
120+
let mut has_slept = false;
104121
let mut state = self.spin_read();
105122

106123
loop {
107124
// If we can lock it, lock it.
108-
if is_read_lockable(state) {
125+
if has_slept && is_read_lockable_after_wakeup(state) || is_read_lockable(state) {
109126
match self.state.compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed)
110127
{
111128
Ok(_) => return, // Locked!
@@ -116,6 +133,7 @@ impl RwLock {
116133
}
117134
}
118135

136+
// FIXME shouldn't this be an assert?
119137
// Check for overflow.
120138
if has_reached_max_readers(state) {
121139
panic!("too many active read locks on RwLock");
@@ -133,22 +151,11 @@ impl RwLock {
133151

134152
// Wait for the state to change.
135153
futex_wait(&self.state, state | READERS_WAITING, None);
154+
has_slept = true;
136155

137-
// FIXME make sure this works
138-
// FIXME this can probably be more elegant
139-
state = self.state.load(Relaxed);
140-
if state & MASK < MAX_READERS && !has_readers_waiting(state) {
141-
match self.state.compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed)
142-
{
143-
Ok(_) => return, // Locked!
144-
Err(s) => {
145-
state = s;
146-
continue;
147-
}
148-
}
149-
}
150-
151-
// Otherwise, spin again after waking up.
156+
// Spin again after waking up.
157+
// Note that if we were waken up by a call to `downgrade`, we will be read-locked, which
158+
// means that this will stop spinning immediately.
152159
state = self.spin_read();
153160
}
154161
}
@@ -178,7 +185,6 @@ impl RwLock {
178185
}
179186
}
180187

181-
// FIXME make sure this works
182188
#[inline]
183189
pub unsafe fn downgrade(&self) {
184190
// Removes all the write bits and adds a single read bit.

0 commit comments

Comments
 (0)