use core::cell::UnsafeCell;
use core::fmt;
use core::future::Future;
use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::pin::Pin;
use core::task::{Context, Poll, Waker};
use std::sync::Arc;
use std::task::Wake;
use std::time::Duration;
use super::{Instant, clock};
use super::wheel::{TimerKey, Wheel};
use crate::rt::context;
use crate::rt::shard::{Shard, Shared};
use crate::sync::atomic_waker::AtomicWaker;
use crate::sync::internal::{AtomicBool, Ordering};
#[inline(always)]
pub fn sleep(duration: Duration) -> Sleep {
Sleep {
timer: Timer::after(duration),
}
}
pub fn sleep_until(deadline: impl Into<Instant>) -> Sleep {
Sleep {
timer: Timer::new(deadline.into()),
}
}
pub(super) fn far_future() -> Instant {
Instant::now() + Duration::from_secs(86_400 * 365 * 30)
}
#[must_use = "a sleep does nothing unless awaited"]
pub struct Sleep {
timer: Timer<Coarse>,
}
impl Sleep {
pub fn deadline(&self) -> Instant {
self.timer.deadline()
}
pub fn is_elapsed(&self) -> bool {
self.timer.is_elapsed()
}
pub fn reset(self: Pin<&mut Self>, deadline: impl Into<Instant>) {
self.get_mut().timer.reset(deadline.into());
}
}
impl Future for Sleep {
type Output = ();
#[inline(always)]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
self.get_mut().timer.poll(cx)
}
}
impl fmt::Debug for Sleep {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Sleep")
.field("deadline", &self.deadline())
.field("elapsed", &self.is_elapsed())
.finish()
}
}
#[inline(always)]
fn ready(cx: &Context<'_>) -> Poll<()> {
ready_in(context::local(), cx)
}
#[inline(always)]
fn ready_in(here: &context::Here, cx: &Context<'_>) -> Poll<()> {
if crate::coop::spend_in(&here.budget, cx.waker()) {
Poll::Ready(())
} else {
Poll::Pending
}
}
pub(crate) trait Grain {
const PRECISE: bool;
}
pub(crate) struct Coarse;
impl Grain for Coarse {
const PRECISE: bool = false;
}
pub(crate) struct Fine;
impl Grain for Fine {
const PRECISE: bool = true;
}
pub(super) struct Timer<G: Grain> {
deadline: Deadline,
state: ManuallyDrop<State>,
grain: PhantomData<G>,
}
#[derive(Clone, Copy)]
enum Deadline {
Nanos(u64),
Given(Instant),
}
enum State {
Idle,
Local { owner: Arc<Shared>, key: TimerKey },
Remote {
shard: Arc<Shared>,
cell: Arc<RemoteTimer>,
},
Done,
}
impl<G: Grain> Timer<G> {
pub(super) fn new(deadline: Instant) -> Self {
Self {
deadline: Deadline::Given(deadline),
state: ManuallyDrop::new(State::Idle),
grain: PhantomData,
}
}
#[inline(always)]
pub(super) fn after(duration: Duration) -> Self {
let span = duration
.as_secs()
.checked_mul(1_000_000_000)
.and_then(|whole| whole.checked_add(u64::from(duration.subsec_nanos())));
let now = clock::now_nanos();
match span.and_then(|span| now.checked_add(span)) {
Some(at) => Self {
deadline: Deadline::Nanos(at),
state: ManuallyDrop::new(State::Idle),
grain: PhantomData,
},
None => Self::new(far_future()),
}
}
pub(super) fn deadline(&self) -> Instant {
match self.deadline {
Deadline::Nanos(at) => Instant::from_std(clock::instant(at)),
Deadline::Given(at) => at,
}
}
#[inline]
fn nanos(&self) -> u64 {
match self.deadline {
Deadline::Nanos(at) => at,
Deadline::Given(at) => clock::nanos(at.into_std()),
}
}
pub(super) fn is_elapsed(&self) -> bool {
matches!(*self.state, State::Done) || clock::now_nanos() >= self.nanos()
}
pub(super) fn reset(&mut self, deadline: Instant) {
self.release();
self.deadline = Deadline::Given(deadline);
}
#[inline]
fn release(&mut self) {
match *self.state {
State::Idle => {}
State::Done => *self.state = State::Idle,
State::Local { .. } | State::Remote { .. } => self.release_armed(),
}
}
#[inline(never)]
fn release_armed(&mut self) {
match core::mem::replace(&mut *self.state, State::Idle) {
State::Local { owner, key } => {
let (here, removed) = context::current(|current| match current {
Some(shard) if shard.is(&owner) => {
(true, shard.wheel_of(G::PRECISE).borrow_mut().remove(key))
}
_ => (false, None),
});
if !here {
owner.post_timer(TimerOp::Remove {
key,
precise: G::PRECISE,
});
}
drop(removed);
}
State::Remote { shard, cell } => {
cell.cancelled.store(true, Ordering::SeqCst);
let waker = cell.waker.take();
shard.post_timer(TimerOp::Cancel(cell));
drop(waker);
}
State::Idle | State::Done => {}
}
}
#[inline(always)]
fn poll_local(&mut self, shard: &Shard, cx: &mut Context<'_>) -> Poll<()> {
if let State::Local { owner, key } = &*self.state
&& shard.is(owner)
{
let key = *key;
let wheel = shard.wheel_of(G::PRECISE);
if wheel.borrow().is_fired(key) {
self.end_local();
return ready(cx);
}
if wheel.borrow().wakes(key, cx.waker()) {
return Poll::Pending;
}
let fresh = cx.waker().clone();
let (old, fired) = {
let mut wheel = wheel.borrow_mut();
let old = wheel.replace_waker(key, fresh);
(old, wheel.is_fired(key))
};
drop(old);
if fired {
self.end_local();
return ready(cx);
}
return Poll::Pending;
}
if !matches!(*self.state, State::Idle) {
if let Some(polled) = self.poll_remote_cell(cx) {
return polled;
}
self.release();
}
let at = self.nanos();
if clock::now_nanos() >= at {
*self.state = State::Done;
return ready(cx);
}
let at = shard.time_of(G::PRECISE, at);
let fresh = cx.waker().clone();
let armed = shard.wheel_of(G::PRECISE).borrow_mut().insert(at, fresh);
let key = match armed {
Ok(key) => key,
Err(unused) => {
*self.state = State::Done;
drop(unused);
return ready(cx);
}
};
*self.state = State::Local {
owner: Arc::clone(&shard.shared),
key,
};
Poll::Pending
}
fn poll_remote(&mut self, cx: &mut Context<'_>) -> Poll<()> {
if let Some(polled) = self.poll_remote_cell(cx) {
return polled;
}
self.release();
let _arming = clock::any_altered().then(Arming::enter).flatten();
let at = self.nanos();
if clock::now_nanos() >= at {
*self.state = State::Done;
return ready(cx);
}
let cell = Arc::new(RemoteTimer {
fired: AtomicBool::new(false),
cancelled: AtomicBool::new(false),
orphaned: AtomicBool::new(false),
precise: G::PRECISE,
waker: AtomicWaker::new(),
key: UnsafeCell::new(None),
});
cell.waker.register(cx.waker());
let shard = crate::rt::runtime::service_shard();
shard.post_timer(TimerOp::Insert {
at,
cell: Arc::clone(&cell),
});
*self.state = State::Remote { shard, cell };
Poll::Pending
}
#[inline]
fn poll_remote_cell(&mut self, cx: &mut Context<'_>) -> Option<Poll<()>> {
let State::Remote { cell, .. } = &*self.state else {
return None;
};
match poll_cell(cell, cx) {
Found::Fired => {
*self.state = State::Done;
Some(ready(cx))
}
Found::Pending => Some(Poll::Pending),
Found::Orphaned => {
*self.state = State::Idle;
None
}
}
}
#[inline(always)]
fn end_local(&mut self) {
if let State::Local { owner, .. } = core::mem::replace(&mut *self.state, State::Done) {
drop(owner);
}
}
#[inline(always)]
pub(super) fn poll(&mut self, cx: &mut Context<'_>) -> Poll<()> {
let here = context::local();
if let State::Local { owner, key } = &*self.state
&& let Some(shard) = context::running_inside(here)
&& shard.is(owner)
&& shard.wheel_of(G::PRECISE).borrow().is_fired(*key)
{
self.end_local();
return ready_in(here, cx);
}
self.poll_any(cx)
}
#[inline(never)]
fn poll_any(&mut self, cx: &mut Context<'_>) -> Poll<()> {
if let State::Done = *self.state {
return ready(cx);
}
if let Some(shard) = context::running_inside(context::local()) {
return self.poll_local(shard, cx);
}
context::current(|current| match current {
Some(shard) => self.poll_local(shard, cx),
None => self.poll_remote(cx),
})
}
}
impl<G: Grain> Drop for Timer<G> {
fn drop(&mut self) {
if matches!(*self.state, State::Local { .. } | State::Remote { .. }) {
self.release_armed();
}
}
}
enum Found {
Fired,
Pending,
Orphaned,
}
fn poll_cell(cell: &RemoteTimer, cx: &mut Context<'_>) -> Found {
if cell.fired.load(Ordering::Acquire) {
return Found::Fired;
}
#[cfg(test)]
before_register();
cell.waker.register(cx.waker());
if cell.fired.load(Ordering::Acquire) {
Found::Fired
} else if cell.orphaned.load(Ordering::Acquire) {
Found::Orphaned
} else {
Found::Pending
}
}
#[cfg(test)]
thread_local! {
pub(super) static BEFORE_REGISTER: core::cell::RefCell<Option<Box<dyn FnOnce()>>> =
const { core::cell::RefCell::new(None) };
}
#[cfg(test)]
fn before_register() {
if let Some(hook) = BEFORE_REGISTER.with(|hook| hook.borrow_mut().take()) {
hook();
}
}
struct Arming(Arc<crate::rt::runtime::Inner>);
impl Arming {
#[cold]
fn enter() -> Option<Self> {
let inner = crate::rt::runtime::running_inner()?;
if !inner.clock.is_altered() {
return None;
}
inner.clock.arm_began();
let arming = Self(inner);
arming.0.clock.wait_advance();
Some(arming)
}
}
impl Drop for Arming {
fn drop(&mut self) {
self.0.clock.arm_ended();
crate::rt::paused::try_advance(&self.0, None);
}
}
pub(crate) struct RemoteTimer {
fired: AtomicBool,
cancelled: AtomicBool,
orphaned: AtomicBool,
precise: bool,
waker: AtomicWaker,
key: UnsafeCell<Option<TimerKey>>,
}
unsafe impl Send for RemoteTimer {}
unsafe impl Sync for RemoteTimer {}
impl RemoteTimer {
fn fire(&self) {
unsafe { *self.key.get() = None };
self.fired.store(true, Ordering::Release);
self.waker.wake();
}
pub(crate) fn orphan(&self) {
if self.fired.load(Ordering::Acquire) || self.cancelled.load(Ordering::SeqCst) {
return;
}
self.orphaned.store(true, Ordering::Release);
self.waker.wake();
}
}
struct Armed(Arc<RemoteTimer>);
impl Wake for Armed {
fn wake(self: Arc<Self>) {
self.0.fire();
}
fn wake_by_ref(self: &Arc<Self>) {
self.0.fire();
}
}
impl Drop for Armed {
fn drop(&mut self) {
self.0.orphan();
}
}
pub(crate) enum TimerOp {
Insert { at: u64, cell: Arc<RemoteTimer> },
Remove { key: TimerKey, precise: bool },
Cancel(Arc<RemoteTimer>),
}
impl TimerOp {
pub(crate) fn needs_wake(&self) -> bool {
matches!(self, Self::Insert { .. }) || super::clock::any_altered()
}
pub(crate) fn orphan(self) {
if let Self::Insert { cell, .. } = self {
cell.orphan();
}
}
pub(crate) fn apply(
self,
shard: &Shard,
wheel: &mut Wheel,
fired: &mut Vec<Waker>,
) -> Option<Waker> {
match self {
Self::Insert { at, cell } => {
if cell.cancelled.load(Ordering::SeqCst) {
return None;
}
let waker = Waker::from(Arc::new(Armed(Arc::clone(&cell))));
let at = shard.time_of(cell.precise, at);
let armed = if cell.precise {
shard.wheel_of(true).borrow_mut().insert(at, waker)
} else {
wheel.insert(at, waker)
};
match armed {
Err(unused) => {
cell.fired.store(true, Ordering::Release);
fired.extend(cell.waker.take());
Some(unused)
}
Ok(key) => {
unsafe { *cell.key.get() = Some(key) };
None
}
}
}
Self::Remove {
key,
precise: false,
} => wheel.remove(key),
Self::Remove { key, precise: true } => shard.wheel_of(true).borrow_mut().remove(key),
Self::Cancel(cell) => {
let key = unsafe { (*cell.key.get()).take() }?;
if cell.precise {
shard.wheel_of(true).borrow_mut().remove(key)
} else {
wheel.remove(key)
}
}
}
}
}
#[cfg(all(test, not(loom)))]
mod tests {
use std::panic::{AssertUnwindSafe, catch_unwind};
use super::*;
use crate::rt::shard::Limits;
fn turns_until(shard: &Shard, armed: usize) {
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10);
while shard.wheel().borrow().armed() != armed {
assert!(
std::time::Instant::now() < deadline,
"the wheel never held {armed}"
);
shard.block_on(crate::task::yield_now());
}
}
#[test]
fn a_remote_sleeps_cancel_is_posted_before_its_waker_drops() {
let shard = Shard::new(Limits {
max_nesting: 8,
buffer_cap: crate::buf::DEFAULT_CAP,
backend: crate::rt::backend::chosen().unwrap(),
})
.unwrap();
let _current = context::enter(&shard);
let (bomb, waker) = crate::time::tests::bomb();
let cell = Arc::new(RemoteTimer {
fired: AtomicBool::new(false),
cancelled: AtomicBool::new(false),
orphaned: AtomicBool::new(false),
precise: false,
waker: AtomicWaker::new(),
key: UnsafeCell::new(None),
});
cell.waker.register(&waker);
let at = clock::now_nanos() + 3_600_000_000_000;
shard.shared.post_timer(TimerOp::Insert {
at,
cell: Arc::clone(&cell),
});
let timer = Timer::<Coarse> {
deadline: Deadline::Nanos(at),
state: ManuallyDrop::new(State::Remote {
shard: Arc::clone(&shard.shared),
cell,
}),
grain: PhantomData,
};
turns_until(&shard, 1);
bomb.arm();
assert!(catch_unwind(AssertUnwindSafe(|| drop(timer))).is_err());
turns_until(&shard, 0);
}
}