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

Ability to (temporarily) disable time auto-advance when paused #4522

Open
sjoerdsimons opened this issue Feb 21, 2022 · 4 comments
Open

Ability to (temporarily) disable time auto-advance when paused #4522

sjoerdsimons opened this issue Feb 21, 2022 · 4 comments
Labels
A-tokio Area: The main tokio crate C-feature-request Category: A feature request. M-time Module: tokio/time

Comments

@sjoerdsimons
Copy link

The timer wheel auto-advancing while in pause is obviously helpful when tasks blocked on timers however when doing a bit of I/O as well things get quite unpredictable. In my particular case the crate i'm testing runs a interval loop in one task while another task is doing some prep with I/O (only). Depending on timing (hah) this causes the time to auto-advance some of the time but not always

Describe the solution you'd like

The ability to pause time while turning auto-advance on and off again so it's easier for the author to ensure there are no auto-advances for at least a given scope

Describe alternatives you've considered

There really no way to always have the right trade-of when mixing tasks in sleep and tasks in i/o as the runtime/test framework can't be omniscient.. auto-advancing the timer is for sure needed to avoid deadlocks if progress relies on timers expiring, but it gets quite awkward if one only wants those to expire after some I/O bound prep has been done (creating files, reading test data, interacting with a mock).

Additional context

Below is an example showing where the clock can unexpectedly; In real situation both the directory creation and the interval will be hidden deeper in the code that's being tested though

on playground:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e0e11bdc8e604a646186c5e9a4a9422c

relevant code:

let start = Instant::now();
tokio::spawn(async {
    let mut interval = interval(Duration::from_secs(1));
    loop {
        interval.tick().await;
    }
});
  
for i in 0..1000 {
    let _ = tokio::fs::create_dir("/tmp/test").await;
    assert_eq!(start, Instant::now(), "Failed after {} times", i);
}   
@sjoerdsimons sjoerdsimons added A-tokio Area: The main tokio crate C-feature-request Category: A feature request. labels Feb 21, 2022
sjoerdsimons added a commit to sjoerdsimons/tokio that referenced this issue Feb 21, 2022
In some cases one might want to stop the clock from auto-advancing to
allow I/O happen before timers expire. Add an no advance mode to pausing
which can be enabled/disabled to support this mode of operation

fixes tokio-rs#4522
@sjoerdsimons
Copy link
Author

If this feature request seems reasonable i'd love to know if the draft PR is going in the right direction :) Though there is a good chunk of work there still (documentations && tests). But it does solve the issue for me at least

@Darksonn Darksonn added the M-time Module: tokio/time label Feb 21, 2022
@Darksonn
Copy link
Contributor

Regardless of this feature request, it does seem unfortunate that spawn_blocking tasks don't prevent the timer from advancing.

@njam
Copy link
Contributor

njam commented Jul 19, 2022

I'm not a tokio maintainer, but I'd love to see this feature as well.
Would you be able to give feedback on this feature request @Darksonn ?

My use case is to test time-related behaviour in unit tests, for which I'd like to control tokio time precisely, and never have the clock forward automatically.

Basically if I sleep(..) in a paused runtime, I'd like it to wait until the runtime gets advance(..)d the according duration.
For example in this case I'd like the sleep() to wait indefinitely. Instead the function finishes immediately currently.

#[tokio::main(flavor = "current_thread")]
async fn main() {
    tokio::time::pause();

    let instant_tokio = tokio::time::Instant::now();
    let instant_system = std::time::Instant::now();
    
    tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
    
    println!("tokio={:?} system={:?}",instant_tokio.elapsed(), instant_system.elapsed());
}

@Darksonn
Copy link
Contributor

If someone wants to write a PR, then I am ok with adding this.

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
3 participants