use std::{
cell::Cell,
future::Future,
pin::Pin,
rc::Rc,
task::{Context, Poll},
time::{Duration, Instant},
};
use crate::executor::current_timer;
use crate::timer::{Timer, TimerHandle};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ZeroBehavior {
Immediate,
Yield,
}
pub struct Sleep {
handle: Option<TimerHandle>,
fired: Cell<bool>,
yield_scheduled: Cell<bool>,
zero_behavior: ZeroBehavior,
deadline: Instant,
timer: Option<Rc<Timer>>,
}
impl Sleep {
#[inline]
pub fn new(duration: Duration) -> Self {
Self {
handle: None,
fired: Cell::new(false),
yield_scheduled: Cell::new(false),
zero_behavior: ZeroBehavior::Immediate,
deadline: Instant::now() + duration,
timer: None,
}
}
#[inline]
pub fn new_with_zero_behavior(duration: Duration, zero_behavior: ZeroBehavior) -> Self {
Self {
handle: None,
fired: Cell::new(false),
yield_scheduled: Cell::new(false),
zero_behavior,
deadline: Instant::now() + duration,
timer: None,
}
}
#[inline]
pub fn sleep_until(deadline: Instant) -> Self {
Self {
handle: None,
fired: Cell::new(false),
yield_scheduled: Cell::new(false),
zero_behavior: ZeroBehavior::Immediate,
deadline,
timer: None,
}
}
#[inline]
pub fn reset(&mut self, deadline: Instant) {
self.deadline = deadline;
self.fired.set(false);
self.yield_scheduled.set(false);
if let Some(handle) = self.handle.take() {
if let Some(timer_rc) = current_timer() {
timer_rc.cancel(handle);
}
}
}
}
impl Future for Sleep {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = unsafe { self.get_unchecked_mut() };
if this.fired.get() {
return Poll::Ready(());
}
if this.handle.is_none() {
let timer_rc = this.timer.get_or_insert_with(|| {
current_timer().expect("Sleep::poll called outside of runtime")
});
let waker = cx.waker().clone();
match timer_rc.submit(this.deadline, waker) {
Some(handle) => {
this.handle = Some(handle);
Poll::Pending
}
None => {
match this.zero_behavior {
ZeroBehavior::Immediate => {
this.fired.set(true);
Poll::Ready(())
}
ZeroBehavior::Yield => {
if !this.yield_scheduled.replace(true) {
cx.waker().wake_by_ref();
Poll::Pending
} else {
this.fired.set(true);
Poll::Ready(())
}
}
}
}
}
} else {
if Instant::now() >= this.deadline {
this.fired.set(true);
this.handle = None;
Poll::Ready(())
} else {
if let Some(handle) = this.handle.take() {
if let Some(timer_rc) = current_timer() {
timer_rc.cancel(handle);
let waker = cx.waker().clone();
match timer_rc.submit(this.deadline, waker) {
Some(handle) => {
this.handle = Some(handle);
return Poll::Pending;
}
None => {
this.fired.set(true);
return Poll::Ready(());
}
}
}
}
Poll::Pending
}
}
}
}
impl Drop for Sleep {
fn drop(&mut self) {
if let Some(handle) = self.handle.take() {
if let Some(timer_rc) = self.timer.take() {
timer_rc.cancel(handle);
}
}
}
}