Skip to content

Commit 13ea138

Browse files
committed
very wrong queue downgrade
1 parent 454329e commit 13ea138

File tree

1 file changed

+50
-32
lines changed

1 file changed

+50
-32
lines changed

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

Lines changed: 50 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,11 @@ const MASK: usize = !(QUEUE_LOCKED | QUEUED | LOCKED);
136136
#[inline]
137137
fn write_lock(state: State) -> Option<State> {
138138
let state = state.wrapping_byte_add(LOCKED);
139-
if state.addr() & LOCKED == LOCKED { Some(state) } else { None }
139+
if state.addr() & LOCKED == LOCKED {
140+
Some(state)
141+
} else {
142+
None
143+
}
140144
}
141145

142146
/// Marks the state as read-locked, if possible.
@@ -317,13 +321,26 @@ impl RwLock {
317321
let mut node = Node::new(write);
318322
let mut state = self.state.load(Relaxed);
319323
let mut count = 0;
324+
let mut has_slept = false;
320325
loop {
321326
if let Some(next) = update(state) {
322327
// The lock is available, try locking it.
323328
match self.state.compare_exchange_weak(state, next, Acquire, Relaxed) {
324329
Ok(_) => return,
325330
Err(new) => state = new,
326331
}
332+
} else if !write && has_slept {
333+
// If we are trying to read and we have already gone to sleep, first check if the
334+
// lock is in read mode before going to sleep again.
335+
let tail = unsafe { add_backlinks_and_find_tail(to_node(state)).as_ref() };
336+
let _ = tail.next.0.fetch_update(Release, Acquire, |count: *mut Node| {
337+
if count.mask(MASK).addr() > 0 {
338+
Some(without_provenance_mut(state.addr().checked_add(SINGLE)? | LOCKED))
339+
} else {
340+
None
341+
}
342+
});
343+
todo!("This is very wrong");
327344
} else if state.addr() & QUEUED == 0 && count < SPIN_COUNT {
328345
// If the lock is not available and no threads are queued, spin
329346
// for a while, using exponential backoff to decrease cache
@@ -388,6 +405,8 @@ impl RwLock {
388405
node.wait();
389406
}
390407

408+
has_slept = true;
409+
391410
// The node was removed from the queue, disarm the guard.
392411
mem::forget(guard);
393412

@@ -453,45 +472,44 @@ impl RwLock {
453472

454473
#[inline]
455474
pub unsafe fn downgrade(&self) {
456-
// Atomically set to read-locked with a single reader, without any waiting threads.
457-
if let Err(mut state) = self.state.compare_exchange(
475+
// Atomically attempt to go from a single writer without any waiting threads to a single
476+
// reader without any waiting threads.
477+
if let Err(state) = self.state.compare_exchange(
458478
without_provenance_mut(LOCKED),
459479
without_provenance_mut(LOCKED | SINGLE),
460480
Release,
461481
Relaxed,
462482
) {
463-
// Attempt to grab the queue lock.
464-
loop {
465-
let next = state.map_addr(|addr| addr | QUEUE_LOCKED);
466-
match self.state.compare_exchange(state, next, AcqRel, Relaxed) {
467-
Err(new_state) => state = new_state,
468-
Ok(new_state) => {
469-
assert_eq!(
470-
new_state.mask(!MASK).addr(),
471-
LOCKED | QUEUED | QUEUE_LOCKED,
472-
"{:p}",
473-
new_state
474-
);
475-
state = new_state;
476-
break;
477-
}
478-
}
479-
}
480-
481-
assert_eq!(state.mask(!MASK).addr(), LOCKED | QUEUED | QUEUE_LOCKED);
483+
debug_assert!(
484+
state.mask(LOCKED).addr() != 0 && state.mask(QUEUED).addr() != 0,
485+
"RwLock should be LOCKED and QUEUED"
486+
);
482487

483-
// SAFETY: We have the queue lock so all safety contracts are fulfilled.
484-
let tail = unsafe { add_backlinks_and_find_tail(to_node(state)).as_ref() };
488+
// FIXME Is this correct?
489+
// SAFETY: Since we have the write lock, nobody else can be modifying state, and since
490+
// we got `state` from the `compare_exchange`, we know it is a valid head of the queue.
491+
let tail = unsafe { add_backlinks_and_find_tail(to_node(state)) };
485492

493+
// FIXME Is this safe to modify? There shouldn't be other threads modifying this since
494+
// we have the write lock and only we should be able to modify the nodes in the queue...
486495
// Increment the reader count from 0 to 1.
487-
assert_eq!(
488-
tail.next.0.fetch_byte_add(SINGLE, AcqRel).addr(),
489-
0,
490-
"Reader count was not zero while we had the write lock"
491-
);
492-
493-
// Release the queue lock.
494-
self.state.fetch_byte_sub(QUEUE_LOCKED, Release);
496+
let old = unsafe { tail.as_ref() }.next.0.fetch_byte_add(SINGLE, AcqRel).addr();
497+
debug_assert_eq!(old, 0, "Reader count was not zero while we had the write lock");
498+
499+
// Now that we are in read mode, traverse the queue and wake up readers until we find a
500+
// writer node.
501+
let mut current = tail;
502+
while unsafe { !current.as_ref().write } {
503+
let prev = unsafe { current.as_ref().prev.get() };
504+
unsafe {
505+
// There must be threads waiting.
506+
Node::complete(current);
507+
}
508+
match prev {
509+
Some(prev) => current = prev,
510+
None => return,
511+
}
512+
}
495513
}
496514
}
497515

0 commit comments

Comments
 (0)