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 19 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 @@ -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);
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 @@ -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();
}
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 @@ -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()) };
Expand All @@ -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;

dvdsk marked this conversation as resolved.
Show resolved Hide resolved
// 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) };
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
75 changes: 43 additions & 32 deletions library/std/src/sys/pal/wasi/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! {
Expand Down Expand Up @@ -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")] {
Expand All @@ -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
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
Loading
Loading