Skip to content

Specialize sleep_until implementation #118480

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

Closed
wants to merge 19 commits into from
Closed
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
@@ -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) {
sleep(delay);
}
}

pub fn join(self) {
unsafe {
let _ = hermit_abi::join(self.tid);
8 changes: 8 additions & 0 deletions library/std/src/sys/pal/itron/thread.rs
Original file line number Diff line number Diff line change
@@ -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() };
8 changes: 8 additions & 0 deletions library/std/src/sys/pal/sgx/thread.rs
Original file line number Diff line number Diff line change
@@ -131,6 +131,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();
}
108 changes: 108 additions & 0 deletions library/std/src/sys/pal/unix/thread.rs
Original file line number Diff line number Diff line change
@@ -303,6 +303,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 XNU source code:
// https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/osfmk/kern/clock.c#L1507-L1543
let ret = mach_wait_until(ticks);
if ret == KERN_SUCCESS {
break;
}
assert_eq!(ret, KERN_ABORTED);
}
}
}

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

// See https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/osfmk/mach/kern_return.h
#[cfg(target_vendor = "apple")]
const KERN_SUCCESS: libc::c_int = 0;
#[cfg(target_vendor = "apple")]
const KERN_ABORTED: libc::c_int = 14;

// See https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/osfmk/mach/mach_time.h
#[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) };
10 changes: 7 additions & 3 deletions library/std/src/sys/pal/unix/time.rs
Original file line number Diff line number Diff line change
@@ -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 {
@@ -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 {
@@ -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 {
75 changes: 43 additions & 32 deletions library/std/src/sys/pal/wasi/thread.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,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! {
@@ -136,41 +136,25 @@ impl Thread {
}

pub fn sleep(dur: Duration) {
let mut nanos = dur.as_nanos();
let mut nanos_all = dur.as_nanos();
while nanos > 0 {
const USERDATA: wasi::Userdata = 0x0123_45678;

let clock = wasi::SubscriptionClock {
id: wasi::CLOCKID_MONOTONIC,
timeout: u64::try_from(nanos).unwrap_or(u64::MAX),
precision: 0,
flags: 0,
};
nanos -= u128::from(clock.timeout);

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"),
}
}
let nanos_sleepable = u64::try_from(full_nanos).unwrap_or(u64::MAX);
nanos_all -= u128::from(nanos_sleepable);
sleep_with(nanos_sleepable, 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) {
cfg_if::cfg_if! {
if #[cfg(target_feature = "atomics")] {
@@ -186,6 +170,33 @@ impl Thread {
}
}

fn sleep_with(nanos: u64, clock_id: wasi::Clockid, flags: u16) {
const USERDATA: wasi::Userdata = 0x0123_45678;

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

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
@@ -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 {
10 changes: 9 additions & 1 deletion library/std/src/sys/pal/windows/thread.rs
Original file line number Diff line number Diff line change
@@ -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;
@@ -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
}
8 changes: 8 additions & 0 deletions library/std/src/sys/pal/xous/thread.rs
Original file line number Diff line number Diff line change
@@ -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();
}
Loading