use alloc::collections::VecDeque;
use ax_kernel_guard::{NoOp, NoPreemptIrqSave};
use ax_kspin::{SpinNoIrq, SpinNoIrqGuard};
use crate::{AxTaskRef, CurrentTask, current_run_queue, select_run_queue};
pub struct WaitQueue {
queue: SpinNoIrq<VecDeque<AxTaskRef>>,
}
pub(crate) type WaitQueueGuard<'a> = SpinNoIrqGuard<'a, VecDeque<AxTaskRef>>;
impl Default for WaitQueue {
fn default() -> Self {
Self::new()
}
}
impl WaitQueue {
pub const fn new() -> Self {
Self {
queue: SpinNoIrq::new(VecDeque::new()),
}
}
fn cancel_events(&self, curr: CurrentTask, _from_timer_list: bool) {
if curr.in_wait_queue() {
self.queue.lock().retain(|t| !curr.ptr_eq(t));
curr.set_in_wait_queue(false);
}
#[cfg(feature = "irq")]
if _from_timer_list {
curr.timer_ticket_expired();
}
}
pub fn wait(&self) {
crate::api::might_sleep();
current_run_queue::<NoPreemptIrqSave>().blocked_resched(self.queue.lock());
self.cancel_events(crate::current(), false);
}
pub fn wait_until<F>(&self, condition: F)
where
F: Fn() -> bool,
{
crate::api::might_sleep();
let curr = crate::current();
loop {
let mut rq = current_run_queue::<NoPreemptIrqSave>();
let wq = self.queue.lock();
if condition() {
break;
}
rq.blocked_resched(wq);
}
self.cancel_events(curr, false);
}
#[cfg(feature = "irq")]
pub fn wait_timeout(&self, dur: core::time::Duration) -> bool {
crate::api::might_sleep();
let mut rq = current_run_queue::<NoPreemptIrqSave>();
let curr = crate::current();
let deadline = ax_hal::time::wall_time() + dur;
debug!(
"task wait_timeout: {} deadline={:?}",
curr.id_name(),
deadline
);
crate::timers::set_alarm_wakeup(deadline, curr.clone());
rq.blocked_resched(self.queue.lock());
let timeout = curr.in_wait_queue();
self.cancel_events(curr, true);
timeout
}
#[cfg(feature = "irq")]
pub fn wait_timeout_until<F>(&self, dur: core::time::Duration, condition: F) -> bool
where
F: Fn() -> bool,
{
crate::api::might_sleep();
let curr = crate::current();
let deadline = ax_hal::time::wall_time() + dur;
debug!(
"task wait_timeout: {}, deadline={:?}",
curr.id_name(),
deadline
);
crate::timers::set_alarm_wakeup(deadline, curr.clone());
let mut timeout = true;
loop {
let mut rq = current_run_queue::<NoPreemptIrqSave>();
if ax_hal::time::wall_time() >= deadline {
break;
}
let wq = self.queue.lock();
if condition() {
timeout = false;
break;
}
rq.blocked_resched(wq);
}
self.cancel_events(curr, true);
timeout
}
pub fn notify_one(&self, resched: bool) -> bool {
let mut wq = self.queue.lock();
if let Some(task) = wq.pop_front() {
unblock_one_task(task, resched);
true
} else {
false
}
}
pub fn notify_one_with<F>(&self, resched: bool, func: F) -> bool
where
F: Fn(u64),
{
let mut wq = self.queue.lock();
if let Some(task) = wq.pop_front() {
func(task.id().as_u64());
unblock_one_task(task, resched);
true
} else {
func(0);
false
}
}
pub fn notify_all(&self, resched: bool) {
while self.notify_one(resched) {
}
}
}
fn unblock_one_task(task: AxTaskRef, resched: bool) {
task.set_in_wait_queue(false);
select_run_queue::<NoOp>(&task).unblock_task(task, resched)
}