diff options
| author | Ralf Jung <post@ralfj.de> | 2025-07-07 05:31:07 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-07 05:31:07 +0000 |
| commit | 51cba7839a62ccbc1afc0c0804175577013052ea (patch) | |
| tree | ddd6a21b5ea26dbb7814908658bface600f6fd20 /library | |
| parent | b2d8c81f7f1930f1a0d1a882c69322c9d7bb0545 (diff) | |
| parent | 0e043a86f06d32dc9dfb4cb993ae37dfbc7c23c6 (diff) | |
Merge pull request #4449 from rust-lang/rustup-2025-07-07
Automatic Rustup
Diffstat (limited to 'library')
29 files changed, 310 insertions, 63 deletions
diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 8f7c090bc1b..88ad1197777 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -5,6 +5,7 @@ use crate::ffi::c_void; #[allow(unused_imports)] use crate::fmt; +use crate::intrinsics::{va_arg, va_copy, va_end}; use crate::marker::{PhantomData, PhantomInvariantLifetime}; use crate::ops::{Deref, DerefMut}; @@ -280,20 +281,3 @@ impl<'f> Drop for VaListImpl<'f> { // This works for now, since `va_end` is a no-op on all current LLVM targets. } } - -/// Destroy the arglist `ap` after initialization with `va_start` or -/// `va_copy`. -#[rustc_intrinsic] -#[rustc_nounwind] -unsafe fn va_end(ap: &mut VaListImpl<'_>); - -/// Copies the current location of arglist `src` to the arglist `dst`. -#[rustc_intrinsic] -#[rustc_nounwind] -unsafe fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); - -/// Loads an argument of type `T` from the `va_list` `ap` and increment the -/// argument `ap` points to. -#[rustc_intrinsic] -#[rustc_nounwind] -unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaListImpl<'_>) -> T; diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index ab99492638e..791d10eda6d 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -54,6 +54,7 @@ )] #![allow(missing_docs)] +use crate::ffi::va_list::{VaArgSafe, VaListImpl}; use crate::marker::{ConstParamTy, DiscriminantKind, PointeeSized, Tuple}; use crate::ptr; @@ -3142,3 +3143,25 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize } ) } + +/// Copies the current location of arglist `src` to the arglist `dst`. +/// +/// FIXME: document safety requirements +#[rustc_intrinsic] +#[rustc_nounwind] +pub unsafe fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); + +/// Loads an argument of type `T` from the `va_list` `ap` and increment the +/// argument `ap` points to. +/// +/// FIXME: document safety requirements +#[rustc_intrinsic] +#[rustc_nounwind] +pub unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaListImpl<'_>) -> T; + +/// Destroy the arglist `ap` after initialization with `va_start` or `va_copy`. +/// +/// FIXME: document safety requirements +#[rustc_intrinsic] +#[rustc_nounwind] +pub unsafe fn va_end(ap: &mut VaListImpl<'_>); diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index 7ffde233da3..e53c128efe0 100644 --- a/library/core/src/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -627,7 +627,7 @@ macro_rules! rem_impl_float { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_ops", issue = "90080")] - impl Rem for $t { + impl const Rem for $t { type Output = $t; #[inline] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 3a3f44c6b85..dc09ba8d788 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -1316,7 +1316,7 @@ impl<T> [T] { assert_unsafe_precondition!( check_language_ub, "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks", - (n: usize = N, len: usize = self.len()) => n != 0 && len % n == 0, + (n: usize = N, len: usize = self.len()) => n != 0 && len.is_multiple_of(n), ); // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length let new_len = unsafe { exact_div(self.len(), N) }; @@ -1512,7 +1512,7 @@ impl<T> [T] { assert_unsafe_precondition!( check_language_ub, "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks", - (n: usize = N, len: usize = self.len()) => n != 0 && len % n == 0 + (n: usize = N, len: usize = self.len()) => n != 0 && len.is_multiple_of(n) ); // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length let new_len = unsafe { exact_div(self.len(), N) }; @@ -4866,7 +4866,7 @@ impl<T> [T] { let byte_offset = elem_start.wrapping_sub(self_start); - if byte_offset % size_of::<T>() != 0 { + if !byte_offset.is_multiple_of(size_of::<T>()) { return None; } @@ -4920,7 +4920,7 @@ impl<T> [T] { let byte_start = subslice_start.wrapping_sub(self_start); - if byte_start % size_of::<T>() != 0 { + if !byte_start.is_multiple_of(size_of::<T>()) { return None; } diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index 4280f7570db..400daba16c1 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -823,7 +823,7 @@ unsafe fn bidirectional_merge<T: FreezeMarker, F: FnMut(&T, &T) -> bool>( let right_end = right_rev.wrapping_add(1); // Odd length, so one element is left unconsumed in the input. - if len % 2 != 0 { + if !len.is_multiple_of(2) { let left_nonempty = left < left_end; let last_src = if left_nonempty { left } else { right }; ptr::copy_nonoverlapping(last_src, dst, 1); diff --git a/library/core/src/slice/sort/stable/drift.rs b/library/core/src/slice/sort/stable/drift.rs index cf1df1e91a5..1edffe095a8 100644 --- a/library/core/src/slice/sort/stable/drift.rs +++ b/library/core/src/slice/sort/stable/drift.rs @@ -158,7 +158,7 @@ fn merge_tree_scale_factor(n: usize) -> u64 { panic!("Platform not supported"); } - ((1 << 62) + n as u64 - 1) / n as u64 + (1u64 << 62).div_ceil(n as u64) } // Note: merge_tree_depth output is < 64 when left < right as f*x and f*y must @@ -182,7 +182,7 @@ fn sqrt_approx(n: usize) -> usize { // Finally we note that the exponentiation / division can be done directly // with shifts. We OR with 1 to avoid zero-checks in the integer log. let ilog = (n | 1).ilog2(); - let shift = (1 + ilog) / 2; + let shift = ilog.div_ceil(2); ((1 << shift) + (n >> shift)) / 2 } diff --git a/library/core/src/str/count.rs b/library/core/src/str/count.rs index 452403b23de..f59ad3e66b4 100644 --- a/library/core/src/str/count.rs +++ b/library/core/src/str/count.rs @@ -52,7 +52,7 @@ fn do_count_chars(s: &str) -> usize { // Check the properties of `CHUNK_SIZE` and `UNROLL_INNER` that are required // for correctness. const _: () = assert!(CHUNK_SIZE < 256); - const _: () = assert!(CHUNK_SIZE % UNROLL_INNER == 0); + const _: () = assert!(CHUNK_SIZE.is_multiple_of(UNROLL_INNER)); // SAFETY: transmuting `[u8]` to `[usize]` is safe except for size // differences which are handled by `align_to`. diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index 425c4eaee28..bcf886484ad 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -102,7 +102,7 @@ impl<'a> Iterator for Chars<'a> { // `(len + 3)` can't overflow, because we know that the `slice::Iter` // belongs to a slice in memory which has a maximum length of // `isize::MAX` (that's well below `usize::MAX`). - ((len + 3) / 4, Some(len)) + (len.div_ceil(4), Some(len)) } #[inline] @@ -1532,11 +1532,11 @@ impl<'a> Iterator for EncodeUtf16<'a> { // belongs to a slice in memory which has a maximum length of // `isize::MAX` (that's well below `usize::MAX`) if self.extra == 0 { - ((len + 2) / 3, Some(len)) + (len.div_ceil(3), Some(len)) } else { // We're in the middle of a surrogate pair, so add the remaining // surrogate to the bounds. - ((len + 2) / 3 + 1, Some(len + 1)) + (len.div_ceil(3) + 1, Some(len + 1)) } } } diff --git a/library/core/src/str/validations.rs b/library/core/src/str/validations.rs index 8174e4ff97d..b54d6478e58 100644 --- a/library/core/src/str/validations.rs +++ b/library/core/src/str/validations.rs @@ -219,7 +219,7 @@ pub(super) const fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { // Ascii case, try to skip forward quickly. // When the pointer is aligned, read 2 words of data per iteration // until we find a word containing a non-ascii byte. - if align != usize::MAX && align.wrapping_sub(index) % USIZE_BYTES == 0 { + if align != usize::MAX && align.wrapping_sub(index).is_multiple_of(USIZE_BYTES) { let ptr = v.as_ptr(); while index < blocks_end { // SAFETY: since `align - index` and `ascii_block_size` are diff --git a/library/portable-simd/crates/core_simd/src/lane_count.rs b/library/portable-simd/crates/core_simd/src/lane_count.rs index 280b27bc9bc..bbdfd5f5f3e 100644 --- a/library/portable-simd/crates/core_simd/src/lane_count.rs +++ b/library/portable-simd/crates/core_simd/src/lane_count.rs @@ -8,7 +8,7 @@ pub struct LaneCount<const N: usize>; impl<const N: usize> LaneCount<N> { /// The number of bytes in a bitmask with this many lanes. - pub const BITMASK_LEN: usize = (N + 7) / 8; + pub const BITMASK_LEN: usize = N.div_ceil(8); } /// Statically guarantees that a lane count is marked as supported. diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index cc1d0b30152..571f0d14248 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -13,7 +13,7 @@ //! depend on the primitive. See [#Overview] bellow. //! //! For the alternative implementations that do not employ poisoning, -//! see `std::sys::nonpoisoning`. +//! see `std::sync::nonpoisoning`. //! //! # Overview //! diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs index 2d465bd0efa..6835ba44ee2 100644 --- a/library/std/src/sys/net/connection/uefi/mod.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -38,16 +38,17 @@ impl TcpStream { unsupported() } - pub fn read(&self, _: &mut [u8]) -> io::Result<usize> { - unsupported() + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) } - pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { - unsupported() + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|buf| self.read(buf), cursor) } - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { - unsupported() + pub fn read_vectored(&self, buf: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + // FIXME: UEFI does support vectored read, so implement that. + crate::io::default_read_vectored(|b| self.read(b), buf) } pub fn is_read_vectored(&self) -> bool { diff --git a/library/std/src/sys/net/connection/uefi/tcp.rs b/library/std/src/sys/net/connection/uefi/tcp.rs index 9c3462e3468..55b6dbf2490 100644 --- a/library/std/src/sys/net/connection/uefi/tcp.rs +++ b/library/std/src/sys/net/connection/uefi/tcp.rs @@ -24,4 +24,10 @@ impl Tcp { Self::V4(client) => client.write(buf), } } + + pub(crate) fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + match self { + Self::V4(client) => client.read(buf), + } + } } diff --git a/library/std/src/sys/net/connection/uefi/tcp4.rs b/library/std/src/sys/net/connection/uefi/tcp4.rs index d0f0d27d975..af1ba2be47a 100644 --- a/library/std/src/sys/net/connection/uefi/tcp4.rs +++ b/library/std/src/sys/net/connection/uefi/tcp4.rs @@ -128,6 +128,45 @@ impl Tcp4 { } } + pub(crate) fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + let evt = unsafe { self.create_evt() }?; + let completion_token = + tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS }; + let data_len = u32::try_from(buf.len()).unwrap_or(u32::MAX); + + let fragment = tcp4::FragmentData { + fragment_length: data_len, + fragment_buffer: buf.as_mut_ptr().cast::<crate::ffi::c_void>(), + }; + let mut tx_data = tcp4::ReceiveData { + urgent_flag: r_efi::efi::Boolean::FALSE, + data_length: data_len, + fragment_count: 1, + fragment_table: [fragment], + }; + + let protocol = self.protocol.as_ptr(); + let mut token = tcp4::IoToken { + completion_token, + packet: tcp4::IoTokenPacket { + rx_data: (&raw mut tx_data).cast::<tcp4::ReceiveData<0>>(), + }, + }; + + let r = unsafe { ((*protocol).receive)(protocol, &mut token) }; + if r.is_error() { + return Err(io::Error::from_raw_os_error(r.as_usize())); + } + + self.wait_for_flag(); + + if completion_token.status.is_error() { + Err(io::Error::from_raw_os_error(completion_token.status.as_usize())) + } else { + Ok(data_len as usize) + } + } + unsafe fn create_evt(&self) -> io::Result<helpers::OwnedEvent> { self.flag.store(false, Ordering::Relaxed); helpers::OwnedEvent::new( 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 |
