add safety comments for queue implementation
This commit is contained in:
parent
26b5a1485e
commit
3d191b50d2
1 changed files with 25 additions and 13 deletions
|
@ -476,16 +476,22 @@ impl RwLock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// * There must be threads queued on the lock.
|
||||||
|
/// * `state` must be a pointer to a node in a valid queue.
|
||||||
|
/// * There cannot be a `downgrade` in progress.
|
||||||
#[cold]
|
#[cold]
|
||||||
unsafe fn read_unlock_contended(&self, state: State) {
|
unsafe fn read_unlock_contended(&self, state: State) {
|
||||||
// The state was observed with acquire ordering above, so the current
|
// SAFETY:
|
||||||
// thread will observe all node initializations.
|
// The state was observed with acquire ordering above, so the current thread will have
|
||||||
|
// observed all node initializations.
|
||||||
// FIXME this is a bit confusing
|
// We also know that no threads can be modifying the queue starting at `state`: because new
|
||||||
// SAFETY: Because new read-locks cannot be acquired while threads are queued, all
|
// read-locks cannot be acquired while there are any threads queued on the lock, all
|
||||||
// queue-lock owners will observe the set `LOCKED` bit. And because no downgrade can be in
|
// queue-lock owners will observe a set `LOCKED` bit in `self.state` and will not modify
|
||||||
// progress (we checked above), they hence do not modify the queue, so the queue will not be
|
// the queue. The other case that a thread could modify the queue is if a downgrade is in
|
||||||
// removed from here.
|
// progress (removal of the entire queue), but since that is part of this function's safety
|
||||||
|
// contract, we can guarantee that no other threads can modify the queue.
|
||||||
let tail = unsafe { find_tail_and_add_backlinks(to_node(state)).as_ref() };
|
let tail = unsafe { find_tail_and_add_backlinks(to_node(state)).as_ref() };
|
||||||
|
|
||||||
// The lock count is stored in the `next` field of `tail`.
|
// The lock count is stored in the `next` field of `tail`.
|
||||||
|
@ -515,10 +521,11 @@ impl RwLock {
|
||||||
///
|
///
|
||||||
/// * The lock must be exclusively owned by this thread.
|
/// * The lock must be exclusively owned by this thread.
|
||||||
/// * There must be threads queued on the lock.
|
/// * There must be threads queued on the lock.
|
||||||
|
/// * `state` must be a pointer to a node in a valid queue.
|
||||||
/// * There cannot be a `downgrade` in progress.
|
/// * There cannot be a `downgrade` in progress.
|
||||||
#[cold]
|
#[cold]
|
||||||
unsafe fn unlock_contended(&self, state: State) {
|
unsafe fn unlock_contended(&self, state: State) {
|
||||||
debug_assert!(state.addr() & STATE == (QUEUED | LOCKED));
|
debug_assert_eq!(state.addr() & (DOWNGRADED | QUEUED | LOCKED), QUEUED | LOCKED);
|
||||||
|
|
||||||
let mut current = state;
|
let mut current = state;
|
||||||
|
|
||||||
|
@ -540,9 +547,10 @@ impl RwLock {
|
||||||
// Atomically release the lock and try to acquire the queue lock.
|
// Atomically release the lock and try to acquire the queue lock.
|
||||||
let next = current.map_addr(|addr| (addr & !LOCKED) | QUEUE_LOCKED);
|
let next = current.map_addr(|addr| (addr & !LOCKED) | QUEUE_LOCKED);
|
||||||
match self.state.compare_exchange_weak(current, next, AcqRel, Relaxed) {
|
match self.state.compare_exchange_weak(current, next, AcqRel, Relaxed) {
|
||||||
|
// Now that we have the queue lock, we can wake up the next waiter.
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// Now that we have the queue lock, we can wake up the next waiter.
|
// SAFETY: This thread just acquired the queue lock, and this function's safety
|
||||||
// SAFETY: This thread is exclusively owned by this thread.
|
// contract requires that there are threads already queued on the lock.
|
||||||
unsafe { self.unlock_queue(next) };
|
unsafe { self.unlock_queue(next) };
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -580,10 +588,11 @@ impl RwLock {
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// * The lock must be write-locked by this thread.
|
/// * The lock must be write-locked by this thread.
|
||||||
|
/// * `state` must be a pointer to a node in a valid queue.
|
||||||
/// * There must be threads queued on the lock.
|
/// * There must be threads queued on the lock.
|
||||||
#[cold]
|
#[cold]
|
||||||
unsafe fn downgrade_slow(&self, mut state: State) {
|
unsafe fn downgrade_slow(&self, mut state: State) {
|
||||||
debug_assert!(state.addr() & (DOWNGRADED | QUEUED | LOCKED) == (QUEUED | LOCKED));
|
debug_assert_eq!(state.addr() & (DOWNGRADED | QUEUED | LOCKED), QUEUED | LOCKED);
|
||||||
|
|
||||||
// Attempt to wake up all waiters by taking ownership of the entire waiter queue.
|
// Attempt to wake up all waiters by taking ownership of the entire waiter queue.
|
||||||
loop {
|
loop {
|
||||||
|
@ -612,7 +621,8 @@ impl RwLock {
|
||||||
let tail = unsafe { find_tail_and_add_backlinks(to_node(state)) };
|
let tail = unsafe { find_tail_and_add_backlinks(to_node(state)) };
|
||||||
|
|
||||||
// Wake up all waiters.
|
// Wake up all waiters.
|
||||||
// SAFETY: `tail` was just computed, meaning the whole queue is linked.
|
// SAFETY: `tail` was just computed, meaning the whole queue is linked, and we have
|
||||||
|
// full ownership of the queue, so we have exclusive access.
|
||||||
unsafe { complete_all(tail) };
|
unsafe { complete_all(tail) };
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -626,11 +636,13 @@ impl RwLock {
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// * The queue lock must be held by the current thread.
|
/// * The queue lock must be held by the current thread.
|
||||||
|
/// * `state` must be a pointer to a node in a valid queue.
|
||||||
/// * There must be threads queued on the lock.
|
/// * There must be threads queued on the lock.
|
||||||
unsafe fn unlock_queue(&self, mut state: State) {
|
unsafe fn unlock_queue(&self, mut state: State) {
|
||||||
debug_assert_eq!(state.addr() & (QUEUED | QUEUE_LOCKED), QUEUED | QUEUE_LOCKED);
|
debug_assert_eq!(state.addr() & (QUEUED | QUEUE_LOCKED), QUEUED | QUEUE_LOCKED);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
// SAFETY: Since we have the queue lock, nobody else can be modifying the queue.
|
||||||
let tail = unsafe { find_tail_and_add_backlinks(to_node(state)) };
|
let tail = unsafe { find_tail_and_add_backlinks(to_node(state)) };
|
||||||
|
|
||||||
if state.addr() & (DOWNGRADED | LOCKED) == LOCKED {
|
if state.addr() & (DOWNGRADED | LOCKED) == LOCKED {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue