about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-07-06 23:00:51 +0000
committerbors <bors@rust-lang.org>2025-07-06 23:00:51 +0000
commitca98d4d4b3114116203699c2734805547df86f9a (patch)
treeeba29b872a9215bd792841f900e746d5d088b0af
parenta84ab0ce6c4557a2f01a3a6c3fdb0f92098db78d (diff)
parent61cf174dcea9e606d7c3e89c2df69e22ae888728 (diff)
downloadrust-ca98d4d4b3114116203699c2734805547df86f9a.tar.gz
rust-ca98d4d4b3114116203699c2734805547df86f9a.zip
Auto merge of #141829 - dvdsk:sleep_until_linux, r=cuviper,RalfJung
Specialize sleep_until implementation for unix (except mac)

related tracking issue: https://github.com/rust-lang/rust/issues/113752
Supersedes https://github.com/rust-lang/rust/pull/118480 for the reasons see: https://github.com/rust-lang/rust/issues/113752#issuecomment-2902594469

Replaces the generic catch all implementation with target_os specific ones for: linux/netbsd/freebsd/android/solaris/illumos etc. Other platforms like wasi, macos/ios/tvos/watchos and windows will follow in later separate PR's (once this is merged).
-rw-r--r--library/std/src/sys/pal/hermit/thread.rs10
-rw-r--r--library/std/src/sys/pal/itron/thread.rs10
-rw-r--r--library/std/src/sys/pal/sgx/thread.rs10
-rw-r--r--library/std/src/sys/pal/teeos/thread.rs10
-rw-r--r--library/std/src/sys/pal/uefi/thread.rs10
-rw-r--r--library/std/src/sys/pal/unix/thread.rs72
-rw-r--r--library/std/src/sys/pal/unix/time.rs18
-rw-r--r--library/std/src/sys/pal/unsupported/thread.rs6
-rw-r--r--library/std/src/sys/pal/wasi/thread.rs10
-rw-r--r--library/std/src/sys/pal/wasm/atomics/thread.rs10
-rw-r--r--library/std/src/sys/pal/windows/thread.rs10
-rw-r--r--library/std/src/sys/pal/xous/thread.rs10
-rw-r--r--library/std/src/thread/mod.rs35
-rw-r--r--library/std/src/time.rs9
-rw-r--r--library/std/tests/thread.rs14
-rw-r--r--src/tools/miri/src/shims/time.rs57
-rw-r--r--src/tools/miri/src/shims/unix/foreign_items.rs11
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-time.rs114
-rw-r--r--src/tools/miri/tests/pass/shims/time.rs10
19 files changed, 411 insertions, 25 deletions
diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs
index bb68a824fc3..9bc5a16b800 100644
--- a/library/std/src/sys/pal/hermit/thread.rs
+++ b/library/std/src/sys/pal/hermit/thread.rs
@@ -4,7 +4,7 @@ use super::hermit_abi;
 use crate::ffi::CStr;
 use crate::mem::ManuallyDrop;
 use crate::num::NonZero;
-use crate::time::Duration;
+use crate::time::{Duration, Instant};
 use crate::{io, ptr};
 
 pub type Tid = hermit_abi::Tid;
@@ -86,6 +86,14 @@ impl Thread {
         }
     }
 
