Skip to content

Commit 78d5f2a

Browse files
committed
potentially working protocol for futex rwlock
1 parent 050ac42 commit 78d5f2a

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
@@ -56,6 +56,22 @@ fn is_read_lockable(state: u32) -> bool {
5656
state & MASK < MAX_READERS && !has_readers_waiting(state) && !has_writers_waiting(state)
5757
}
5858

59+
#[inline]
60+
fn is_read_lockable_after_wakeup(state: u32) -> bool {
61+
// We make a special case for checking if we can read-lock _after_ a reader thread that went to
62+
// sleep has been woken up by a call to `downgrade`.
63+
//
64+
// `downgrade` will wake up all readers and place the lock in read mode. Thus, there should be
65+
// no readers waiting and the lock should not be write-locked.
66+
//
67+
// If the lock happens to be unlocked, then we defer to the normal `is_read_lockable`
68+
// check that will prioritize any waiting writers first.
69+
state & MASK < MAX_READERS
70+
&& !has_readers_waiting(state)
71+
&& !is_write_locked(state)
72+
&& !is_unlocked(state)
73+
}
74+
5975
#[inline]
6076
fn has_reached_max_readers(state: u32) -> bool {
6177
state & MASK == MAX_READERS
@@ -103,11 +119,12 @@ impl RwLock {
103119

104120
#[cold]
105121
fn read_contended(&self) {
122+
let mut has_slept = false;
106123
let mut state = self.spin_read();
107124

108125
loop {
109126
// If we can lock it, lock it.
110-
if is_read_lockable(state) {
127+
if has_slept && is_read_lockable_after_wakeup(state) || is_read_lockable(state) {
111128
match self.state.compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed)
112129
{
113130
Ok(_) => return, // Locked!
@@ -118,6 +135,7 @@ impl RwLock {
118135
}
119136
}
120137

138+
// FIXME shouldn't this be an assert?
121139
// Check for overflow.
122140
if has_reached_max_readers(state) {
123141
panic!("too many active read locks on RwLock");
@@ -135,22 +153,11 @@ impl RwLock {
135153

136154
// Wait for the state to change.
137155
futex_wait(&self.state, state | READERS_WAITING, None);
156+
has_slept = true;
138157

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

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

0 commit comments

Comments
 (0)