use core::cell::UnsafeCell;
use core::fmt;
use core::future::Future;
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;
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};
pub fn sleep(duration: Duration) -> Sleep {
sleep_until(
Instant::now()
.checked_add(duration)
.unwrap_or_else(far_future),
)
}
pub fn sleep_until(deadline: impl Into<Instant>) -> Sleep {
Sleep {
deadline: deadline.into(),
state: State::Idle,
}
}
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 {
deadline: Instant,
state: State,
}
enum State {
Idle,
Local { owner: Arc<Shared>, key: TimerKey },
Remote {
shard: Arc<Shared>,
cell: Arc<RemoteTimer>,
},
Done,
}
impl Sleep {
pub fn deadline(&self) -> Instant {
self.deadline
}
pub fn is_elapsed(&self) -> bool {
matches!(self.state, State::Done) || Instant::now() >= self.deadline
}
pub fn reset(self: Pin<&mut Self>, deadline: impl Into<Instant>) {
let deadline = deadline.into();
let this = self.get_mut();
this.release();
this.deadline = deadline;
}
fn release(&mut self) {
match core::mem::replace(&mut self.state, State::Idle) {
State::Local { owner, key } => {
let here = context::current(|current| match current {
Some(shard) if shard.is(&owner) => {
shard.wheel().borrow_mut().remove(key);
true
}
_ => false,
});
if !here {
owner.post_timer(TimerOp::Remove(key));
}
}
State::Remote { shard, cell } => {
cell.cancelled.store(true, Ordering::SeqCst);
drop(cell.waker.take());
shard.post_timer(TimerOp::Cancel(cell));
}
State::Idle | State::Done => {}
}
}
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 mut wheel = shard.wheel().borrow_mut();
if wheel.is_fired(key) {
wheel.remove(key);
drop(wheel);
self.state = State::Done;
return Poll::Ready(());
}
wheel.set_waker(key, cx.waker());
return Poll::Pending;
}
if let State::Remote { cell, .. } = &self.state {
return Self::poll_cell(cell, cx).map(|()| self.state = State::Done);
}
self.release();
if Instant::now() >= self.deadline {
self.state = State::Done;
return Poll::Ready(());
}
let at = shard.wheel_time(self.deadline.into_std());
let mut wheel = shard.wheel().borrow_mut();
let key = wheel.insert(at, cx.waker());
if wheel.is_fired(key) {
wheel.remove(key);
drop(wheel);
self.state = State::Done;
return Poll::Ready(());
}
drop(wheel);
self.state = State::Local {
owner: Arc::clone(&shard.shared),
key,
};
Poll::Pending
}
fn poll_remote(&mut self, cx: &mut Context<'_>) -> Poll<()> {
if let State::Remote { cell, .. } = &self.state {
return Self::poll_cell(cell, cx).map(|()| self.state = State::Done);
}
self.release();
if Instant::now() >= self.deadline {
self.state = State::Done;
return Poll::Ready(());
}
let cell = Arc::new(RemoteTimer {
fired: AtomicBool::new(false),
cancelled: AtomicBool::new(false),
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: self.deadline,
cell: Arc::clone(&cell),
});
self.state = State::Remote { shard, cell };
Poll::Pending
}
fn poll_cell(cell: &RemoteTimer, cx: &mut Context<'_>) -> Poll<()> {
if cell.fired.load(Ordering::Acquire) {
return Poll::Ready(());
}
cell.waker.register(cx.waker());
if cell.fired.load(Ordering::Acquire) {
Poll::Ready(())
} else {
Poll::Pending
}
}
}
impl Future for Sleep {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
let this = self.get_mut();
if let State::Done = this.state {
return Poll::Ready(());
}
context::current(|current| match current {
Some(shard) => this.poll_local(shard, cx),
None => this.poll_remote(cx),
})
}
}
impl Drop for Sleep {
fn drop(&mut self) {
self.release();
}
}
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()
}
}
pub(crate) struct RemoteTimer {
fired: AtomicBool,
cancelled: AtomicBool,
waker: AtomicWaker,
key: UnsafeCell<Option<TimerKey>>,
}
unsafe impl Send for RemoteTimer {}
unsafe impl Sync for RemoteTimer {}
impl Wake for RemoteTimer {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
if let Some(key) = unsafe { (*self.key.get()).take() } {
context::current(|current| {
if let Some(shard) = current {
shard.wheel().borrow_mut().remove(key);
}
});
}
self.fired.store(true, Ordering::Release);
self.waker.wake();
}
}
pub(crate) enum TimerOp {
Insert { at: Instant, cell: Arc<RemoteTimer> },
Remove(TimerKey),
Cancel(Arc<RemoteTimer>),
}
impl TimerOp {
pub(crate) fn needs_wake(&self) -> bool {
matches!(self, Self::Insert { .. })
}
pub(crate) fn apply(self, shard: &Shard, wheel: &mut Wheel) {
match self {
Self::Insert { at, cell } => {
if cell.cancelled.load(Ordering::SeqCst) {
return;
}
let waker = Waker::from(Arc::clone(&cell));
let key = wheel.insert(shard.wheel_time(at.into_std()), &waker);
if wheel.is_fired(key) {
wheel.remove(key);
cell.fired.store(true, Ordering::Release);
cell.waker.wake();
} else {
unsafe { *cell.key.get() = Some(key) };
}
}
Self::Remove(key) => wheel.remove(key),
Self::Cancel(cell) => {
if let Some(key) = unsafe { (*cell.key.get()).take() } {
wheel.remove(key);
}
}
}
}
}