use core::mem::ManuallyDrop;
use std::sync::Arc;
use super::context;
use super::handoff::home::Home;
use super::shard::Shared;
use super::task::{Runnable, Schedule, ScheduleInfo};
use crate::sync::internal::Ordering;
pub(crate) struct Sched {
shard: ManuallyDrop<Arc<Shared>>,
home: ManuallyDrop<Option<Arc<Home>>>,
hooks: ManuallyDrop<Option<Arc<super::tuning::TaskHooks>>>,
counted: bool,
}
impl Sched {
pub(crate) fn counted(
shard: Arc<Shared>,
home: Option<Arc<Home>>,
hooks: Option<Arc<super::tuning::TaskHooks>>,
) -> Self {
if let Some(home) = &home {
home.began();
}
shard.load.fetch_add(1, Ordering::Relaxed);
Self {
shard: ManuallyDrop::new(shard),
home: ManuallyDrop::new(home),
hooks: ManuallyDrop::new(hooks),
counted: true,
}
}
#[cfg(unix)]
pub(crate) fn uncounted(shard: Arc<Shared>) -> Self {
Self {
shard: ManuallyDrop::new(shard),
home: ManuallyDrop::new(None),
hooks: ManuallyDrop::new(None),
counted: false,
}
}
}
impl Drop for Sched {
#[inline(always)]
fn drop(&mut self) {
unsafe {
ManuallyDrop::drop(&mut self.shard);
ManuallyDrop::drop(&mut self.home);
ManuallyDrop::drop(&mut self.hooks);
}
}
}
unsafe impl Schedule for Sched {
#[inline(always)]
fn schedule(&self, runnable: Runnable, info: ScheduleInfo) {
let left = match &*self.home {
None => queue_here(&self.shard, runnable, info),
Some(home) => queue_home_here(home, runnable, info),
};
if let Some(runnable) = left {
self.schedule_elsewhere(runnable, info);
}
}
#[inline(always)]
fn drop_future(&self, drop: impl FnOnce()) {
if !self.counted {
return drop();
}
{
let _runtime = self.shard.enter_runtime();
if let Some(home) = &*self.home {
home.ended();
}
drop();
}
if self.shard.load.fetch_sub(1, Ordering::Release) == 1 {
self.shard.last_task_done();
}
}
#[inline]
fn before_poll(&self, id: super::task::Id) {
if let Some(hooks) = &*self.hooks {
super::tuning::call(hooks.before_poll.as_ref(), id);
}
}
#[inline]
fn after_poll(&self, id: super::task::Id) {
if let Some(hooks) = &*self.hooks {
super::tuning::call(hooks.after_poll.as_ref(), id);
}
}
#[inline]
fn terminated(&self, id: super::task::Id) {
if let Some(hooks) = &*self.hooks {
super::tuning::call(hooks.terminate.as_ref(), id);
}
}
fn panicked(&self, _id: super::task::Id) {
let runtime = self
.hooks
.as_ref()
.and_then(|hooks| hooks.shutdown.as_ref());
if let Some(runtime) = runtime.and_then(std::sync::Weak::upgrade) {
super::task::abort_on_panic(|| runtime.shut_down_on_panic());
}
}
}
#[inline(always)]
pub(crate) fn queue_here(
shard: &Arc<Shared>,
runnable: Runnable,
info: ScheduleInfo,
) -> Option<Runnable> {
let here = context::local();
match context::running_inside(here) {
Some(current) if current.is(shard) => {
current.push_local_from(runnable, info, here.in_task());
None
}
_ => Some(runnable),
}
}
#[inline(always)]
pub(crate) fn queue_home_here(
home: &Home,
runnable: Runnable,
info: ScheduleInfo,
) -> Option<Runnable> {
let here = context::local();
match context::running_inside(here) {
Some(current) if context::is_home(home) || current.owns_home(home) => {
current.push_local_from(runnable, info, here.in_task());
None
}
_ => Some(runnable),
}
}
impl Sched {
#[inline(never)]
fn schedule_elsewhere(&self, runnable: Runnable, info: ScheduleInfo) {
if let Some(home) = &*self.home {
let remote = context::current(|current| match current {
Some(shard) if context::is_home(home) || shard.owns_home(home) => {
shard.push_local(runnable, info);
None
}
_ => Some(runnable),
});
if let Some(runnable) = remote {
let keep = runnable.waker();
home.push(runnable);
drop(keep);
}
return;
}
let remote = context::current(|current| match current {
Some(shard) if shard.is(&self.shard) => {
shard.push_local(runnable, info);
None
}
_ => Some(runnable),
});
if let Some(runnable) = remote {
let keep = runnable.waker();
self.shard.push_remote(runnable);
drop(keep);
}
}
}