use super::clock::Clock;
use crate::{
intrusive_pairing_heap::{HeapNode, PairingHeap},
utils::update_waker_ref,
NoopLock,
};
use core::{pin::Pin, time::Duration};
use futures_core::{
future::{FusedFuture, Future},
task::{Context, Poll, Waker},
};
use lock_api::{Mutex, RawMutex};
#[derive(PartialEq)]
enum PollState {
Unregistered,
Registered,
Expired,
}
struct TimerQueueEntry {
expiry: u64,
task: Option<Waker>,
state: PollState,
}
impl TimerQueueEntry {
fn new(expiry: u64) -> TimerQueueEntry {
TimerQueueEntry {
expiry,
task: None,
state: PollState::Unregistered,
}
}
}
impl PartialEq for TimerQueueEntry {
fn eq(&self, other: &TimerQueueEntry) -> bool {
self.expiry == other.expiry
}
}
impl Eq for TimerQueueEntry {}
impl PartialOrd for TimerQueueEntry {
fn partial_cmp(
&self,
other: &TimerQueueEntry,
) -> Option<core::cmp::Ordering> {
self.expiry.partial_cmp(&other.expiry)
}
}
impl Ord for TimerQueueEntry {
fn cmp(&self, other: &TimerQueueEntry) -> core::cmp::Ordering {
self.expiry.cmp(&other.expiry)
}
}
struct TimerState {
clock: &'static dyn Clock,
waiters: PairingHeap<TimerQueueEntry>,
}
impl TimerState {
fn new(clock: &'static dyn Clock) -> TimerState {
TimerState {
clock,
waiters: PairingHeap::new(),
}
}
unsafe fn try_wait(
&mut self,
wait_node: &mut HeapNode<TimerQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<()> {
match wait_node.state {
PollState::Unregistered => {
let now = self.clock.now();
if now >= wait_node.expiry {
wait_node.state = PollState::Expired;
Poll::Ready(())
} else {
wait_node.task = Some(cx.waker().clone());
wait_node.state = PollState::Registered;
self.waiters.insert(wait_node);
Poll::Pending
}
}
PollState::Registered => {
update_waker_ref(&mut wait_node.task, cx);
Poll::Pending
}
PollState::Expired => Poll::Ready(()),
}
}
fn remove_waiter(&mut self, wait_node: &mut HeapNode<TimerQueueEntry>) {
if let PollState::Registered = wait_node.state {
unsafe { self.waiters.remove(wait_node) };
wait_node.state = PollState::Unregistered;
}
}
fn next_expiration(&self) -> Option<u64> {
unsafe { self.waiters.peek_min().map(|first| first.as_ref().expiry) }
}
fn check_expirations(&mut self) {
let now = self.clock.now();
while let Some(mut first) = self.waiters.peek_min() {
unsafe {
let entry = first.as_mut();
let first_expiry = entry.expiry;
if now >= first_expiry {
entry.state = PollState::Expired;
if let Some(task) = entry.task.take() {
task.wake();
}
} else {
break;
}
self.waiters.remove(entry);
}
}
}
}
trait TimerAccess {
unsafe fn try_wait(
&self,
wait_node: &mut HeapNode<TimerQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<()>;
fn remove_waiter(&self, wait_node: &mut HeapNode<TimerQueueEntry>);
}
pub trait LocalTimer {
fn delay(&self, delay: Duration) -> LocalTimerFuture;
fn deadline(&self, timestamp: u64) -> LocalTimerFuture;
}
pub trait Timer {
fn delay(&self, delay: Duration) -> TimerFuture;
fn deadline(&self, timestamp: u64) -> TimerFuture;
}
pub struct GenericTimerService<MutexType: RawMutex> {
inner: Mutex<MutexType, TimerState>,
}
unsafe impl<MutexType: RawMutex + Send> Send
for GenericTimerService<MutexType>
{
}
unsafe impl<MutexType: RawMutex + Sync> Sync
for GenericTimerService<MutexType>
{
}
impl<MutexType: RawMutex> core::fmt::Debug for GenericTimerService<MutexType> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("TimerService").finish()
}
}
impl<MutexType: RawMutex> GenericTimerService<MutexType> {
pub fn new(clock: &'static dyn Clock) -> GenericTimerService<MutexType> {
GenericTimerService::<MutexType> {
inner: Mutex::new(TimerState::new(clock)),
}
}
pub fn next_expiration(&self) -> Option<u64> {
self.inner.lock().next_expiration()
}
pub fn check_expirations(&self) {
self.inner.lock().check_expirations()
}
fn deadline_from_now(&self, duration: Duration) -> u64 {
let now = self.inner.lock().clock.now();
let duration_ms =
core::cmp::min(duration.as_millis(), core::u64::MAX as u128) as u64;
now.saturating_add(duration_ms)
}
}
impl<MutexType: RawMutex> LocalTimer for GenericTimerService<MutexType> {
fn delay(&self, delay: Duration) -> LocalTimerFuture {
let deadline = self.deadline_from_now(delay);
LocalTimer::deadline(&*self, deadline)
}
fn deadline(&self, timestamp: u64) -> LocalTimerFuture {
LocalTimerFuture {
timer: Some(self),
wait_node: HeapNode::new(TimerQueueEntry::new(timestamp)),
}
}
}
impl<MutexType: RawMutex> Timer for GenericTimerService<MutexType>
where
MutexType: Sync,
{
fn delay(&self, delay: Duration) -> TimerFuture {
let deadline = self.deadline_from_now(delay);
Timer::deadline(&*self, deadline)
}
fn deadline(&self, timestamp: u64) -> TimerFuture {
TimerFuture {
timer_future: LocalTimerFuture {
timer: Some(self),
wait_node: HeapNode::new(TimerQueueEntry::new(timestamp)),
},
}
}
}
impl<MutexType: RawMutex> TimerAccess for GenericTimerService<MutexType> {
unsafe fn try_wait(
&self,
wait_node: &mut HeapNode<TimerQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<()> {
self.inner.lock().try_wait(wait_node, cx)
}
fn remove_waiter(&self, wait_node: &mut HeapNode<TimerQueueEntry>) {
self.inner.lock().remove_waiter(wait_node)
}
}
#[must_use = "futures do nothing unless polled"]
pub struct LocalTimerFuture<'a> {
timer: Option<&'a dyn TimerAccess>,
wait_node: HeapNode<TimerQueueEntry>,
}
impl<'a> core::fmt::Debug for LocalTimerFuture<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("LocalTimerFuture").finish()
}
}
impl<'a> Future for LocalTimerFuture<'a> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
let mut_self: &mut LocalTimerFuture =
unsafe { Pin::get_unchecked_mut(self) };
let timer =
mut_self.timer.expect("polled TimerFuture after completion");
let poll_res = unsafe { timer.try_wait(&mut mut_self.wait_node, cx) };
if poll_res.is_ready() {
mut_self.timer = None;
}
poll_res
}
}
impl<'a> FusedFuture for LocalTimerFuture<'a> {
fn is_terminated(&self) -> bool {
self.timer.is_none()
}
}
impl<'a> Drop for LocalTimerFuture<'a> {
fn drop(&mut self) {
if let Some(timer) = self.timer {
timer.remove_waiter(&mut self.wait_node);
}
}
}
#[must_use = "futures do nothing unless polled"]
pub struct TimerFuture<'a> {
timer_future: LocalTimerFuture<'a>,
}
unsafe impl<'a> Send for TimerFuture<'a> {}
impl<'a> core::fmt::Debug for TimerFuture<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("TimerFuture").finish()
}
}
impl<'a> Future for TimerFuture<'a> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
let inner_pin = unsafe {
Pin::map_unchecked_mut(self, |fut| &mut fut.timer_future)
};
inner_pin.poll(cx)
}
}
impl<'a> FusedFuture for TimerFuture<'a> {
fn is_terminated(&self) -> bool {
self.timer_future.is_terminated()
}
}
pub type LocalTimerService = GenericTimerService<NoopLock>;
#[cfg(feature = "std")]
mod if_std {
use super::*;
pub type TimerService = GenericTimerService<parking_lot::RawMutex>;
}
#[cfg(feature = "std")]
pub use self::if_std::*;