Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions library/std/src/sys/thread/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,26 @@ pub fn sleep(dur: Duration) {
pub fn sleep_until(deadline: crate::time::Instant) {
use crate::time::Instant;

let timespec = deadline.into_inner().into_timespec();
if timespec.tv_sec < 0 {
// `clock_nanosleep` fails with EINVAL if
// > The tp argument to clock_settime() is outside the range for the
// > given clock ID.
//
// This specification allows *any* clock range, which means we'd
// theoretically have to detect whether the time point is in the
// future (and block indefinitely) or the past (and return immediately)
// when encountering `EINVAL`. But since all existing implementations
// interpret this as saying that negative `tv_sec` values are unsupported,
// we can just test that and return – given that POSIX specifies that
// `CLOCK_MONOTONIC` measures the time "since an unspecified amount
// in the past" negative values are definitely in the past. If you
// observe any platform returning `EINVAL` for more cases, please
// file a bug; we'd need to add logic handling `EINVAL` when it
// occurs.
return;
}

#[cfg(all(
target_os = "linux",
target_env = "gnu",
Expand All @@ -672,7 +692,7 @@ pub fn sleep_until(deadline: crate::time::Instant) {
}

if let Some(clock_nanosleep) = __clock_nanosleep_time64.get() {
let ts = deadline.into_inner().into_timespec().to_timespec64();
let ts = timespec.to_timespec64();
loop {
let r = unsafe {
clock_nanosleep(
Expand Down Expand Up @@ -700,7 +720,7 @@ pub fn sleep_until(deadline: crate::time::Instant) {
}
}

let Some(ts) = deadline.into_inner().into_timespec().to_timespec() else {
let Some(ts) = timespec.to_timespec() else {
// The deadline is further in the future then can be passed to
// clock_nanosleep. We have to use Self::sleep instead. This might
// happen on 32 bit platforms, especially closer to 2038.
Expand Down
7 changes: 4 additions & 3 deletions library/std/src/thread/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,10 @@ pub fn sleep(dur: Duration) {

/// Puts the current thread to sleep until the specified deadline has passed.
///
/// The thread may still be asleep after the deadline specified due to
/// scheduling specifics or platform-dependent functionality. It will never
/// wake before.
/// If the deadline has already passed at the time this function is called, it
/// will return immediately. Note that the thread may still be asleep after the
/// deadline specified due to scheduling specifics or platform-dependent
/// functionality. It will never wake before.
///
/// This function is blocking, and should not be used in `async` functions.
///
Expand Down
9 changes: 9 additions & 0 deletions library/std/src/thread/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,15 @@ fn sleep_ms_smoke() {
thread::sleep(Duration::from_millis(2));
}

#[test]
fn sleep_until_elapsed() {
// UNIX's `clock_nanosleep` doesn't like timeouts that are too far back.
// Test that `sleep_until` returns immediately instead of panicking.
// Going 10 years back should be enough to trigger any errors.
let earlier = Instant::now() - Duration::from_secs(10 * 365 * 24 * 3600);
thread::sleep_until(earlier);
}

#[test]
fn test_size_of_option_thread_id() {
assert_eq!(size_of::<Option<ThreadId>>(), size_of::<ThreadId>());
Expand Down
Loading