Skip to content
This repository has been archived by the owner on Oct 30, 2019. It is now read-only.

Consolidate timer mechanisms #14

Closed
srijs opened this issue Apr 24, 2018 · 10 comments
Closed

Consolidate timer mechanisms #14

srijs opened this issue Apr 24, 2018 · 10 comments

Comments

@srijs
Copy link

srijs commented Apr 24, 2018

This is a follow-up from rust-lang/futures-rs#818.

Timers (and the timeouts they enable) are an important piece of functionality for networking services, enabling them to react to slow running or stalled connections or requests.

Currently, there are two crates that provide futures-based timer functionality, futures-timer and tokio-timer. Both of these crates can define their own "global" default timer, and are generally not interoperable (though they can both be used simultaneously).

The futures-timer crate by default provides a separate thread for handling timer operations, while tokio-timer hooks into the various tokio executors to provide thread-local timer handling (there is some rationale in tokio-rs/tokio-rfcs#2 (comment) for why this might be desirable).

While there might be good reasons to have multiple timer implementations, I believe it would be desirable to align on a single API for providing and consuming timers, so that similar to executors, any timer implementation could be used with any executor and/or event loop.

@srijs srijs changed the title Consolidate timer mechanism Consolidate timer mechanisms Apr 24, 2018
@Ekleog
Copy link

Ekleog commented Nov 5, 2018

My personal reason for wanting that (that appears to not be explicitly mentioned here, even though I guess it's implicit) is that the core logic usually needs only to handle futures, and not IO… but it does require timeouts.

As such, having a pluggable timeout support would be of great help.

An idea to not force this on all implementers may be to support executor-specific futures: parameterize the Future type on the Executor type, and then have some Executors implement a HasTimer trait.

This way Futures could explicitly opt-in to requiring a timer implementation, while keeping open the possibility to eg. have no_std implementations that don't support timers because the hardware doesn't make one easily accessible and it wouldn't be used anyway.

Now, the drawback is that it likely adds some type annotations in the cases that don't need timeouts… but are these that frequent? Anything doing network should have timeouts for almost every future it handles, for instance.

@DoumanAsh
Copy link

DoumanAsh commented Apr 22, 2019

Dead issue, but I thought I'll mention that I worked out some generic interface for timer facilities.

The code is here https://github.com/DoumanAsh/async-timer

The idea is to have platform default timer implementation (e.g. windows thread pools, posix timers and etc) while providing ability for user to supply own implementation.

This I believe would be a better approach: to make timer independent of particular runtime like tokio.
But it would require to work out Timer interface to allow all possible use cases (e.g. timerfd_create that should be polled by event loop)

@yoshuawuyts
Copy link
Collaborator

@DoumanAsh thanks for sharing this! This looks really good! It also shares some similarities to the work @tinaun has been doing in https://github.com/tinaun/futures-native-timers.

In general I feel we'd be very interested in adding platform timers to Romio (as per withoutboats/romio#62), which in turn would allow us to implement timers for runtime (rustasync/runtime#14).

I'm not sure what the best way to go from here is, but timers are coming up frequently, and having cross-platform bindings to them seems important. Would you be willing to help with adding timers to Romio?

@DoumanAsh
Copy link

DoumanAsh commented Apr 23, 2019

@yoshuawuyts I believe timer facilities can be independent of runtime itself.
Only few platforms provide APIs such as timerfd_create that rely on OS polling mechanism.

So I think if Romio is to have timers, it should be implementation of Timer trait rather than full fledged facilities.
Though I still hasn't considered interface for Timer that would consider polling cases like linux's fd timer.

I'll cross-post my idea to that issue too, maybe there will be more input on my idea with Timer interface
I still need to find time to experiment with Linux timers

@yoshuawuyts
Copy link
Collaborator

@DoumanAsh I think where you're trying to generalize inside the async-timer crate, we're trying to generalize inside the runtime crate. Let me check if I'm understanding you correctly.

Our proposal

  • Romio has a native timer implementation
  • Tokio has a hashed wheel timer implementation
  • Runtime provides a uniform interface over the different timer APIs.

Your proposal

  • The async-timer crate contains different implementations for timers
  • The async-timer crate contains a Timer trait that's generic over those timers
  • Romio only uses the Timer trait to be generic over those timers
  • I'm not sure what the story for Tokio is
  • I'm also unsure about how Runtime fits in here

I feel this touches on the question of where we want this "genericness" to exist. I feel that if we want to be generic over any implementation than we can't expect them to implement those traits, but we'll have to wrap them somehow.

Does this seem about right?

@DoumanAsh
Copy link

DoumanAsh commented Apr 23, 2019

The async-timer crate contains different implementations for timers

This is not necessarily true, I only provided common implementations as it might be useful for users that do not want to depend on any runtime.
And Timer trait allows any runtime to provide own optimized implementation that could be re-exported with aliases for Delay and Timed

I'm not sure what the story for Tokio is

In case of fd timer I believe Tokio would adopt approach of independent crate as it is Linux only thingy

I'm also unsure about how Runtime fits in here

Runtime doesn't necessary need to provide anything in case of async-timer approach as implementations of Timer is either in crate itself or in one of the Runtime implementations

I feel that if we want to be generic over any implementation than we can't expect them to implement those traits, but we'll have to wrap them somehow.

I'm bit a unsure who you mean by them?
Is it user or is it Runtime implementation?
In any case the goal of async-timer is to provide facilities for primitives that could be customized by both users and library authors, but also supply with common implementations that would depend only on platform APIs

In case of your proposal, you tie user to runtime crate for generic implementation while in my proposal user is not necessary tied to runtime and is able to customize Timer for its needs.
Latter is necessary for power users and being tied to runtime is not good for such users.

P.s. To clarify: my general proposal to make Timer concept that is independent of Runtime
Timer doesn't require to depend on runtime in general as most platforms provides you with APIs that is not tied to OS async mechanisms.
Therefore I believe approach that ties Timer to runtime is not correct

@yoshuawuyts
Copy link
Collaborator

@DoumanAsh ah yeah, I guess that confirms our philosophies here are very different then. Thanks for the clarification!

@DoumanAsh
Copy link

DoumanAsh commented Apr 24, 2019

@yoshuawuyts Most likely I'll make timer implementation for Linux based on mio with feature-gate for tokio/romio which should help to solidify interface for Timer

After that in general timer related functionality can remain in separate crate I believe, unless you really wish to have own wrapper (considering that both tokio and romio is not going to expose native Timer itself I guess it doesn't really matter to me)

@DoumanAsh
Copy link

Made timerfd implementation with romio https://github.com/DoumanAsh/async-timer/blob/master/src/provider/timer_fd.rs

In the end Timer trait will have to be similar to future as it needs to provide polling mechanism though it is optional for implementations

@DoumanAsh
Copy link

DoumanAsh commented Jul 16, 2019

If this topic is still of interest, I'll share my trait based approach to timer mechanism for 0.2.x:

Timer interface:

pub trait Oneshot: Send + Sync + Unpin + Future<Output = ()> {
    fn new(timeout: Duration) -> Self;
    fn is_ticking(&self) -> bool;
    fn is_expired(&self) -> bool;
    fn cancel(&mut self);
    fn restart(&mut self, timeout: &Duration, waker: &Waker);
}

It completely relies on async story, but it actually can be used in sync code with some boilerplate(you only need to make fake waker that would call your callback)
Also armed with Future::poll rather than instantly on creation

Then there is high level primitive Timed that uses either platform alias or your own timer to provide future that limits execution

The same approach I'm planning to use for any other new primitives (personally didn't have any need for other primitives so I didn't add it)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants