Skip to content

Commit 406e32b

Browse files
committed
make sleep_until platform specific and add impl for unix
1 parent 852f15c commit 406e32b

File tree

11 files changed

+160
-11
lines changed

11 files changed

+160
-11
lines changed

library/std/src/sys/pal/hermit/thread.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ impl Thread {
8686
}
8787
}
8888

89+
pub fn sleep_until(deadline: Instant) {
90+
let now = Instant::now();
91+
92+
if let Some(delay) = deadline.checked_duration_since(now) {
93+
sleep(delay);
94+
}
95+
}
96+
8997
pub fn join(self) {
9098
unsafe {
9199
let _ = hermit_abi::join(self.tid);

library/std/src/sys/pal/itron/thread.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,14 @@ impl Thread {
205205
}
206206
}
207207

208+
pub fn sleep_until(deadline: Instant) {
209+
let now = Instant::now();
210+
211+
if let Some(delay) = deadline.checked_duration_since(now) {
212+
sleep(delay);
213+
}
214+
}
215+
208216
pub fn join(self) {
209217
// Safety: `ThreadInner` is alive at this point
210218
let inner = unsafe { self.p_inner.as_ref() };

library/std/src/sys/pal/sgx/thread.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,14 @@ impl Thread {
132132
usercalls::wait_timeout(0, dur, || true);
133133
}
134134

135+
pub fn sleep_until(deadline: Instant) {
136+
let now = Instant::now();
137+
138+
if let Some(delay) = deadline.checked_duration_since(now) {
139+
sleep(delay);
140+
}
141+
}
142+
135143
pub fn join(self) {
136144
self.0.wait();
137145
}

library/std/src/sys/pal/unix/thread.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,63 @@ impl Thread {
296296
}
297297
}
298298

299+
#[cfg(not(any(
300+
target_vendor = "apple",
301+
target_os = "freebsd",
302+
target_os = "netbsd",
303+
target_os = "linux",
304+
target_os = "android",
305+
target_os = "solaris",
306+
target_os = "illumos",
307+
target_os = "dragonfly",
308+
target_os = "hurd",
309+
target_os = "fuchsia",
310+
target_os = "vxworks",
311+
)))]
312+
pub fn sleep_until(deadline: Instant) {
313+
let now = Instant::now();
314+
315+
if let Some(delay) = deadline.checked_duration_since(now) {
316+
sleep(delay);
317+
}
318+
}
319+
320+
// Note depends on clock_nanosleep (not supported on os's by apple)
321+
#[cfg(any(
322+
target_os = "freebsd",
323+
target_os = "netbsd",
324+
target_os = "linux",
325+
target_os = "android",
326+
target_os = "solaris",
327+
target_os = "illumos",
328+
target_os = "dragonfly",
329+
target_os = "hurd",
330+
target_os = "fuchsia",
331+
target_os = "vxworks",
332+
))]
333+
pub fn sleep_until(deadline: crate::time::Instant) {
334+
let mut ts = deadline
335+
.into_inner()
336+
.into_timespec()
337+
.to_timespec()
338+
.expect("Timespec is narrower then libc::timespec thus conversion can't fail");
339+
let ts_ptr = &mut ts as *mut _;
340+
341+
// If we're awoken with a signal and the return value is -1
342+
// clock_nanosleep needs to be called again.
343+
unsafe {
344+
while libc::clock_nanosleep(libc::CLOCK_MONOTONIC, libc::TIMER_ABSTIME, ts_ptr, ts_ptr)
345+
== -1
346+
{
347+
assert_eq!(
348+
os::errno(),
349+
libc::EINTR,
350+
"clock nanosleep should only return an error if interrupted"
351+
);
352+
}
353+
}
354+
}
355+
299356
pub fn join(self) {
300357
let id = self.into_id();
301358
let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };

library/std/src/sys/pal/unix/time.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,10 @@ impl Instant {
291291
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
292292
Some(Instant { t: self.t.checked_sub_duration(other)? })
293293
}
294+
295+
pub(crate) fn into_timespec(self) -> Timespec {
296+
self.t
297+
}
294298
}
295299

296300
impl fmt::Debug for Instant {

library/std/src/sys/pal/wasi/thread.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,14 @@ impl Thread {
171171
}
172172
}
173173

174+
pub fn sleep_until(deadline: Instant) {
175+
let now = Instant::now();
176+
177+
if let Some(delay) = deadline.checked_duration_since(now) {
178+
sleep(delay);
179+
}
180+
}
181+
174182
pub fn join(self) {
175183
cfg_if::cfg_if! {
176184
if #[cfg(target_feature = "atomics")] {

library/std/src/sys/pal/windows/thread.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::os::windows::io::{AsRawHandle, HandleOrNull};
88
use crate::sys::handle::Handle;
99
use crate::sys::{c, stack_overflow};
1010
use crate::sys_common::FromInner;
11-
use crate::time::Duration;
11+
use crate::time::{Duration, Instant};
1212
use crate::{io, ptr};
1313

1414
pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
@@ -106,6 +106,14 @@ impl Thread {
106106
}
107107
}
108108

109+
pub fn sleep_until(deadline: Instant) {
110+
let now = Instant::now();
111+
112+
if let Some(delay) = deadline.checked_duration_since(now) {
113+
Self::sleep(delay);
114+
}
115+
}
116+
109117
pub fn handle(&self) -> &Handle {
110118
&self.handle
111119
}

library/std/src/sys/pal/xous/thread.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,14 @@ impl Thread {
128128
}
129129
}
130130

131+
pub fn sleep_until(deadline: Instant) {
132+
let now = Instant::now();
133+
134+
if let Some(delay) = deadline.checked_duration_since(now) {
135+
sleep(delay);
136+
}
137+
}
138+
131139
pub fn join(self) {
132140
join_thread(self.tid).unwrap();
133141
}

library/std/src/thread/mod.rs

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -897,8 +897,33 @@ pub fn sleep(dur: Duration) {
897897
///
898898
/// # Platform-specific behavior
899899
///
900-
/// This function uses [`sleep`] internally, see its platform-specific behavior.
901-
///
900+
/// In most cases this function will call an OS specific function. Where that
901+
/// is not supported [`sleep`] is used. Those platforms are referred to as other
902+
/// in the table below.
903+
///
904+
/// # Underlying System calls
905+
///
906+
/// The following system calls are [currently] being used:
907+
///
908+
/// | Platform | System call |
909+
/// |-----------|----------------------------------------------------------------------|
910+
/// | Linux | [clock_nanosleep] (Monotonic clock) |
911+
/// | BSD except OpenBSD | [clock_nanosleep] (Monotonic Clock)] |
912+
/// | Android | [clock_nanosleep] (Monotonic Clock)] |
913+
/// | Solaris | [clock_nanosleep] (Monotonic Clock)] |
914+
/// | Illumos | [clock_nanosleep] (Monotonic Clock)] |
915+
/// | Dragonfly | [clock_nanosleep] (Monotonic Clock)] |
916+
/// | Hurd | [clock_nanosleep] (Monotonic Clock)] |
917+
/// | Fuchsia | [clock_nanosleep] (Monotonic Clock)] |
918+
/// | Vxworks | [clock_nanosleep] (Monotonic Clock)] |
919+
/// | Other | `sleep_until` uses [`sleep`] and does not issue a syscall itself |
920+
///
921+
/// [currently]: crate::io#platform-specific-behavior
922+
/// [clock_nanosleep]: https://linux.die.net/man/3/clock_nanosleep
923+
/// [subscription_clock]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-subscription_clock-record
924+
/// [mach_wait_until]: https://developer.apple.com/library/archive/technotes/tn2169/_index.html
925+
///
926+
/// **Disclaimer:** These system calls might change over time.
902927
///
903928
/// # Examples
904929
///
@@ -923,9 +948,9 @@ pub fn sleep(dur: Duration) {
923948
/// }
924949
/// ```
925950
///
926-
/// A slow api we must not call too fast and which takes a few
951+
/// A slow API we must not call too fast and which takes a few
927952
/// tries before succeeding. By using `sleep_until` the time the
928-
/// api call takes does not influence when we retry or when we give up
953+
/// API call takes does not influence when we retry or when we give up
929954
///
930955
/// ```no_run
931956
/// #![feature(thread_sleep_until)]
@@ -960,11 +985,7 @@ pub fn sleep(dur: Duration) {
960985
/// ```
961986
#[unstable(feature = "thread_sleep_until", issue = "113752")]
962987
pub fn sleep_until(deadline: Instant) {
963-
let now = Instant::now();
964-
965-
if let Some(delay) = deadline.checked_duration_since(now) {
966-
sleep(delay);
967-
}
988+
imp::Thread::sleep_until(deadline)
968989
}
969990

970991
/// Used to ensure that `park` and `park_timeout` do not unwind, as that can

library/std/src/time.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,13 @@ impl Instant {
407407
pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
408408
self.0.checked_sub_duration(&duration).map(Instant)
409409
}
410+
411+
// used by platform specific `sleep_until` implementations.
412+
// reason for #[allow(unused)]: not every platform has a specific `sleep_until`.
413+
#[allow(unused)]
414+
pub(crate) fn into_inner(self) -> time::Instant {
415+
self.0
416+
}
410417
}
411418

412419
#[stable(feature = "time2", since = "1.8.0")]

library/std/tests/thread.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
#![feature(thread_sleep_until)]
12
use std::cell::{Cell, RefCell};
23
use std::sync::{Arc, Mutex};
34
use std::thread;
4-
use std::time::Duration;
5+
use std::time::{Duration, Instant};
56

67
#[test]
78
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
@@ -17,6 +18,17 @@ fn sleep_very_long() {
1718
assert_eq!(*finished.lock().unwrap(), false);
1819
}
1920

21+
#[test]
22+
fn sleep_until() {
23+
let now = Instant::now();
24+
let period = Duration::from_millis(100);
25+
let deadline = now + period;
26+
thread::sleep_until(deadline);
27+
28+
let elapsed = now.elapsed();
29+
assert!(elapsed >= period);
30+
}
31+
2032
#[test]
2133
fn thread_local_containing_const_statements() {
2234
// This exercises the `const $init:block` cases of the thread_local macro.

0 commit comments

Comments
 (0)