From d92d3917d950e4c61c37c2170f3ce273d2a0f7d1 Mon Sep 17 00:00:00 2001 From: dswij Date: Thu, 6 Jul 2023 21:34:42 +0800 Subject: [PATCH] feat(rt): add downcast on `Sleep` trait (#3125) This commit allows downcasting pinned `Sleep` object to support implementations of `Timer::reset`. One example where this is useful is when using `TokioSleep`, i.e. `Sleep` provided by tokio. Closes #3027 --- benches/support/tokiort.rs | 15 ++++- src/rt/mod.rs | 24 +------ src/rt/timer.rs | 127 +++++++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 25 deletions(-) create mode 100644 src/rt/timer.rs diff --git a/benches/support/tokiort.rs b/benches/support/tokiort.rs index 67ae3a91aa..4708bd67a1 100644 --- a/benches/support/tokiort.rs +++ b/benches/support/tokiort.rs @@ -41,6 +41,12 @@ impl Timer for TokioTimer { inner: tokio::time::sleep_until(deadline.into()), }) } + + fn reset(&self, sleep: &mut Pin>, new_deadline: Instant) { + if let Some(sleep) = sleep.as_mut().downcast_mut_pin::() { + sleep.reset(new_deadline.into()) + } + } } struct TokioTimeout { @@ -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()); + } +} diff --git a/src/rt/mod.rs b/src/rt/mod.rs index 803d010f40..82854a546c 100644 --- a/src/rt/mod.rs +++ b/src/rt/mod.rs @@ -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. /// @@ -39,20 +36,3 @@ pub trait Executor { /// 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>; - - /// Return a future that resolves at `deadline`. - fn sleep_until(&self, deadline: Instant) -> Pin>; - - /// Reset a future to resolve at `new_deadline` instead. - fn reset(&self, sleep: &mut Pin>, new_deadline: Instant) { - *sleep = self.sleep_until(new_deadline); - } -} - -/// A future returned by a `Timer`. -pub trait Sleep: Send + Sync + Future {} diff --git a/src/rt/timer.rs b/src/rt/timer.rs new file mode 100644 index 0000000000..6ecb964373 --- /dev/null +++ b/src/rt/timer.rs @@ -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::pin(TokioSleep { +//! inner: tokio::time::sleep(duration), +//! }) +//! } +//! +//! fn sleep_until(&self, deadline: Instant) -> Pin> { +//! Box::pin(TokioSleep { +//! inner: tokio::time::sleep_until(deadline.into()), +//! }) +//! } +//! +//! fn reset(&self, sleep: &mut Pin>, new_deadline: Instant) { +//! if let Some(sleep) = sleep.as_mut().downcast_mut_pin::() { +//! 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.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>; + + /// Return a future that resolves at `deadline`. + fn sleep_until(&self, deadline: Instant) -> Pin>; + + /// Reset a future to resolve at `new_deadline` instead. + fn reset(&self, sleep: &mut Pin>, new_deadline: Instant) { + *sleep = self.sleep_until(new_deadline); + } +} + +/// A future returned by a `Timer`. +pub trait Sleep: Send + Sync + Future { + #[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::() + } +} + +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(&self) -> bool + where + T: Sleep + 'static, + { + self.__type_id(private::Sealed {}) == TypeId::of::() + } + + /// Downcast a pinned &mut Sleep object to its original type + pub fn downcast_mut_pin(self: Pin<&mut Self>) -> Option> + where + T: Sleep + 'static, + { + if self.is::() { + 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 {} +}