+    pub fn sleep_until(deadline: Instant) {
+        let now = Instant::now();
+
+        if let Some(delay) = deadline.checked_duration_since(now) {
+            Self::sleep(delay);
+        }
+    }
+
     pub fn join(self) {
         unsafe {
             let _ = hermit_abi::join(self.tid);
diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs
index a974f4f17ae..813e1cbcd58 100644
--- a/library/std/src/sys/pal/itron/thread.rs
+++ b/library/std/src/sys/pal/itron/thread.rs
@@ -10,7 +10,7 @@ use crate::mem::ManuallyDrop;
 use crate::num::NonZero;
 use crate::ptr::NonNull;
 use crate::sync::atomic::{Atomic, AtomicUsize, Ordering};
-use crate::time::Duration;
+use crate::time::{Duration, Instant};
 use crate::{hint, io};
 
 pub struct Thread {
@@ -205,6 +205,14 @@ impl Thread {
         }
     }
 
+    pub fn sleep_until(deadline: Instant) {
+        let now = Instant::now();
+
+        if let Some(delay) = deadline.checked_duration_since(now) {
+            Self::sleep(delay);
+        }
+    }
+
     pub fn join(self) {
         // Safety: `ThreadInner` is alive at this point
         let inner = unsafe { self.p_inner.as_ref() };
diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs
index 219ef1b7a98..85f6dcd96b4 100644
--- a/library/std/src/sys/pal/sgx/thread.rs
+++ b/library/std/src/sys/pal/sgx/thread.rs
@@ -5,7 +5,7 @@ use super::unsupported;
 use crate::ffi::CStr;
 use crate::io;
 use crate::num::NonZero;
-use crate::time::Duration;
+use crate::time::{Duration, Instant};
 
 pub struct Thread(task_queue::JoinHandle);
 
@@ -132,6 +132,14 @@ impl Thread {
         usercalls::wait_timeout(0, dur, || true);
     }
 
+    pub fn sleep_until(deadline: Instant) {
+        let now = Instant::now();
+
+        if let Some(delay) = deadline.checked_duration_since(now) {
+            Self::sleep(delay);
+        }
+    }
+
     pub fn join(self) {
         self.0.wait();
     }
diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs
index e3b4908f858..b9cdc7a2a58 100644
--- a/library/std/src/sys/pal/teeos/thread.rs
+++ b/library/std/src/sys/pal/teeos/thread.rs
@@ -2,7 +2,7 @@ use crate::ffi::CStr;
 use crate::mem::{self, ManuallyDrop};
 use crate::num::NonZero;
 use crate::sys::os;
-use crate::time::Duration;
+use crate::time::{Duration, Instant};
 use crate::{cmp, io, ptr};
 
 pub const DEFAULT_MIN_STACK_SIZE: usize = 8 * 1024;
@@ -109,6 +109,14 @@ impl Thread {
         }
     }
 
+    pub fn sleep_until(deadline: Instant) {
+        let now = Instant::now();
+
+        if let Some(delay) = deadline.checked_duration_since(now) {
+            Self::sleep(delay);
+        }
+    }
+
     /// must join, because no pthread_detach supported
     pub fn join(self) {
         let id = self.into_id();
diff --git a/library/std/src/sys/pal/uefi/thread.rs b/library/std/src/sys/pal/uefi/thread.rs
index 7d4006ff4b2..e4776ec42fb 100644
--- a/library/std/src/sys/pal/uefi/thread.rs
+++ b/library/std/src/sys/pal/uefi/thread.rs
@@ -3,7 +3,7 @@ use crate::ffi::CStr;
 use crate::io;
 use crate::num::NonZero;
 use crate::ptr::NonNull;
-use crate::time::Duration;
+use crate::time::{Duration, Instant};
 
 pub struct Thread(!);
 
@@ -39,6 +39,14 @@ impl Thread {
         }
     }
 
+    pub fn sleep_until(deadline: Instant) {
+        let now = Instant::now();
+
+        if let Some(delay) = deadline.checked_duration_since(now) {
+            Self::sleep(delay);
+        }
+    }
+
     pub fn join(self) {
         self.0
     }
diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs
index d8b189413f4..53f0d1eeda5 100644
--- a/library/std/src/sys/pal/unix/thread.rs
+++ b/library/std/src/sys/pal/unix/thread.rs
@@ -6,7 +6,7 @@ use crate::sys::weak::dlsym;
 #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto",))]
 use crate::sys::weak::weak;
 use crate::sys::{os, stack_overflow};
-use crate::time::Duration;
+use crate::time::{Duration, Instant};
 use crate::{cmp, io, ptr};
 #[cfg(not(any(
     target_os = "l4re",
@@ -296,6 +296,76 @@ impl Thread {
         }
     }
 
+    // Any unix that has clock_nanosleep
+    // If this list changes update the MIRI chock_nanosleep shim
+    #[cfg(any(
+        target_os = "freebsd",
+        target_os = "netbsd",
+        target_os = "linux",
+        target_os = "android",
+        target_os = "solaris",
+        target_os = "illumos",
+        target_os = "dragonfly",
+        target_os = "hurd",
+        target_os = "fuchsia",
+        target_os = "vxworks",
+    ))]
+    pub fn sleep_until(deadline: Instant) {
+        let Some(ts) = deadline.into_inner().into_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.
+            let now = Instant::now();
+            if let Some(delay) = deadline.checked_duration_since(now) {
+                Self::sleep(delay);
+            }
+            return;
+        };
+
+        unsafe {
+            // When we get interrupted (res = EINTR) call clock_nanosleep again
+            loop {
+                let res = libc::clock_nanosleep(
+                    super::time::Instant::CLOCK_ID,
+                    libc::TIMER_ABSTIME,
+                    &ts,
+                    core::ptr::null_mut(), // not required with TIMER_ABSTIME
+                );
+
+                if res == 0 {
+                    break;
+                } else {
+                    assert_eq!(
+                        res,
+                        libc::EINTR,
+                        "timespec is in range,
+                         clockid is valid and kernel should support it"
+                    );
+                }
+            }
+        }
+    }
+
+    // Any unix that does not have clock_nanosleep
+    #[cfg(not(any(
+        target_os = "freebsd",
+        target_os = "netbsd",
+        target_os = "linux",
+        target_os = "android",
+        target_os = "solaris",
+        target_os = "illumos",
+        target_os = "dragonfly",
+        target_os = "hurd",
+        target_os = "fuchsia",
+        target_os = "vxworks",
+    )))]
+    pub fn sleep_until(deadline: Instant) {
+        let now = Instant::now();
+        if let Some(delay) = deadline.checked_duration_since(now) {
+            Self::sleep(delay);
+        }
+    }
+
     pub fn join(self) {
         let id = self.into_id();
         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs
index 0074d767474..bd7f74fea6a 100644
--- a/library/std/src/sys/pal/unix/time.rs
+++ b/library/std/src/sys/pal/unix/time.rs
@@ -261,6 +261,10 @@ pub struct Instant {
 }
 
 impl Instant {
+    #[cfg(target_vendor = "apple")]
+    pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_UPTIME_RAW;
+    #[cfg(not(target_vendor = "apple"))]
+    pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_MONOTONIC;
     pub fn now() -> Instant {
         // https://www.manpagez.com/man/3/clock_gettime/
         //
@@ -273,11 +277,7 @@ impl Instant {
         //
         // Instant on macos was historically implemented using mach_absolute_time;
         // we preserve this value domain out of an abundance of caution.
-        #[cfg(target_vendor = "apple")]
-        const clock_id: libc::clockid_t = libc::CLOCK_UPTIME_RAW;
-        #[cfg(not(target_vendor = "apple"))]
-        const clock_id: libc::clockid_t = libc::CLOCK_MONOTONIC;
-        Instant { t: Timespec::now(clock_id) }
+        Instant { t: Timespec::now(Self::CLOCK_ID) }
     }
 
     pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
@@ -291,6 +291,14 @@ impl Instant {
     pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
         Some(Instant { t: self.t.checked_sub_duration(other)? })
     }
+
+    #[cfg_attr(
+        not(target_os = "linux"),
+        allow(unused, reason = "needed by the `sleep_until` on some unix platforms")
+    )]
+    pub(crate) fn into_timespec(self) -> Timespec {
+        self.t
+    }
 }
 
 impl fmt::Debug for Instant {
diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/pal/unsupported/thread.rs
index 89f8bad7026..8a3119fa292 100644
--- a/library/std/src/sys/pal/unsupported/thread.rs
+++ b/library/std/src/sys/pal/unsupported/thread.rs
@@ -2,7 +2,7 @@ use super::unsupported;
 use crate::ffi::CStr;
 use crate::io;
 use crate::num::NonZero;
-use crate::time::Duration;
+use crate::time::{Duration, Instant};
 
 pub struct Thread(!);
 
@@ -26,6 +26,10 @@ impl Thread {
         panic!("can't sleep");
     }
 
+    pub fn sleep_until(_deadline: Instant) {
+        panic!("can't sleep");
+    }
+
     pub fn join(self) {
         self.0
     }
diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs
index cc569bb3daf..5f21a553673 100644
--- a/library/std/src/sys/pal/wasi/thread.rs
+++ b/library/std/src/sys/pal/wasi/thread.rs
@@ -2,7 +2,7 @@
 
 use crate::ffi::CStr;
 use crate::num::NonZero;
-use crate::time::Duration;
+use crate::time::{Duration, Instant};
 use crate::{io, mem};
 
 cfg_if::cfg_if! {
@@ -171,6 +171,14 @@ impl Thread {
         }
     }
 
+    pub fn sleep_until(deadline: Instant) {
+        let now = Instant::now();
+
+        if let Some(delay) = deadline.checked_duration_since(now) {
+            Self::sleep(delay);
+        }
+    }
+
     pub fn join(self) {
         cfg_if::cfg_if! {
             if #[cfg(target_feature = "atomics")] {
diff --git a/library/std/src/sys/pal/wasm/atomics/thread.rs b/library/std/src/sys/pal/wasm/atomics/thread.rs
index dd5aff391fd..44ce3eab109 100644
--- a/library/std/src/sys/pal/wasm/atomics/thread.rs
+++ b/library/std/src/sys/pal/wasm/atomics/thread.rs
@@ -2,7 +2,7 @@ use crate::ffi::CStr;
 use crate::io;
 use crate::num::NonZero;
 use crate::sys::unsupported;
-use crate::time::Duration;
+use crate::time::{Duration, Instant};
 
 pub struct Thread(!);
 
@@ -41,6 +41,14 @@ impl Thread {
         }
     }
 
+    pub fn sleep_until(deadline: Instant) {
+        let now = Instant::now();
+
+        if let Some(delay) = deadline.checked_duration_since(now) {
+            Self::sleep(delay);
+        }
+    }
+
     pub fn join(self) {}
 }
 
diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs
index 45e52cf4d04..14785171755 100644
--- a/library/std/src/sys/pal/windows/thread.rs
+++ b/library/std/src/sys/pal/windows/thread.rs
@@ -8,7 +8,7 @@ use crate::os::windows::io::{AsRawHandle, HandleOrNull};
 use crate::sys::handle::Handle;
 use crate::sys::{c, stack_overflow};
 use crate::sys_common::FromInner;
-use crate::time::Duration;
+use crate::time::{Duration, Instant};
 use crate::{io, ptr};
 
 pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
@@ -106,6 +106,14 @@ impl Thread {
         }
     }
 
+    pub fn sleep_until(deadline: Instant) {
+        let now = Instant::now();
+
+        if let Some(delay) = deadline.checked_duration_since(now) {
+            Self::sleep(delay);
+        }
+    }
+
     pub fn handle(&self) -> &Handle {
         &self.handle
     }
diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/pal/xous/thread.rs
index 0ebb46dc19f..1b344e984dc 100644
--- a/library/std/src/sys/pal/xous/thread.rs
+++ b/library/std/src/sys/pal/xous/thread.rs
@@ -8,7 +8,7 @@ use crate::os::xous::ffi::{
     map_memory, update_memory_flags,
 };
 use crate::os::xous::services::{TicktimerScalar, ticktimer_server};
-use crate::time::Duration;
+use crate::time::{Duration, Instant};
 
 pub struct Thread {
     tid: ThreadId,
@@ -128,6 +128,14 @@ impl Thread {
         }
     }
 
+    pub fn sleep_until(deadline: Instant) {
+        let now = Instant::now();
+
+        if let Some(delay) = deadline.checked_duration_since(now) {
+            Self::sleep(delay);
+        }
+    }
+
     pub fn join(self) {
         join_thread(self.tid).unwrap();
     }
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 26b2fb44724..6075173db47 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -897,8 +897,31 @@ pub fn sleep(dur: Duration) {
 ///
 /// # Platform-specific behavior
 ///
-/// This function uses [`sleep`] internally, see its platform-specific behavior.
+/// In most cases this function will call an OS specific function. Where that
+/// is not supported [`sleep`] is used. Those platforms are referred to as other
+/// in the table below.
 ///
+/// # Underlying System calls
+///
+/// The following system calls are [currently] being used:
+///
+/// |  Platform |               System call                                            |
+/// |-----------|----------------------------------------------------------------------|
+/// | Linux     | [clock_nanosleep] (Monotonic clock)                                  |
+/// | BSD except OpenBSD | [clock_nanosleep] (Monotonic Clock)]                        |
+/// | Android   | [clock_nanosleep] (Monotonic Clock)]                                 |
+/// | Solaris   | [clock_nanosleep] (Monotonic Clock)]                                 |
+/// | Illumos   | [clock_nanosleep] (Monotonic Clock)]                                 |
+/// | Dragonfly | [clock_nanosleep] (Monotonic Clock)]                                 |
+/// | Hurd      | [clock_nanosleep] (Monotonic Clock)]                                 |
+/// | Fuchsia   | [clock_nanosleep] (Monotonic Clock)]                                 |
+/// | Vxworks   | [clock_nanosleep] (Monotonic Clock)]                                 |
+/// | Other     | `sleep_until` uses [`sleep`] and does not issue a syscall itself     |
+///
+/// [currently]: crate::io#platform-specific-behavior
+/// [clock_nanosleep]: https://linux.die.net/man/3/clock_nanosleep
+///
+/// **Disclaimer:** These system calls might change over time.
 ///
 /// # Examples
 ///
@@ -923,9 +946,9 @@ pub fn sleep(dur: Duration) {
 /// }
 /// ```
 ///
-/// A slow api we must not call too fast and which takes a few
+/// A slow API we must not call too fast and which takes a few
 /// tries before succeeding. By using `sleep_until` the time the
-/// api call takes does not influence when we retry or when we give up
+/// API call takes does not influence when we retry or when we give up
 ///
 /// ```no_run
 /// #![feature(thread_sleep_until)]
@@ -960,11 +983,7 @@ pub fn sleep(dur: Duration) {
 /// ```
 #[unstable(feature = "thread_sleep_until", issue = "113752")]
 pub fn sleep_until(deadline: Instant) {
-    let now = Instant::now();
-
-    if let Some(delay) = deadline.checked_duration_since(now) {
-        sleep(delay);
-    }
+    imp::Thread::sleep_until(deadline)
 }
 
 /// Used to ensure that `park` and `park_timeout` do not unwind, as that can
diff --git a/library/std/src/time.rs b/library/std/src/time.rs
index 393426be087..cd0683f44c9 100644
--- a/library/std/src/time.rs
+++ b/library/std/src/time.rs
@@ -407,6 +407,15 @@ impl Instant {
     pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
         self.0.checked_sub_duration(&duration).map(Instant)
     }
+
+    // Used by platform specific `sleep_until` implementations such as the one used on Linux.
+    #[cfg_attr(
+        not(target_os = "linux"),
+        allow(unused, reason = "not every platform has a specific `sleep_until`")
+    )]
+    pub(crate) fn into_inner(self) -> time::Instant {
+        self.0
+    }
 }
 
 #[stable(feature = "time2", since = "1.8.0")]
diff --git a/library/std/tests/thread.rs b/library/std/tests/thread.rs
index 1bb17d149fa..32561dd6ab6 100644
--- a/library/std/tests/thread.rs
+++ b/library/std/tests/thread.rs
@@ -1,7 +1,8 @@
+#![feature(thread_sleep_until)]
 use std::cell::{Cell, RefCell};
 use std::sync::{Arc, Mutex};
 use std::thread;
-use std::time::Duration;
+use std::time::{Duration, Instant};
 
 #[test]
 #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
@@ -18,6 +19,17 @@ fn sleep_very_long() {
 }
 
 #[test]
+fn sleep_until() {
+    let now = Instant::now();
+    let period = Duration::from_millis(100);
+    let deadline = now + period;
+    thread::sleep_until(deadline);
+
+    let elapsed = now.elapsed();
+    assert!(elapsed >= period);
+}
+
+#[test]
 fn thread_local_containing_const_statements() {
     // This exercises the `const $init:block` cases of the thread_local macro.
     // Despite overlapping with expression syntax, the `const { ... }` is not
diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs
index 2fc42c13edd..eb21abc2a45 100644
--- a/src/tools/miri/src/shims/time.rs
+++ b/src/tools/miri/src/shims/time.rs
@@ -359,6 +359,63 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         interp_ok(Scalar::from_i32(0))
     }
 
+    fn clock_nanosleep(
+        &mut self,
+        clock_id: &OpTy<'tcx>,
+        flags: &OpTy<'tcx>,
+        timespec: &OpTy<'tcx>,
+        rem: &OpTy<'tcx>,
+    ) -> InterpResult<'tcx, Scalar> {
+        let this = self.eval_context_mut();
+        let clockid_t_size = this.libc_ty_layout("clockid_t").size;
+
+        let clock_id = this.read_scalar(clock_id)?.to_int(clockid_t_size)?;
+        let timespec = this.deref_pointer_as(timespec, this.libc_ty_layout("timespec"))?;
+        let flags = this.read_scalar(flags)?.to_i32()?;
+        let _rem = this.read_pointer(rem)?; // Signal handlers are not supported, so rem will never be written to.
+
+        // The standard lib through sleep_until only needs CLOCK_MONOTONIC
+        if clock_id != this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)? {
+            throw_unsup_format!("clock_nanosleep: only CLOCK_MONOTONIC is supported");
+        }
+
+        let duration = match this.read_timespec(&timespec)? {
+            Some(duration) => duration,
+            None => {
+                return this.set_last_error_and_return_i32(LibcError("EINVAL"));
+            }
+        };
+
+        let timeout_anchor = if flags == 0 {
+            // No flags set, the timespec should be interperted as a duration
+            // to sleep for
+            TimeoutAnchor::Relative
+        } else if flags == this.eval_libc_i32("TIMER_ABSTIME") {
+            // Only flag TIMER_ABSTIME set, the timespec should be interperted as
+            // an absolute time.
+            TimeoutAnchor::Absolute
+        } else {
+            // The standard lib (through `sleep_until`) only needs TIMER_ABSTIME
+            throw_unsup_format!(
+                "`clock_nanosleep` unsupported flags {flags}, only no flags or \
+                TIMER_ABSTIME is supported"
+            );
+        };
+
+        this.block_thread(
+            BlockReason::Sleep,
+            Some((TimeoutClock::Monotonic, timeout_anchor, duration)),
+            callback!(
+                @capture<'tcx> {}
+                |_this, unblock: UnblockKind| {
+                    assert_eq!(unblock, UnblockKind::TimedOut);
+                    interp_ok(())
+                }
+            ),
+        );
+        interp_ok(Scalar::from_i32(0))
+    }
+
     #[allow(non_snake_case)]
     fn Sleep(&mut self, timeout: &OpTy<'tcx>) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs
index 438a9b420be..548eabb1b9f 100644
--- a/src/tools/miri/src/shims/unix/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/foreign_items.rs
@@ -967,6 +967,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let result = this.nanosleep(duration, rem)?;
                 this.write_scalar(result, dest)?;
             }
