Description
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);
}
}
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 Instant
s.
Links and related work
- This was previously brought up last year as an alternative to Instant::until, calculating the time until another Instant #55.
- In 2017 it was proposed to use the
_until
suffix instead of_deadline
for functions that do not have meaningful blocking versions such as the thread::sleep family.
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