Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add downcast on Sleep trait #3125

Merged
merged 6 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions benches/support/tokiort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ impl Timer for TokioTimer {
inner: tokio::time::sleep_until(deadline.into()),
})
}

fn reset(&self, sleep: &mut Pin<Box<dyn Sleep>>, new_deadline: Instant) {
if let Some(sleep) = sleep.as_mut().downcast_mut_pin::<TokioSleep>() {
sleep.reset(new_deadline.into())
}
}
}

struct TokioTimeout<T> {
Expand Down Expand Up @@ -75,7 +81,10 @@ impl Future for TokioSleep {
}
}

// Use HasSleep to get tokio::time::Sleep to implement Unpin.
// see https://docs.rs/tokio/latest/tokio/time/struct.Sleep.html

impl Sleep for TokioSleep {}

impl TokioSleep {
pub fn reset(self: Pin<&mut Self>, deadline: Instant) {
self.project().inner.as_mut().reset(deadline.into());
}
}
24 changes: 2 additions & 22 deletions src/rt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@
//! to plug in other runtimes.

pub mod bounds;
mod timer;

use std::{
future::Future,
pin::Pin,
time::{Duration, Instant},
};
pub use timer::{Sleep, Timer};

/// An executor of futures.
///
Expand Down Expand Up @@ -39,20 +36,3 @@ pub trait Executor<Fut> {
/// Place the future into the executor to be run.
fn execute(&self, fut: Fut);
}

/// A timer which provides timer-like functions.
pub trait Timer {
/// Return a future that resolves in `duration` time.
fn sleep(&self, duration: Duration) -> Pin<Box<dyn Sleep>>;

/// Return a future that resolves at `deadline`.
fn sleep_until(&self, deadline: Instant) -> Pin<Box<dyn Sleep>>;

/// Reset a future to resolve at `new_deadline` instead.
fn reset(&self, sleep: &mut Pin<Box<dyn Sleep>>, new_deadline: Instant) {
*sleep = self.sleep_until(new_deadline);
}
}

/// A future returned by a `Timer`.
pub trait Sleep: Send + Sync + Future<Output = ()> {}
127 changes: 127 additions & 0 deletions src/rt/timer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
//! Provides a timer trait with timer-like functions
//!
//! Example using tokio timer:
//! ```rust
//! use std::{
//! pin::Pin,
//! task::{Context, Poll},
//! time::{Duration, Instant},
//! };
//!
//! use futures_util::Future;
//! use pin_project_lite::pin_project;
//! use hyper::rt::{Timer, Sleep};
//!
//! #[derive(Clone, Debug)]
//! pub struct TokioTimer;
//!
//! impl Timer for TokioTimer {
//! fn sleep(&self, duration: Duration) -> Pin<Box<dyn Sleep>> {
//! Box::pin(TokioSleep {
//! inner: tokio::time::sleep(duration),
//! })
//! }
//!
//! fn sleep_until(&self, deadline: Instant) -> Pin<Box<dyn Sleep>> {
//! Box::pin(TokioSleep {
//! inner: tokio::time::sleep_until(deadline.into()),
//! })
//! }
//!
//! fn reset(&self, sleep: &mut Pin<Box<dyn Sleep>>, new_deadline: Instant) {
//! if let Some(sleep) = sleep.as_mut().downcast_mut_pin::<TokioSleep>() {
//! sleep.reset(new_deadline.into())
//! }
//! }
//! }
//!
//! pin_project! {
//! pub(crate) struct TokioSleep {
//! #[pin]
//! pub(crate) inner: tokio::time::Sleep,
//! }
//! }
//!
//! impl Future for TokioSleep {
//! type Output = ();
//!
//! fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
//! self.project().inner.poll(cx)
//! }
//! }
//!
//! impl Sleep for TokioSleep {}
//!
//! impl TokioSleep {
//! pub fn reset(self: Pin<&mut Self>, deadline: Instant) {
//! self.project().inner.as_mut().reset(deadline.into());
//! }
//! }
//! ````

use std::{
any::TypeId,
future::Future,
pin::Pin,
time::{Duration, Instant},
};

/// A timer which provides timer-like functions.
pub trait Timer {
/// Return a future that resolves in `duration` time.
fn sleep(&self, duration: Duration) -> Pin<Box<dyn Sleep>>;

/// Return a future that resolves at `deadline`.
fn sleep_until(&self, deadline: Instant) -> Pin<Box<dyn Sleep>>;

/// Reset a future to resolve at `new_deadline` instead.
fn reset(&self, sleep: &mut Pin<Box<dyn Sleep>>, new_deadline: Instant) {
*sleep = self.sleep_until(new_deadline);
}
}

/// A future returned by a `Timer`.
pub trait Sleep: Send + Sync + Future<Output = ()> {
#[doc(hidden)]
/// This method is private and can not be implemented by downstream crate
fn __type_id(&self, _: private::Sealed) -> TypeId
where
Self: 'static,
{
TypeId::of::<Self>()
}
}

impl dyn Sleep {
//! This is a re-implementation of downcast methods from std::any::Any

/// Check whether the type is the same as `T`
pub fn is<T>(&self) -> bool
where
T: Sleep + 'static,
{
self.__type_id(private::Sealed {}) == TypeId::of::<T>()
}

/// Downcast a pinned &mut Sleep object to its original type
pub fn downcast_mut_pin<T>(self: Pin<&mut Self>) -> Option<Pin<&'static mut T>>
where
T: Sleep + 'static,
{
if self.is::<T>() {
unsafe {
let inner = Pin::into_inner_unchecked(self);
Some(Pin::new_unchecked(
&mut *(&mut *inner as *mut dyn Sleep as *mut T),
))
}
} else {
None
}
}
}

mod private {
#![allow(missing_debug_implementations)]
pub struct Sealed {}
}