+            "clock_nanosleep" => {
+                // Currently this function does not exist on all Unixes, e.g. on macOS.
+                this.check_target_os(
+                    &["freebsd", "linux", "android", "solaris", "illumos"],
+                    link_name,
+                )?;
+                let [clock_id, flags, req, rem] =
+                    this.check_shim(abi, CanonAbi::C, link_name, args)?;
+                let result = this.clock_nanosleep(clock_id, flags, req, rem)?;
+                this.write_scalar(result, dest)?;
+            }
             "sched_getaffinity" => {
                 // Currently this function does not exist on all Unixes, e.g. on macOS.
                 this.check_target_os(&["linux", "freebsd", "android"], link_name)?;
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-time.rs b/src/tools/miri/tests/pass-dep/libc/libc-time.rs
index e53201e0bc5..e8957846ad5 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-time.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-time.rs
@@ -1,5 +1,6 @@
 //@ignore-target: windows # no libc time APIs on Windows
 //@compile-flags: -Zmiri-disable-isolation
+use std::time::{Duration, Instant};
 use std::{env, mem, ptr};
 
 fn main() {
@@ -20,6 +21,19 @@ fn main() {
     test_localtime_r_future_32b();
     #[cfg(target_pointer_width = "64")]
     test_localtime_r_future_64b();
+
+    test_nanosleep();
+    #[cfg(any(
+        target_os = "freebsd",
+        target_os = "linux",
+        target_os = "android",
+        target_os = "solaris",
+        target_os = "illumos"
+    ))]
+    {
+        test_clock_nanosleep::absolute();
+        test_clock_nanosleep::relative();
+    }
 }
 
 /// Tests whether clock support exists at all
