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

Allow scheduling a tokio::time::Interval to tick as soon as possible or after a custom time? #5874

Closed
danya02 opened this issue Jul 16, 2023 · 4 comments · Fixed by #5878
Closed
Labels
A-tokio Area: The main tokio crate C-feature-request Category: A feature request. M-time Module: tokio/time

Comments

@danya02
Copy link

danya02 commented Jul 16, 2023

Is your feature request related to a problem? Please describe.
I was writing a message handler for an app that has some state. Clients connecting to it should receive state updates periodically, but also on every state change. I had something like this:

let mut foo_update_int = tokio::time::interval(Duration::from_secs(1));
let mut bar_update_int = tokio::time::interval(Duration::from_secs(5));

tokio::select! {
    _ = foo_update_int.tick() => {
        let current_foo_state = todo!();
        foo_state_tx.send(current_foo_state).await?;
    },
    _ = bar_update_int.tick() => {
        let current_bar_state = todo!();
        bar_state_tx.send(current_bar_state).await?;
    },
    event = event_queue.recv() => {
        match event {
            DbEvent::FooUpdate => {
                // ...update the foo...
                // now send the new foo's state?
                foo_update_int.reset();
            },
            DbEvent::BarUpdate => {
                // ...update the bar...
                // now send the new bar's state?
                bar_update_int.reset();
            },
     }
}

However, this doesn't accomplish the goal of making sure that the update code runs as fast as possible; in fact it delays it until now + interval.

Describe the solution you'd like
The implementation of reset() calls the tokio::time::sleep::Sleep::reset with a new deadline of (now + self.period).
Perhaps there could be an option to schedule the next tick to happen at exactly now, or perhaps with a customizable delay, like:

foo_update_int.reset_immediately();
foo_update_int.reset_after(Duration::from_millis(100));
foo_update_int.reset_at(Instant::now() + Duration::from_millis(100));  // acts like reset_immediately() if argument is in the past

Describe alternatives you've considered
For my particular case, I could run the regular reset(), and then manually do the same things as the tick() clause would have done.
This may not be easy in all cases however, because the logic there may be complex and not easy to refactor out into a function or closure, and copying it into each such reset site would get hard to maintain.

As for the proposal of a custom time for the next tick, you could perhaps do that with a mutable Instant variable you keep around, and the tick handler would check that the Instant is in the past before running the main update code. Some clever extra logic around resetting the main Interval at strategic moments may ensure that the tick happens not too far into the future as compared to the Instant, but I can't figure out what that should be right away.

@danya02 danya02 added A-tokio Area: The main tokio crate C-feature-request Category: A feature request. labels Jul 16, 2023
@Darksonn Darksonn added the M-time Module: tokio/time label Jul 17, 2023
@Darksonn
Copy link
Contributor

Seems reasonable enough to me.

@Darksonn Darksonn added the E-help-wanted Call for participation: Help is requested to fix this issue. label Jul 17, 2023
@victor-timofei
Copy link
Contributor

I would like to help with this. Would this be a good first issue?

@Darksonn
Copy link
Contributor

Yes, it's a good first issue.

victor-timofei added a commit to victor-timofei/tokio that referenced this issue Jul 17, 2023
victor-timofei added a commit to victor-timofei/tokio that referenced this issue Jul 18, 2023
@danya02
Copy link
Author

danya02 commented Jul 19, 2023

I just figured out that, in a pinch, you could emulate the behavior of my reset_immediately with:

foo_update_int = tokio::time::interval(foo_update_int.period());

which works because in order to tick() the Interval, you need to have at least a mutable reference to it, and are usually the owner of it, and both of those allow you to outright replace the Interval with a new one; meanwhile, the interval constructor function produces intervals that tick immediately, and there's also an interval_at if you want to delay the first tick like my reset_at.

Still, having these be features of the Interval itself would mean that you don't need to drop and recreate one. The Interval has a Box<Sleep> inside it, and dropping and recreating that requires a round-trip to the heap allocator which can be avoided. Also, ergonomics for when someone else has the same need as me, so that they don't have to figure this out again.

victor-timofei added a commit to victor-timofei/tokio that referenced this issue Aug 1, 2023
victor-timofei added a commit to victor-timofei/tokio that referenced this issue Aug 3, 2023
@Darksonn Darksonn removed the E-help-wanted Call for participation: Help is requested to fix this issue. label Aug 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-tokio Area: The main tokio crate C-feature-request Category: A feature request. M-time Module: tokio/time
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants