use core::future::Future;
use core::pin::{Pin, pin};
use core::task::{Context, Poll};
use std::sync::atomic::Ordering;
use std::sync::{Arc, Weak};
use std::time::Instant;
use super::Shard;
use crate::rt::context;
use crate::rt::runtime::Inner;
use crate::time::wheel::Wheel;
pub(super) struct Side {
inner: Arc<Inner>,
wheel: Wheel,
precise: Wheel,
swapped: bool,
}
enum Serving {
Same,
Other(Arc<Inner>),
}
const TAG_SHIFT: u32 = 48;
impl Shard {
fn serving(&self, found: Option<&Arc<Inner>>, inner: &Arc<Inner>) -> Serving {
let addr = self.shared.runtime_addr.load(Ordering::Acquire);
if addr != 0 {
if addr == Arc::as_ptr(inner) as usize {
return Serving::Same;
}
if let Some(served) = self.shared.runtime.get().and_then(Weak::upgrade) {
return Serving::Other(served);
}
}
let serving = self.serving.get();
if !serving.is_null() {
if core::ptr::eq(serving, Arc::as_ptr(inner)) {
return Serving::Same;
}
return Serving::Other(unsafe {
Arc::increment_strong_count(serving);
Arc::from_raw(serving)
});
}
match found {
Some(found) if !Arc::ptr_eq(found, inner) => Serving::Other(Arc::clone(found)),
_ => Serving::Same,
}
}
pub(crate) fn serves_other(&self, inner: &Inner) -> bool {
let addr = self.shared.runtime_addr.load(Ordering::Acquire);
if addr != 0 {
return addr != core::ptr::from_ref(inner) as usize;
}
let serving = self.serving.get();
!serving.is_null() && !core::ptr::eq(serving, inner)
}
pub(crate) fn mark_served(&self, served: &Arc<Inner>) -> bool {
if self.shared.runtime_addr.load(Ordering::Relaxed) != 0 || !self.serving.get().is_null() {
return false;
}
self.serving.set(Arc::as_ptr(served));
true
}
pub(crate) fn unmark(&self) {
self.serving.set(core::ptr::null());
}
#[inline(never)]
pub(crate) fn block_on_nested<F: Future>(
&self,
future: F,
found: Option<&Arc<Inner>>,
inner: &Arc<Inner>,
) -> F::Output {
let _aside = Aside::enter(self);
match self.serving(found, inner) {
Serving::Same => self.block_on(future),
Serving::Other(served) => self.block_on_foreign(future, &served, inner),
}
}
pub(crate) fn block_on_foreign<F: Future>(
&self,
future: F,
outer: &Arc<Inner>,
inner: &Arc<Inner>,
) -> F::Output {
let side = (!context::watched()).then(|| self.side_for(inner));
let at = side.map(|(at, _)| at);
let _frame = side.map(|(_, made)| Frame { shard: self, made });
let _outer = context::set_runtime(outer);
let _serving = ServingSet::enter(self, outer);
let mut future = pin!(future);
self.block_on(core::future::poll_fn(|cx| {
self.poll_inside(at, inner, future.as_mut(), cx)
}))
}
fn side_for(&self, inner: &Arc<Inner>) -> (usize, bool) {
let shared = self
.side
.borrow()
.iter()
.position(|entry| Arc::ptr_eq(&entry.inner, inner));
match shared {
Some(at) => (at, false),
None => (self.push_side(inner), true),
}
}
fn push_side(&self, inner: &Arc<Inner>) -> usize {
let seq = self.side_seq.get() + 1;
self.side_seq.set(seq);
let tag = seq << TAG_SHIFT;
let mut wheel = Wheel::tagged(tag);
let mut precise = Wheel::tagged(tag);
let mut none = Vec::new();
wheel.advance(self.wheel_now(), &mut none);
precise.advance(self.precise_now(), &mut none);
let mut side = self.side.borrow_mut();
side.push(Side {
inner: Arc::clone(inner),
wheel,
precise,
swapped: false,
});
side.len() - 1
}
fn poll_inside<F: Future>(
&self,
at: Option<usize>,
inner: &Arc<Inner>,
future: Pin<&mut F>,
cx: &mut Context<'_>,
) -> Poll<F::Output> {
let _runtime = context::set_runtime(inner);
let _swapped = at.map(|at| Swapped::enter(self, at));
future.poll(cx)
}
fn swap_side(&self, at: usize) {
let mut side = self.side.borrow_mut();
let entry = &mut side[at];
core::mem::swap(&mut *self.wheel.borrow_mut(), &mut entry.wheel);
core::mem::swap(&mut *self.precise.borrow_mut(), &mut entry.precise);
entry.swapped = !entry.swapped;
self.foreign_poll.set(entry.swapped);
drop(side);
self.count_side();
}
fn count_side(&self) {
let n = self
.side
.borrow()
.iter()
.filter(|entry| !entry.swapped)
.map(|entry| entry.wheel.armed() + entry.precise.armed())
.sum();
self.precise.borrow_mut().set_phantom(n);
}
#[cold]
#[inline(never)]
pub(super) fn fire_side(&self) {
let mut fired = self.fired.take();
{
let mut side = self.side.borrow_mut();
for entry in side.iter_mut().filter(|entry| !entry.swapped) {
let _runtime = context::set_runtime(&entry.inner);
entry.wheel.advance(self.wheel_now(), &mut fired);
entry.precise.advance(self.precise_now(), &mut fired);
}
}
self.count_side();
self.wake_fired(fired);
}
pub(super) fn side_deadline(&self) -> Option<Instant> {
let side = self.side.borrow();
let wait = side
.iter()
.filter(|entry| !entry.swapped && !entry.inner.clock.is_paused())
.filter_map(|entry| {
let coarse = entry.wheel.next_deadline().map(|ms| ms * 1_000_000);
let fine = entry.precise.next_deadline().map(|us| us * 1_000);
let at = coarse.into_iter().chain(fine).min()?;
Some(at.saturating_sub(entry.inner.clock.now_nanos()))
})
.min()?;
Some(crate::time::clock::now() + core::time::Duration::from_nanos(wait))
}
}
struct Swapped<'a> {
shard: &'a Shard,
at: usize,
}
impl<'a> Swapped<'a> {
fn enter(shard: &'a Shard, at: usize) -> Self {
shard.swap_side(at);
Self { shard, at }
}
}
impl Drop for Swapped<'_> {
fn drop(&mut self) {
if !context::detached_from(&self.shard.shared) {
self.shard.swap_side(self.at);
}
}
}
struct ServingSet<'a> {
shard: &'a Shard,
previous: *const Inner,
}
impl<'a> ServingSet<'a> {
fn enter(shard: &'a Shard, outer: &Arc<Inner>) -> Self {
let previous = shard.serving.replace(Arc::as_ptr(outer));
Self { shard, previous }
}
}
impl Drop for ServingSet<'_> {
fn drop(&mut self) {
self.shard.serving.set(self.previous);
}
}
struct Aside<'a> {
shard: &'a Shard,
at: Option<usize>,
}
impl<'a> Aside<'a> {
fn enter(shard: &'a Shard) -> Self {
let at = shard
.foreign_poll
.get()
.then(|| shard.side.borrow().iter().rposition(|entry| entry.swapped))
.flatten();
if let Some(at) = at {
shard.swap_side(at);
}
Self { shard, at }
}
}
impl Drop for Aside<'_> {
fn drop(&mut self) {
if let Some(at) = self.at
&& !context::detached_from(&self.shard.shared)
{
self.shard.swap_side(at);
}
}
}
struct Frame<'a> {
shard: &'a Shard,
made: bool,
}
impl Drop for Frame<'_> {
fn drop(&mut self) {
if !self.made || context::detached_from(&self.shard.shared) {
return;
}
let gone = self.shard.side.borrow_mut().pop();
self.shard.count_side();
drop(gone);
}
}