Skip to content

thread::sleep_until, wait until a deadline is reached #237

Closed
@dvdsk

Description

@dvdsk

Implemented; see tracking issue: rust-lang/rust#113752

Proposal

Problem statement

There is no method to pause a thread until a deadline is reached. Currently that means writing one yourself. I argue the use case is common enough that it is worth it to bring this into the std. It would make code that needs this slightly shorter, a bit more readable and easier to write.

Last but not least while a pause until method is not hard to write it is easy to do incorrectly without noticing. A naive approach subtracts the deadline from Instant::now(). Currently, this works, however it might panic in the future see Monotonicity and Instant::sub. Adding sleep_until lessens the impact of future rust versions panicking again a bit.

Motivating examples or use cases

Most use-cases take the shape of a planning problem, performing some action at a set moment. For example today I wanted to use sleep_until to exponentially back off on requesting a certificate up to a deadline. Like any web request it may take a while before it fails, therefore we must take the time the work takes into account.

const MAX_DURATION: Duration = Duration::from_secs(10);
let mut next_attempt = Instant::now();
let deadline = Instant::now() + MAX_DURATION;
let mut delay = Duration::from_millis(250);

loop {
    if Instant::now() > deadline {
        break Err(()),
    }
    // get_signed_certificate also takes some time
    if let Ready(cert) = get_signed_certificate() {
        break Ok(cert),
    }

    delay *= 2;
    next_attempt = deadline.min(next_attempt + delay);
    sleep_until(next_attempt);
}

This can be generalized by replacing get_signed_certificate with any fallible function that takes a non-trivial amount of time.

On GitHub there are about 3k hits for rust code containing fn sleep_until. Though a minor argument, having this in the std could help porting async code using tokios sleep_until easier.

Solution sketch

I propose we make the solution look like the sleep_until function in tokio::time::sleep_until.

The API:

pub fn sleep_until(deadline: Instant);

The implementation:

// proposed implementation
pub fn sleep_until(deadline: Instant) {
    let now = Instant::now();
    
    // if now is after the deadline do not sleep
    if let Some(delay) = deadline.checked_duration_since(now) {
        thread::sleep(delay);        
    }
}

playground link

Alternatives

If we chose not to implement sleep_until an example should be added to thread::sleep to steer users away from using subtract on two Instants.

Links and related work

What happens now?

This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.

if the libs-api team decides to adopt this proposal I would like to implement the RFC

Metadata

Metadata

Assignees

No one assigned

    Labels

    ACP-acceptedAPI Change Proposal is accepted (seconded with no objections)T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions