use core::task::{Context, Poll};
use std::sync::Arc;
use std::time::Instant;
use super::target::Target;
use crate::rt::inbox::{Inbox, Pop};
use crate::rt::shard::backoff::Backoff;
use crate::rt::shard::{Shard, Shared};
use crate::rt::task::Runnable;
use crate::rt::task::list::{Links, TaskList};
use crate::sync::atomic_waker::AtomicWaker;
pub(crate) struct Home {
inbox: Inbox,
target: Target,
tasks: TaskList,
drained: AtomicWaker,
}
impl Home {
pub(crate) fn new(target: Arc<Shared>) -> Self {
Self {
inbox: Inbox::new(),
target: Target::new(target),
tasks: TaskList::new(),
drained: AtomicWaker::new(),
}
}
pub(crate) fn push(&self, runnable: Runnable) {
self.inbox.push(runnable);
self.target.get().unpark();
}
pub(crate) unsafe fn pop(&self) -> Option<Runnable> {
match unsafe { self.inbox.pop() } {
Pop::Item(runnable) => Some(runnable),
Pop::Empty | Pop::Busy => None,
}
}
pub(crate) unsafe fn is_drained(&self) -> bool {
unsafe { self.inbox.is_drained() }
}
pub(crate) fn target(&self) -> &Target {
&self.target
}
#[inline(always)]
pub(crate) unsafe fn push_task(&self, task: &Links) {
unsafe { self.tasks.push(task) }
}
#[inline(always)]
pub(crate) unsafe fn task_ended(&self) {
if unsafe { self.tasks.is_empty() } {
self.drained.wake();
}
}
pub(crate) unsafe fn is_idle(&self) -> bool {
unsafe { self.tasks.is_empty() }
}
pub(crate) unsafe fn poll_drained(&self, cx: &mut Context<'_>) -> Poll<()> {
if unsafe { self.tasks.is_empty() } {
return Poll::Ready(());
}
self.drained.register(cx.waker());
if unsafe { self.tasks.is_empty() } {
Poll::Ready(())
} else {
Poll::Pending
}
}
}
impl Home {
pub(crate) unsafe fn cancel_listed(&self) -> usize {
unsafe { self.tasks.cancel_all() }
}
pub(crate) unsafe fn cancel_all(&self, own: Option<&Shard>, deadline: Instant) {
let mut backoff = Backoff::new();
loop {
while let Some(runnable) = unsafe { self.pop() } {
drop(runnable);
}
if let Some(own) = own {
own.drop_queued();
}
unsafe { self.tasks.cancel_all() };
if unsafe { self.is_idle() } {
return;
}
if Instant::now() >= deadline {
let left = unsafe { self.tasks.len() };
crate::rt::counters::tasks_left(left as u64);
log::warn!(
"{left} tasks bound to an exiting thread were still being woken at the shutdown deadline; they are leaked"
);
return;
}
backoff.pause();
}
}
}
impl Drop for Home {
fn drop(&mut self) {
unsafe { self.inbox.drain(drop) };
}
}