Refine WaitQueue with fast path to return early

This commit is contained in:
LI Qing
2024-05-10 16:59:02 +08:00
committed by Tate, Hongliang Tian
parent 035e12a4bd
commit 81cca42205

View File

@ -2,7 +2,7 @@
use alloc::{collections::VecDeque, sync::Arc}; use alloc::{collections::VecDeque, sync::Arc};
use core::{ use core::{
sync::atomic::{AtomicBool, Ordering}, sync::atomic::{AtomicBool, AtomicU32, Ordering},
time::Duration, time::Duration,
}; };
@ -19,12 +19,15 @@ use crate::{
/// Other threads may invoke the `wake`-family methods of a wait queue to /// Other threads may invoke the `wake`-family methods of a wait queue to
/// wake up one or many waiter threads. /// wake up one or many waiter threads.
pub struct WaitQueue { pub struct WaitQueue {
// A copy of `wakers.len()`, used for the lock-free fast path in `wake_one` and `wake_all`.
num_wakers: AtomicU32,
wakers: SpinLock<VecDeque<Arc<Waker>>>, wakers: SpinLock<VecDeque<Arc<Waker>>>,
} }
impl WaitQueue { impl WaitQueue {
pub const fn new() -> Self { pub const fn new() -> Self {
WaitQueue { WaitQueue {
num_wakers: AtomicU32::new(0),
wakers: SpinLock::new(VecDeque::new()), wakers: SpinLock::new(VecDeque::new()),
} }
} }
@ -111,8 +114,20 @@ impl WaitQueue {
/// Wake up one waiting thread. /// Wake up one waiting thread.
pub fn wake_one(&self) { pub fn wake_one(&self) {
while let Some(waker) = self.wakers.lock_irq_disabled().pop_front() { // Fast path
if self.is_empty() {
return;
}
loop {
let mut wakers = self.wakers.lock_irq_disabled();
let Some(waker) = wakers.pop_front() else {
break;
};
self.num_wakers.fetch_sub(1, Ordering::Release);
// Avoid holding lock when calling `wake_up` // Avoid holding lock when calling `wake_up`
drop(wakers);
if waker.wake_up() { if waker.wake_up() {
return; return;
} }
@ -121,18 +136,32 @@ impl WaitQueue {
/// Wake up all waiting threads. /// Wake up all waiting threads.
pub fn wake_all(&self) { pub fn wake_all(&self) {
while let Some(waker) = self.wakers.lock_irq_disabled().pop_front() { // Fast path
if self.is_empty() {
return;
}
loop {
let mut wakers = self.wakers.lock_irq_disabled();
let Some(waker) = wakers.pop_front() else {
break;
};
self.num_wakers.fetch_sub(1, Ordering::Release);
// Avoid holding lock when calling `wake_up` // Avoid holding lock when calling `wake_up`
drop(wakers);
waker.wake_up(); waker.wake_up();
} }
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.wakers.lock_irq_disabled().is_empty() self.num_wakers.load(Ordering::Acquire) == 0
} }
fn enqueue(&self, waker: Arc<Waker>) { fn enqueue(&self, waker: Arc<Waker>) {
self.wakers.lock_irq_disabled().push_back(waker); let mut wakers = self.wakers.lock_irq_disabled();
wakers.push_back(waker);
self.num_wakers.fetch_add(1, Ordering::Release);
} }
} }