-
Notifications
You must be signed in to change notification settings - Fork 633
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
Pre-RFC: Built-in timeout/deadline functionality for Futures #818
Comments
Expanding on this, a struct Timeout {
instant: Instant
}
impl Timeout {
pub fn new(duration: Duration) -> Timeout {
Timeout { instant: Instant::now() + duration }
}
}
impl Future for Timeout {
type Item = ();
type Error = Never;
fn poll(&mut self, cx: &mut task::Context) -> Poll<Self::Item, Self::Error> {
let now = Instant::now();
if now < self.instant {
cx.set_timeout_ms(/* figure out distance in ms between now and self.instant */);
// the futures contract would be expanded, so that when a poll() returns NotReady, it either:
// - must wake up the task at a later point, or
// - must have set a timeout on the context before returning
Ok(Async::NotReady)
} else {
Ok(Async::Ready(()))
}
}
} |
This is how I personally at least interpreted #198 and my conclusion was that this isn't something we should put into the core futures themselves. Is there a reason though that something like |
IMO, the best strategy is going to be some trait that lib authors can use. Tokio will be giving tokio-timer an overhaul and integrating it into the runtime. Specifically, the timer logic will be embedded as part of the work-stealing scheduler such that timers are kept close to the tasks that need them. |
Hmm, somehow #198 wasn't on my radar when proposing this. Based on the discussion happening there and your comments here, it seems to me like this may be a classical 2/3 situation where two out of these three are achievable, but not all:
From my understanding, As someone who's never written an executor, perhaps unsurprisingly, my view was that 2 and 3 are desirable over 1, with the rationale that the number of executors that will be implemented in the futures ecosystem are likely much fewer than the number of users that would like to use futures-based timeouts in their library or application, so therefore the impact of 1 would be smaller. From your comments in the other issue it sounds like you are concerned about the impact of (1), would that be fair to say, @alexcrichton? I'll keep thinking about this, maybe there's a way to achieve all three after all... |
FWIW I'd love to see timeout functionality integrated into futures in a pluggable manner (much like the new executor model). @carllerche and I have worked through API designs that may allow that, but we need to revisit after the dust settles with 0.2. @srijs, I'd love to work with you on this! I don't have time today, but I can try soon to write up a braindump of what we'd explored in the past. |
That sounds great, @aturon, I'd love to help make this happen! I'll continue exploring this avenue here a bit, and once you find the time to write up the brain dump that should provide a better direction to probe :) To understand the broader context a bit better, what was the envisioning like around having different executors? For instance, in which cases would you write your own executor, how many different executors would we generally expect to appear in the ecosystem (order of magnitude?), etc. If there's anything written up on it, please just point me there and I'll go and do my homework! :) |
I wrote this up at one point: tokio-rs/tokio-rfcs#2 (comment) |
Okay, I'm getting back to exploring this today. @carllerche Thanks for the pointer! You wrote this in your comment:
Do I understand this correctly in that you believe timers should be a responsibility of the executor rather than the event loop (or some external logic/thread)? |
There are existing libraries that can be used to add timeouts to futures, but the core |
@cramertj What do you suggest, for a library that wants to add timeouts to the future combinators it generates? |
@Ekleog something like |
@cramertj I'm not sure about And AFAIU So both of those mean that using timeouts would unnecessarily split the ecosystem, either between tokio and not-tokio, or between thread-able and non-thread-able. While the second one sounds much less bad, I still think that we can do a better job abstracting such common things out? Not necessarily built-in That said, I've for a while thought of writing a trait-only crate that'd abstract over the exact executor and only provide only bare capabilities (timeout, network, file, etc.), that each executor would then implement; maybe it'd be better to start this way actually. Never had the time to actually work on that, unfortunately. |
@Ekleog That split already exists: wasm does not support threads, so it cannot use any of the And similarly, we can't use existing timer crates (since they don't work on wasm), so we had to create our own timer system. That's just how it is for some targets, you can't really expect perfect portability. Having said that, I think it would be reasonable for the |
@Pauan I'm not saying we don't have to write executors or timer systems, what I'm saying is it'd be way better for third-party libraries (random interested thought: a library for the SMTP protocol that uses just future combinators and timeouts, plus an optional part for opening network sockets) to not have to say “I won't work on platform X, Y and Z.” Actually your point reinforces mine, I would think? :) |
@Ekleog It sounded like you were against the idea of a |
@Pauan Do you mean having |
@Ekleog Why is that unreasonable? It's quite common: that's what Another example is how we use In fact, that's the standard idiomatic way of handling different targets in Rust (it's literally what It's also not hard at all to use the same implementation for multiple targets, since |
@Pauan What I mean is that, for the similarity with My SMTP library should not have to make a choice between wasm and not-wasm, because it's not something it should be interested in. Another solution to upstreaming all the targets into |
Yes, and? Why is that a problem? The wasm code has to live somewhere, and the best place for it to live is in
And it doesn't need to make that choice. It can just depend on
There's really no need (or benefit) for a trait in this case. It just adds a lot of extra complexity both for the implementation and the users. Of course there's nothing wrong with creating a trait for this, but the
You would still have to make a new Also, the It does not need to make a new implementation for every single target (which you seem to be assuming). Like I said, there won't be any code duplication. The |
@Pauan Based on your latest message, I guess we agree on the problem statement, just not on the solution.
Then why is
This is not more flexible, though, it's more convenient. The trait approach would, as you state, be much less convenient (in particular because there are no default generic arguments yet that work in my experience -- unless things have changed recently), but would not be more flexible.
If it is at all possible to use the For instance, the mere existence of |
So I just finished converting rusoto to futures/tokio, and we'll be cutting a new release soon! 🎉
With that out of the door, I wanted to see if I can feed some ideas back that I had while working on the conversion...
One of the major challenges in that process was implementing timeouts. The design goal was to keep the core of Rusoto independent of any event loop functionality (no direct tokio dependency), and only based it on futures. This made implementing timeouts tricky, since current approaches are either coupled to tokio (
Timeout
intokio-core
) or need to spawn an additional thread (tokio-timer
). This required us to push the complexity of timeout handling into the pluggable parts that do depend on tokio/hyper, but IMO it would have been preferable to separate these concerns.It would be neat if there was some built-in timeout functionality in futures itself, perhaps as part of thread park/wait mechanism. This would enable things like a pure-futures
Timeout
combinator, or perhaps even implementing some idle timeout tracking forAsyncRead
. If there is general interest to support something like it, I'd be willing to write up an RFC to facilitate more discussion around how to approach it.Familiarising myself with the futures 0.2 architecture, I came up with the following straw-man proposal (I'm confident there are much better ways to achieve this). I thought I'd just float this and see if something sticks :)
Let me know what you think! (and please also don't hesitate to tell me if I'm way off track here)
Staw-man Proposal
Changes to
futures-core
The idea here is to keep track of a deadline/timeout on the task context.
Changes to
futures-executor
Executors will now need to be aware of the timeout setting on the Waker, and incorporate that into their parking logic.
Example for
local-pool.rs
Alternatives
An alternative could be to extend the
Async<T>
enum to beAsync::Ready(T)
orAsync::NotReady(Option<Duration>)
. That would avoid having to move the timeout as atomics intoWaker
, but on the downside it complicates the futures contract for the 90% of futures that don't need timeouts.The text was updated successfully, but these errors were encountered: