Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specialize sleep_until implementation #118480

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
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
8 changes: 8 additions & 0 deletions library/std/src/sys/pal/hermit/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ impl Thread {
}
}

pub fn sleep_until(deadline: Instant) {
let now = Instant::now();

if let Some(delay) = deadline.checked_duration_since(now) {
sleep(delay);
}
}

pub fn join(self) {
unsafe {
let _ = hermit_abi::join(self.tid);
Expand Down
8 changes: 8 additions & 0 deletions library/std/src/sys/pal/itron/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
sleep(delay);
}
}

pub fn join(self) {
// Safety: `ThreadInner` is alive at this point
let inner = unsafe { self.p_inner.as_ref() };
Expand Down
8 changes: 8 additions & 0 deletions library/std/src/sys/pal/sgx/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,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) {
sleep(delay);
}
}

pub fn join(self) {
self.0.wait();
}
Expand Down
108 changes: 108 additions & 0 deletions library/std/src/sys/pal/unix/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,93 @@ impl Thread {
}
}

#[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",
target_vendor = "apple"
)))]
pub fn sleep_until(deadline: Instant) {
let now = Instant::now();

if let Some(delay) = deadline.checked_duration_since(now) {
sleep(delay);
}
}

// Note depends on clock_nanosleep (not supported on os's by apple)
#[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: crate::time::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"
);
}
}
}

#[cfg(target_vendor = "apple")]
pub fn sleep_until(deadline: crate::time::Instant) {
use core::mem::MaybeUninit;

use super::time::Timespec;

let Timespec { tv_sec, tv_nsec } = deadline.into_inner().into_timespec();
let nanos = (tv_sec as u64).saturating_mul(1_000_000_000).saturating_add(tv_nsec.0 as u64);

let mut info = MaybeUninit::uninit();
unsafe {
let ret = mach_timebase_info(info.as_mut_ptr());
assert_eq!(ret, KERN_SUCCESS);

let info = info.assume_init();
let ticks = nanos * (info.denom as u64) / (info.numer as u64);

loop {
// There are no docs on the mach_wait_until some details can be
// learned from the `Apple OSS Distributions` xnu source code.
// Specifically: xnu/osfmk/clock.h commit 94d3b45 on Github
let ret = mach_wait_until(ticks);
if ret != KERN_ABORTED {
break;
}
}
assert_eq!(ret, KERN_SUCCESS);
}
}

pub fn join(self) {
let id = self.into_id();
let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
Expand All @@ -279,6 +366,27 @@ impl Thread {
}
}

// these come from the `Apple OSS Distributions` xnu source code.
// Specifically: xnu/osfmk/mach/kern_return.h commit 94d3b45 on Github
#[cfg(target_vendor = "apple")]
const KERN_SUCCESS: libc::c_int = 0;
#[cfg(target_vendor = "apple")]
const KERN_ABORTED: libc::c_int = 14;

#[cfg(target_vendor = "apple")]
#[repr(C)]
struct mach_timebase_info_type {
numer: u32,
denom: u32,
}

#[cfg(target_vendor = "apple")]
extern "C" {
fn mach_wait_until(deadline: u64) -> libc::c_int;
fn mach_timebase_info(info: *mut mach_timebase_info_type) -> libc::c_int;

}

impl Drop for Thread {
fn drop(&mut self) {
let ret = unsafe { libc::pthread_detach(self.id) };
Expand Down
10 changes: 7 additions & 3 deletions library/std/src/sys/pal/unix/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub(in crate::sys) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec {
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(0)]
#[rustc_layout_scalar_valid_range_end(999_999_999)]
struct Nanoseconds(u32);
pub(crate) struct Nanoseconds(pub(crate) u32);

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SystemTime {
Expand All @@ -28,8 +28,8 @@ pub struct SystemTime {

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct Timespec {
tv_sec: i64,
tv_nsec: Nanoseconds,
pub(crate) tv_sec: i64,
pub(crate) tv_nsec: Nanoseconds,
}

impl SystemTime {
Expand Down Expand Up @@ -287,6 +287,10 @@ impl Instant {
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_sub_duration(other)? })
}

pub(crate) fn into_timespec(self) -> Timespec {
self.t
}
}

impl fmt::Debug for Instant {
Expand Down
69 changes: 39 additions & 30 deletions library/std/src/sys/pal/wasi/thread.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::ffi::CStr;
use crate::num::NonZero;
use crate::sys::unsupported;
use crate::time::Duration;
use crate::time::{Duration, Instant};
use crate::{io, mem};

cfg_if::cfg_if! {
Expand Down Expand Up @@ -137,35 +137,18 @@ impl Thread {
let nanos = dur.as_nanos();
assert!(nanos <= u64::MAX as u128);

const USERDATA: wasi::Userdata = 0x0123_45678;

let clock = wasi::SubscriptionClock {
id: wasi::CLOCKID_MONOTONIC,
timeout: nanos as u64,
precision: 0,
flags: 0,
};

let in_ = wasi::Subscription {
userdata: USERDATA,
u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } },
};
unsafe {
let mut event: wasi::Event = mem::zeroed();
let res = wasi::poll_oneoff(&in_, &mut event, 1);
match (res, event) {
(
Ok(1),
wasi::Event {
userdata: USERDATA,
error: wasi::ERRNO_SUCCESS,
type_: wasi::EVENTTYPE_CLOCK,
..
},
) => {}
_ => panic!("thread::sleep(): unexpected result of poll_oneoff"),
}
}
sleep_with(nanos as u64, wasi::CLOCKID_MONOTONIC, 0);
}

pub fn sleep_until(deadline: Instant) {
let nanos = deadline.into_inner().into_inner().as_nanos();
assert!(nanos <= u64::MAX as u128);

sleep_with(
nanos as u64,
wasi::CLOCKID_MONOTONIC,
wasi::SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME,
);
}

pub fn join(self) {
Expand All @@ -183,6 +166,32 @@ impl Thread {
}
}

fn sleep_with(nanos: u64, clock_id: wasi::Clockid, flags: u16) {
let clock = wasi::SubscriptionClock { id: clock_id, timeout: nanos, precision: 0, flags };

const USERDATA: wasi::Userdata = 0x0123_45678;
let in_ = wasi::Subscription {
userdata: USERDATA,
u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } },
};
unsafe {
let mut event: wasi::Event = mem::zeroed();
let res = wasi::poll_oneoff(&in_, &mut event, 1);
match (res, event) {
(
Ok(1),
wasi::Event {
userdata: USERDATA,
error: wasi::ERRNO_SUCCESS,
type_: wasi::EVENTTYPE_CLOCK,
..
},
) => {}
_ => panic!("thread::sleep(): unexpected result of poll_oneoff"),
}
}
}

pub fn available_parallelism() -> io::Result<NonZero<usize>> {
unsupported()
}
4 changes: 4 additions & 0 deletions library/std/src/sys/pal/wasi/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ impl Instant {
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant(self.0.checked_sub(*other)?))
}

pub(crate) fn into_inner(self) -> Duration {
self.0
}
}

impl SystemTime {
Expand Down
10 changes: 9 additions & 1 deletion library/std/src/sys/pal/windows/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -105,6 +105,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
}
Expand Down
8 changes: 8 additions & 0 deletions library/std/src/sys/pal/xous/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
sleep(delay);
}
}

pub fn join(self) {
join_thread(self.tid).unwrap();
}
Expand Down
36 changes: 30 additions & 6 deletions library/std/src/thread/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -926,8 +926,36 @@ pub fn sleep(dur: Duration) {
///
/// # Platform-specific behavior
///
/// This function uses [`sleep`] internally, see its platform-specific behaviour.
/// In most cases this function will an call 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)] |
/// | Darwin | [mach_wait_until] |
/// | WASI | [subscription_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
///
Expand Down Expand Up @@ -989,11 +1017,7 @@ pub fn sleep(dur: Duration) {
/// ```
#[unstable(feature = "thread_sleep_until", issue = "113752")]
pub fn sleep_until(deadline: Instant) {
dvdsk marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down
Loading
Loading