@@ -315,3 +329,103 @@ fn test_localtime_r_multiple_calls_deduplication() {
         NUM_CALLS - 1
     );
 }
+
+fn test_nanosleep() {
+    let start_test_sleep = Instant::now();
+    let duration_zero = libc::timespec { tv_sec: 0, tv_nsec: 0 };
+    let remainder = ptr::null_mut::<libc::timespec>();
+    let is_error = unsafe { libc::nanosleep(&duration_zero, remainder) };
+    assert_eq!(is_error, 0);
+    assert!(start_test_sleep.elapsed() < Duration::from_millis(10));
+
+    let start_test_sleep = Instant::now();
+    let duration_100_millis = libc::timespec { tv_sec: 0, tv_nsec: 1_000_000_000 / 10 };
+    let remainder = ptr::null_mut::<libc::timespec>();
+    let is_error = unsafe { libc::nanosleep(&duration_100_millis, remainder) };
+    assert_eq!(is_error, 0);
+    assert!(start_test_sleep.elapsed() > Duration::from_millis(100));
+}
+
+#[cfg(any(
+    target_os = "freebsd",
+    target_os = "linux",
+    target_os = "android",
+    target_os = "solaris",
+    target_os = "illumos"
+))]
+mod test_clock_nanosleep {
+    use super::*;
+
+    /// Helper function used to create an instant in the future
+    fn add_100_millis(mut ts: libc::timespec) -> libc::timespec {
+        // While tv_nsec has type `c_long` tv_sec has type `time_t`. These might
+        // end up as different types (for example: like i32 and i64).
+        const SECOND: libc::c_long = 1_000_000_000;
+        ts.tv_nsec += SECOND / 10;
+        // If this pushes tv_nsec to SECOND or higher, we need to overflow to tv_sec.
+        ts.tv_sec += (ts.tv_nsec / SECOND) as libc::time_t;
+        ts.tv_nsec %= SECOND;
+        ts
+    }
+
+    /// Helper function to get the current time for testing relative sleeps
+    fn timespec_now(clock: libc::clockid_t) -> libc::timespec {
+        let mut timespec = mem::MaybeUninit::<libc::timespec>::uninit();
+        let is_error = unsafe { libc::clock_gettime(clock, timespec.as_mut_ptr()) };
+        assert_eq!(is_error, 0);
+        unsafe { timespec.assume_init() }
+    }
+
+    pub fn absolute() {
+        let start_test_sleep = Instant::now();
+        let before_start = libc::timespec { tv_sec: 0, tv_nsec: 0 };
+        let remainder = ptr::null_mut::<libc::timespec>();
+        let error = unsafe {
+            // this will not sleep since unix time zero is in the past
+            libc::clock_nanosleep(
+                libc::CLOCK_MONOTONIC,
+                libc::TIMER_ABSTIME,
+                &before_start,
+                remainder,
+            )
+        };
+        assert_eq!(error, 0);
+        assert!(start_test_sleep.elapsed() < Duration::from_millis(10));
+
+        let start_test_sleep = Instant::now();
+        let hunderd_millis_after_start = add_100_millis(timespec_now(libc::CLOCK_MONOTONIC));
+        let remainder = ptr::null_mut::<libc::timespec>();
+        let error = unsafe {
+            libc::clock_nanosleep(
+                libc::CLOCK_MONOTONIC,
+                libc::TIMER_ABSTIME,
+                &hunderd_millis_after_start,
+                remainder,
+            )
+        };
+        assert_eq!(error, 0);
+        assert!(start_test_sleep.elapsed() > Duration::from_millis(100));
+    }
+
+    pub fn relative() {
+        const NO_FLAGS: i32 = 0;
+
+        let start_test_sleep = Instant::now();
+        let duration_zero = libc::timespec { tv_sec: 0, tv_nsec: 0 };
+        let remainder = ptr::null_mut::<libc::timespec>();
+        let error = unsafe {
+            libc::clock_nanosleep(libc::CLOCK_MONOTONIC, NO_FLAGS, &duration_zero, remainder)
+        };
+        assert_eq!(error, 0);
+        assert!(start_test_sleep.elapsed() < Duration::from_millis(10));
+
+        let start_test_sleep = Instant::now();
+        let duration_100_millis = libc::timespec { tv_sec: 0, tv_nsec: 1_000_000_000 / 10 };
+        let remainder = ptr::null_mut::<libc::timespec>();
+        let error = unsafe {
+            libc::clock_nanosleep(libc::CLOCK_MONOTONIC, NO_FLAGS, &duration_100_millis, remainder)
+        };
+        assert_eq!(error, 0);
+        assert!(start_test_sleep.elapsed() > Duration::from_millis(100));
+    }
+}
diff --git a/src/tools/miri/tests/pass/shims/time.rs b/src/tools/miri/tests/pass/shims/time.rs
index 226f04ade0f..ef0b400f1a7 100644
--- a/src/tools/miri/tests/pass/shims/time.rs
+++ b/src/tools/miri/tests/pass/shims/time.rs
@@ -1,4 +1,5 @@
 //@compile-flags: -Zmiri-disable-isolation
+#![feature(thread_sleep_until)]
 
 use std::time::{Duration, Instant, SystemTime};
 
@@ -15,6 +16,14 @@ fn test_sleep() {
     assert!((after - before).as_millis() >= 100);
 }
 
+fn test_sleep_until() {
+    let before = Instant::now();
+    let hunderd_millis_after_start = before + Duration::from_millis(100);
+    std::thread::sleep_until(hunderd_millis_after_start);
+    let after = Instant::now();
+    assert!((after - before).as_millis() >= 100);
+}
+
 fn main() {
     // Check `SystemTime`.
     let now1 = SystemTime::now();
@@ -49,4 +58,5 @@ fn main() {
     duration_sanity(diff);
 
     test_sleep();
+    test_sleep_until();
 }