use core::task::{Context, Poll};
use std::sync::Arc;
use super::target::Target;
use crate::rt::inbox::{Inbox, Pop};
use crate::rt::shard::Shared;
use crate::rt::task::Runnable;
use crate::sync::atomic_waker::AtomicWaker;
use crate::sync::internal::{AtomicUsize, Ordering};
pub(crate) struct Home {
inbox: Inbox,
target: Target,
live: AtomicUsize,
drained: AtomicWaker,
}
impl Home {
pub(crate) fn new(target: Arc<Shared>) -> Self {
Self {
inbox: Inbox::new(),
target: Target::new(target),
live: AtomicUsize::new(0),
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
}
pub(crate) fn began(&self) {
self.live.fetch_add(1, Ordering::Relaxed);
}
pub(crate) fn ended(&self) {
if self.live.fetch_sub(1, Ordering::AcqRel) == 1 {
self.drained.wake();
}
}
pub(crate) fn is_idle(&self) -> bool {
self.live.load(Ordering::Acquire) == 0
}
pub(crate) fn poll_drained(&self, cx: &mut Context<'_>) -> Poll<()> {
if self.live.load(Ordering::Acquire) == 0 {
return Poll::Ready(());
}
self.drained.register(cx.waker());
if self.live.load(Ordering::Acquire) == 0 {
Poll::Ready(())
} else {
Poll::Pending
}
}
}
impl Drop for Home {
fn drop(&mut self) {
unsafe { self.inbox.drain(core::mem::forget) };
}
}