From 8763ffa9526e906432339315962de687cd68a775 Mon Sep 17 00:00:00 2001 From: dvdsk Date: Sat, 31 May 2025 15:33:46 +0200 Subject: [PATCH 1/5] sleep_until: add platform specific implementations Except for unix they are all copies of the existing non platform specific implementation. This is the only way since tidy does not allow `#[cfg(target_os = ` in src/thread/mod.rs. Once this is merged more specializations will follow. --- library/std/src/sys/pal/hermit/thread.rs | 10 +++- library/std/src/sys/pal/itron/thread.rs | 10 +++- library/std/src/sys/pal/sgx/thread.rs | 10 +++- library/std/src/sys/pal/teeos/thread.rs | 10 +++- library/std/src/sys/pal/uefi/thread.rs | 10 +++- library/std/src/sys/pal/unix/thread.rs | 59 ++++++++++++++++++- library/std/src/sys/pal/unix/time.rs | 7 +++ library/std/src/sys/pal/unsupported/thread.rs | 4 ++ library/std/src/sys/pal/wasi/thread.rs | 10 +++- .../std/src/sys/pal/wasm/atomics/thread.rs | 10 +++- library/std/src/sys/pal/windows/thread.rs | 10 +++- library/std/src/sys/pal/xous/thread.rs | 10 +++- library/std/src/thread/mod.rs | 39 +++++++++--- library/std/src/time.rs | 7 +++ library/std/tests/thread.rs | 14 ++++- 15 files changed, 200 insertions(+), 20 deletions(-) diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index bb68a824fc313..9bc5a16b80023 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 a974f4f17ae67..813e1cbcd58fb 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 219ef1b7a9897..85f6dcd96b4a5 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 e3b4908f85863..b9cdc7a2a58bb 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 7d4006ff4b2f7..e4776ec42fbba 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 d8b189413f4a3..7ee7241f1000b 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,63 @@ impl Thread { } } + // Any unix that has clock_nanosleep + #[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 mut ts = deadline + .into_inner() + .into_timespec() + .to_timespec() + .expect("Timespec is narrower then libc::timespec thus conversion can't fail"); + let ts_ptr = &mut ts as *mut _; + + // If we're awoken with a signal and the return value is -1 + // clock_nanosleep needs to be called again. + unsafe { + while libc::clock_nanosleep(libc::CLOCK_MONOTONIC, libc::TIMER_ABSTIME, ts_ptr, ts_ptr) + == -1 + { + assert_eq!( + os::errno(), + libc::EINTR, + "clock nanosleep should only return an error if interrupted" + ); + } + } + } + + // 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 0074d7674741b..d58e1c8b564f5 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -291,6 +291,13 @@ impl Instant { pub fn checked_sub_duration(&self, other: &Duration) -> Option { Some(Instant { t: self.t.checked_sub_duration(other)? }) } + + // reason for allow(unused): this is needed by the `sleep_until` implementation + // for some unix platforms not all. + #[allow(unused)] + 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 89f8bad7026ee..9e52d5c94bc68 100644 --- a/library/std/src/sys/pal/unsupported/thread.rs +++ b/library/std/src/sys/pal/unsupported/thread.rs @@ -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 cc569bb3daf68..5f21a553673a3 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 dd5aff391fd8b..44ce3eab109f4 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 45e52cf4d047f..147851717553a 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 0ebb46dc19faa..1b344e984dc36 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 26b2fb4472436..7ee6990be8bb7 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -897,8 +897,33 @@ 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 +/// [subscription_clock]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-subscription_clock-record +/// [mach_wait_until]: https://developer.apple.com/library/archive/technotes/tn2169/_index.html +/// +/// **Disclaimer:** These system calls might change over time. /// /// # Examples /// @@ -923,9 +948,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 +985,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 03af35e809c91..08d137dc14ac2 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -407,6 +407,13 @@ impl Instant { pub fn checked_sub(&self, duration: Duration) -> Option { self.0.checked_sub_duration(&duration).map(Instant) } + + // used by platform specific `sleep_until` implementations. + // reason for #[allow(unused)]: not every platform has a specific `sleep_until`. + #[allow(unused)] + 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 1bb17d149fa10..32561dd6ab6a3 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 @@ -17,6 +18,17 @@ fn sleep_very_long() { assert_eq!(*finished.lock().unwrap(), false); } +#[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. From b21da820c28801405662e081ce15a3066ef1434d Mon Sep 17 00:00:00 2001 From: David Kleingeld Date: Tue, 3 Jun 2025 08:13:26 +0200 Subject: [PATCH 2/5] sleep_until: add reason to allow(unused) Co-authored-by: Josh Stone --- library/std/src/sys/pal/unix/time.rs | 4 +--- library/std/src/time.rs | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index d58e1c8b564f5..afce740af9f80 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -292,9 +292,7 @@ impl Instant { Some(Instant { t: self.t.checked_sub_duration(other)? }) } - // reason for allow(unused): this is needed by the `sleep_until` implementation - // for some unix platforms not all. - #[allow(unused)] + #[allow(unused, reason = "needed by the `sleep_until` on some unix platforms")] pub(crate) fn into_timespec(self) -> Timespec { self.t } diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 08d137dc14ac2..b4d62b5688a22 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -409,8 +409,7 @@ impl Instant { } // used by platform specific `sleep_until` implementations. - // reason for #[allow(unused)]: not every platform has a specific `sleep_until`. - #[allow(unused)] + #[allow(unused, reason = "not every platform has a specific `sleep_until`")] pub(crate) fn into_inner(self) -> time::Instant { self.0 } From 5065a35fa8861692e26094e91ddbe0ebf04c2257 Mon Sep 17 00:00:00 2001 From: dvdsk Date: Tue, 3 Jun 2025 08:51:10 +0200 Subject: [PATCH 3/5] sleep_until: handle 32bit time_t targets --- library/std/src/sys/pal/unix/thread.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 7ee7241f1000b..42441dd43b370 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -310,11 +310,17 @@ impl Thread { target_os = "vxworks", ))] pub fn sleep_until(deadline: Instant) { - let mut ts = deadline - .into_inner() - .into_timespec() - .to_timespec() - .expect("Timespec is narrower then libc::timespec thus conversion can't fail"); + let Some(mut 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 intead. 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; + }; + let ts_ptr = &mut ts as *mut _; // If we're awoken with a signal and the return value is -1 @@ -347,7 +353,6 @@ impl Thread { )))] pub fn sleep_until(deadline: Instant) { let now = Instant::now(); - if let Some(delay) = deadline.checked_duration_since(now) { Self::sleep(delay); } From cfe7c6b0920627561c3ccf8bbba79053988536c5 Mon Sep 17 00:00:00 2001 From: David Kleingeld Date: Thu, 5 Jun 2025 18:13:19 +0200 Subject: [PATCH 4/5] sleep_until: improve maintainability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jonas Böttiger - use the internal platform specific Instant::CLOCK_ID - skip allow(unused) on on platform that uses it such that it can not become dead code - sleep_until: remove leftover links --- library/std/src/sys/pal/unix/thread.rs | 8 ++++++-- library/std/src/sys/pal/unix/time.rs | 10 +++++----- library/std/src/thread/mod.rs | 2 -- library/std/src/time.rs | 7 +++++-- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 42441dd43b370..c1bc497c0f674 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -326,8 +326,12 @@ impl Thread { // If we're awoken with a signal and the return value is -1 // clock_nanosleep needs to be called again. unsafe { - while libc::clock_nanosleep(libc::CLOCK_MONOTONIC, libc::TIMER_ABSTIME, ts_ptr, ts_ptr) - == -1 + while libc::clock_nanosleep( + super::time::Instant::CLOCK_ID, + libc::TIMER_ABSTIME, + ts_ptr, + ts_ptr, + ) == -1 { assert_eq!( os::errno(), diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index afce740af9f80..f34e6d1dcc968 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 { diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 7ee6990be8bb7..6075173db47f4 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -920,8 +920,6 @@ pub fn sleep(dur: Duration) { /// /// [currently]: crate::io#platform-specific-behavior /// [clock_nanosleep]: https://linux.die.net/man/3/clock_nanosleep -/// [subscription_clock]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-subscription_clock-record -/// [mach_wait_until]: https://developer.apple.com/library/archive/technotes/tn2169/_index.html /// /// **Disclaimer:** These system calls might change over time. /// diff --git a/library/std/src/time.rs b/library/std/src/time.rs index b4d62b5688a22..979f51ad3a39e 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -408,8 +408,11 @@ impl Instant { self.0.checked_sub_duration(&duration).map(Instant) } - // used by platform specific `sleep_until` implementations. - #[allow(unused, reason = "not every platform has a specific `sleep_until`")] + // 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 } From 9c52693a73e96d555485a339bb75b61c6164f470 Mon Sep 17 00:00:00 2001 From: dvdsk Date: Thu, 12 Jun 2025 01:07:28 +0200 Subject: [PATCH 5/5] sleep_until: Fix error handling + improve maintainablity - In contradiction to nanosleep clock_nanosleep returns the error directly and does not require a call to `os::errno()`. - The last argument to clock_nanosleep can be NULL removing the need for mutating the timespec. - Missed an `allow(unused)` that could be made conditional. --- library/std/src/sys/pal/unix/thread.rs | 30 ++++++++++++-------------- library/std/src/sys/pal/unix/time.rs | 5 ++++- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index c1bc497c0f674..a0dc9432b9d35 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -310,7 +310,7 @@ impl Thread { target_os = "vxworks", ))] pub fn sleep_until(deadline: Instant) { - let Some(mut ts) = deadline.into_inner().into_timespec().to_timespec() else { + 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 intead. This might // happen on 32 bit platforms, especially closer to 2038. @@ -321,23 +321,21 @@ impl Thread { return; }; - let ts_ptr = &mut ts as *mut _; - - // If we're awoken with a signal and the return value is -1 - // clock_nanosleep needs to be called again. unsafe { - while libc::clock_nanosleep( - super::time::Instant::CLOCK_ID, - libc::TIMER_ABSTIME, - ts_ptr, - ts_ptr, - ) == -1 - { - assert_eq!( - os::errno(), - libc::EINTR, - "clock nanosleep should only return an error if interrupted" + // 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); + } } } } diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index f34e6d1dcc968..bd7f74fea6a9c 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -292,7 +292,10 @@ impl Instant { Some(Instant { t: self.t.checked_sub_duration(other)? }) } - #[allow(unused, reason = "needed by the `sleep_until` on some unix platforms")] + #[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 }