mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-29 16:13:27 +00:00
Refine WaitQueue
with fast path to return early
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
035e12a4bd
commit
81cca42205
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user