std: move thread into sys
This commit is contained in:
parent
4b15dd5a84
commit
ad08577a50
29 changed files with 843 additions and 1043 deletions
|
|
@ -31,6 +31,7 @@ pub mod process;
|
|||
pub mod random;
|
||||
pub mod stdio;
|
||||
pub mod sync;
|
||||
pub mod thread;
|
||||
pub mod thread_local;
|
||||
|
||||
// FIXME(117276): remove this, move feature implementations into individual
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ pub mod futex;
|
|||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
pub mod thread;
|
||||
pub mod time;
|
||||
|
||||
pub fn unsupported<T>() -> crate::io::Result<T> {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64
|
|||
let tls_guard = unsafe { tls.activate() };
|
||||
|
||||
if secondary {
|
||||
let join_notifier = super::thread::Thread::entry();
|
||||
let join_notifier = crate::sys::thread::Thread::entry();
|
||||
drop(tls_guard);
|
||||
drop(join_notifier);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ mod libunwind_integration;
|
|||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
pub mod thread;
|
||||
pub mod thread_parking;
|
||||
pub mod time;
|
||||
pub mod waitqueue;
|
||||
|
|
|
|||
|
|
@ -10,10 +10,8 @@ pub mod itron {
|
|||
pub mod error;
|
||||
pub mod spin;
|
||||
pub mod task;
|
||||
pub mod thread;
|
||||
pub mod thread_parking;
|
||||
pub mod time;
|
||||
use super::unsupported;
|
||||
}
|
||||
|
||||
// `error` is `pub(crate)` so that it can be accessed by `itron/error.rs` as
|
||||
|
|
@ -22,7 +20,7 @@ pub(crate) mod error;
|
|||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
pub use self::itron::{thread, thread_parking};
|
||||
pub use self::itron::thread_parking;
|
||||
pub mod time;
|
||||
|
||||
// SAFETY: must be called only once during runtime initialization.
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
pub mod thread;
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[path = "../unix/time.rs"]
|
||||
pub mod time;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ pub mod helpers;
|
|||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
pub mod thread;
|
||||
pub mod time;
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ pub mod os;
|
|||
pub mod pipe;
|
||||
pub mod stack_overflow;
|
||||
pub mod sync;
|
||||
pub mod thread;
|
||||
pub mod thread_parking;
|
||||
pub mod time;
|
||||
|
||||
|
|
@ -55,7 +54,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
|
|||
// thread-id for the main thread and so renaming the main thread will rename the
|
||||
// process and we only want to enable this on platforms we've tested.
|
||||
if cfg!(target_vendor = "apple") {
|
||||
thread::Thread::set_name(&c"main");
|
||||
crate::sys::thread::set_name(c"main");
|
||||
}
|
||||
|
||||
unsafe fn sanitize_standard_fds() {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ pub mod futex;
|
|||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
pub mod thread;
|
||||
pub mod time;
|
||||
|
||||
#[path = "../unsupported/common.rs"]
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ pub mod futex;
|
|||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
pub mod thread;
|
||||
pub mod time;
|
||||
|
||||
#[path = "../unsupported/common.rs"]
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ impl Instant {
|
|||
Some(Instant(self.0.checked_sub(*other)?))
|
||||
}
|
||||
|
||||
pub(super) fn as_duration(&self) -> &Duration {
|
||||
pub(crate) fn as_duration(&self) -> &Duration {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,18 +23,9 @@ pub mod pipe;
|
|||
#[path = "../unsupported/time.rs"]
|
||||
pub mod time;
|
||||
|
||||
cfg_select! {
|
||||
target_feature = "atomics" => {
|
||||
#[path = "atomics/futex.rs"]
|
||||
pub mod futex;
|
||||
#[path = "atomics/thread.rs"]
|
||||
pub mod thread;
|
||||
}
|
||||
_ => {
|
||||
#[path = "../unsupported/thread.rs"]
|
||||
pub mod thread;
|
||||
}
|
||||
}
|
||||
#[cfg(target_feature = "atomics")]
|
||||
#[path = "atomics/futex.rs"]
|
||||
pub mod futex;
|
||||
|
||||
#[path = "../unsupported/common.rs"]
|
||||
#[deny(unsafe_op_in_unsafe_fn)]
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ pub mod futex;
|
|||
pub mod handle;
|
||||
pub mod os;
|
||||
pub mod pipe;
|
||||
pub mod thread;
|
||||
pub mod time;
|
||||
cfg_select! {
|
||||
not(target_vendor = "uwp") => {
|
||||
|
|
@ -48,9 +47,9 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {
|
|||
unsafe {
|
||||
stack_overflow::init();
|
||||
|
||||
// Normally, `thread::spawn` will call `Thread::set_name` but since this thread already
|
||||
// Normally, `thread::spawn` will call `set_name` but since this thread already
|
||||
// exists, we have to call it ourselves.
|
||||
thread::Thread::set_name_wide(wide_str!("main"));
|
||||
crate::sys::thread::set_name_wide(wide_str!("main"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ mod perf_counter {
|
|||
}
|
||||
|
||||
/// A timer you can wait on.
|
||||
pub(super) struct WaitableTimer {
|
||||
pub(crate) struct WaitableTimer {
|
||||
handle: c::HANDLE,
|
||||
}
|
||||
impl WaitableTimer {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ use crate::os::xous::ffi::exit;
|
|||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
pub mod thread;
|
||||
pub mod time;
|
||||
|
||||
#[path = "../unsupported/common.rs"]
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use super::hermit_abi;
|
||||
use crate::ffi::CStr;
|
||||
use crate::mem::ManuallyDrop;
|
||||
use crate::num::NonZero;
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::time::Duration;
|
||||
use crate::{io, ptr};
|
||||
|
||||
pub type Tid = hermit_abi::Tid;
|
||||
|
|
@ -68,57 +63,30 @@ impl Thread {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn yield_now() {
|
||||
unsafe {
|
||||
hermit_abi::yield_now();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_name(_name: &CStr) {
|
||||
// nope
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn sleep(dur: Duration) {
|
||||
let micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 };
|
||||
let micros = u64::try_from(micros).unwrap_or(u64::MAX);
|
||||
|
||||
unsafe {
|
||||
hermit_abi::usleep(micros);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> Tid {
|
||||
self.tid
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_id(self) -> Tid {
|
||||
ManuallyDrop::new(self).tid
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn current_os_id() -> Option<u64> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||
unsafe { Ok(NonZero::new_unchecked(hermit_abi::available_parallelism())) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn sleep(dur: Duration) {
|
||||
let micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 };
|
||||
let micros = u64::try_from(micros).unwrap_or(u64::MAX);
|
||||
|
||||
unsafe {
|
||||
hermit_abi::usleep(micros);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn yield_now() {
|
||||
unsafe {
|
||||
hermit_abi::yield_now();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
152
library/std/src/sys/thread/mod.rs
Normal file
152
library/std/src/sys/thread/mod.rs
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
cfg_select! {
|
||||
target_os = "hermit" => {
|
||||
mod hermit;
|
||||
pub use hermit::{Thread, available_parallelism, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||
#[expect(dead_code)]
|
||||
mod unsupported;
|
||||
pub use unsupported::{current_os_id, set_name};
|
||||
}
|
||||
all(target_vendor = "fortanix", target_env = "sgx") => {
|
||||
mod sgx;
|
||||
pub use sgx::{Thread, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||
|
||||
// SGX should protect in-enclave data from outside attackers, so there
|
||||
// must not be any data leakage to the OS, particularly no 1-1 mapping
|
||||
// between SGX thread names and OS thread names. Hence `set_name` is
|
||||
// intentionally a no-op.
|
||||
//
|
||||
// Note that the internally visible SGX thread name is already provided
|
||||
// by the platform-agnostic Rust thread code. This can be observed in
|
||||
// the [`std::thread::tests::test_named_thread`] test, which succeeds
|
||||
// as-is with the SGX target.
|
||||
#[expect(dead_code)]
|
||||
mod unsupported;
|
||||
pub use unsupported::{available_parallelism, set_name};
|
||||
}
|
||||
target_os = "solid_asp3" => {
|
||||
mod solid;
|
||||
pub use solid::{Thread, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||
#[expect(dead_code)]
|
||||
mod unsupported;
|
||||
pub use unsupported::{available_parallelism, current_os_id, set_name};
|
||||
}
|
||||
target_os = "teeos" => {
|
||||
mod teeos;
|
||||
pub use teeos::{Thread, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||
#[expect(dead_code)]
|
||||
mod unsupported;
|
||||
pub use unsupported::{available_parallelism, current_os_id, set_name};
|
||||
}
|
||||
target_os = "uefi" => {
|
||||
mod uefi;
|
||||
pub use uefi::{available_parallelism, sleep};
|
||||
#[expect(dead_code)]
|
||||
mod unsupported;
|
||||
pub use unsupported::{Thread, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||
}
|
||||
target_family = "unix" => {
|
||||
mod unix;
|
||||
pub use unix::{Thread, available_parallelism, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||
#[cfg(not(any(
|
||||
target_env = "newlib",
|
||||
target_os = "l4re",
|
||||
target_os = "emscripten",
|
||||
target_os = "redox",
|
||||
target_os = "hurd",
|
||||
target_os = "aix",
|
||||
)))]
|
||||
pub use unix::set_name;
|
||||
#[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 use unix::sleep_until;
|
||||
#[expect(dead_code)]
|
||||
mod unsupported;
|
||||
#[cfg(any(
|
||||
target_env = "newlib",
|
||||
target_os = "l4re",
|
||||
target_os = "emscripten",
|
||||
target_os = "redox",
|
||||
target_os = "hurd",
|
||||
target_os = "aix",
|
||||
))]
|
||||
pub use unsupported::set_name;
|
||||
}
|
||||
all(target_os = "wasi", target_env = "p1") => {
|
||||
mod wasip1;
|
||||
pub use wasip1::{DEFAULT_MIN_STACK_SIZE, sleep, yield_now};
|
||||
#[cfg(target_feature = "atomics")]
|
||||
pub use wasip1::{Thread, available_parallelism};
|
||||
#[expect(dead_code)]
|
||||
mod unsupported;
|
||||
pub use unsupported::{current_os_id, set_name};
|
||||
#[cfg(not(target_feature = "atomics"))]
|
||||
pub use unsupported::{Thread, available_parallelism};
|
||||
}
|
||||
all(target_os = "wasi", target_env = "p2") => {
|
||||
mod wasip2;
|
||||
pub use wasip2::{sleep, sleep_until};
|
||||
#[expect(dead_code)]
|
||||
mod unsupported;
|
||||
// Note that unlike WASIp1 even if the wasm `atomics` feature is enabled
|
||||
// there is no support for threads, not even experimentally, not even in
|
||||
// wasi-libc. Thus this is unconditionally unsupported.
|
||||
pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||
}
|
||||
all(target_family = "wasm", target_feature = "atomics") => {
|
||||
mod wasm;
|
||||
pub use wasm::sleep;
|
||||
|
||||
#[expect(dead_code)]
|
||||
mod unsupported;
|
||||
pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||
}
|
||||
target_os = "windows" => {
|
||||
mod windows;
|
||||
pub use windows::{Thread, available_parallelism, current_os_id, set_name, set_name_wide, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||
}
|
||||
target_os = "xous" => {
|
||||
mod xous;
|
||||
pub use xous::{Thread, available_parallelism, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||
|
||||
#[expect(dead_code)]
|
||||
mod unsupported;
|
||||
pub use unsupported::{current_os_id, set_name};
|
||||
}
|
||||
_ => {
|
||||
mod unsupported;
|
||||
pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||
}
|
||||
}
|
||||
|
||||
#[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",
|
||||
all(target_os = "wasi", target_env = "p2"),
|
||||
)))]
|
||||
pub fn sleep_until(deadline: crate::time::Instant) {
|
||||
use crate::time::Instant;
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
if let Some(delay) = deadline.checked_duration_since(now) {
|
||||
sleep(delay);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,8 @@
|
|||
#![cfg_attr(test, allow(dead_code))] // why is this necessary?
|
||||
|
||||
use super::abi::{thread, usercalls};
|
||||
use super::unsupported;
|
||||
use crate::ffi::CStr;
|
||||
use crate::io;
|
||||
use crate::num::NonZero;
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::sys::pal::abi::{thread, usercalls};
|
||||
use crate::time::Duration;
|
||||
|
||||
pub struct Thread(task_queue::JoinHandle);
|
||||
|
||||
|
|
@ -108,51 +105,27 @@ impl Thread {
|
|||
Ok(Thread(handle))
|
||||
}
|
||||
|
||||
pub(super) fn entry() -> JoinNotifier {
|
||||
pub(crate) fn entry() -> JoinNotifier {
|
||||
let mut pending_tasks = task_queue::lock();
|
||||
let task = rtunwrap!(Some, pending_tasks.pop());
|
||||
drop(pending_tasks); // make sure to not hold the task queue lock longer than necessary
|
||||
task.run()
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
let wait_error = rtunwrap!(Err, usercalls::wait(0, usercalls::raw::WAIT_NO));
|
||||
rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock);
|
||||
}
|
||||
|
||||
/// SGX should protect in-enclave data from the outside (attacker),
|
||||
/// so there should be no data leakage to the OS,
|
||||
/// and therefore also no 1-1 mapping between SGX thread names and OS thread names.
|
||||
///
|
||||
/// This is why the method is intentionally No-Op.
|
||||
pub fn set_name(_name: &CStr) {
|
||||
// Note that the internally visible SGX thread name is already provided
|
||||
// by the platform-agnostic (target-agnostic) Rust thread code.
|
||||
// This can be observed in the [`std::thread::tests::test_named_thread`] test,
|
||||
// which succeeds as-is with the SGX target.
|
||||
}
|
||||
|
||||
pub fn sleep(dur: Duration) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn current_os_id() -> Option<u64> {
|
||||
pub fn current_os_id() -> Option<u64> {
|
||||
Some(thread::current().addr().get() as u64)
|
||||
}
|
||||
|
||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||
unsupported()
|
||||
pub fn sleep(dur: Duration) {
|
||||
usercalls::wait_timeout(0, dur, || true);
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
let wait_error = rtunwrap!(Err, usercalls::wait(0, usercalls::raw::WAIT_NO));
|
||||
rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
//! Thread implementation backed by μITRON tasks. Assumes `acre_tsk` and
|
||||
//! `exd_tsk` are available.
|
||||
|
||||
use super::error::{ItronError, expect_success, expect_success_aborting};
|
||||
use super::time::dur2reltims;
|
||||
use super::{abi, task};
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::ffi::CStr;
|
||||
use crate::mem::ManuallyDrop;
|
||||
use crate::num::NonZero;
|
||||
use crate::ptr::NonNull;
|
||||
use crate::sync::atomic::{Atomic, AtomicUsize, Ordering};
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::sys::pal::itron::error::{ItronError, expect_success, expect_success_aborting};
|
||||
use crate::sys::pal::itron::time::dur2reltims;
|
||||
use crate::sys::pal::itron::{abi, task};
|
||||
use crate::time::Duration;
|
||||
use crate::{hint, io};
|
||||
|
||||
pub struct Thread {
|
||||
|
|
@ -195,28 +193,6 @@ impl Thread {
|
|||
Ok(Self { p_inner, task: new_task })
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
expect_success(unsafe { abi::rot_rdq(abi::TPRI_SELF) }, &"rot_rdq");
|
||||
}
|
||||
|
||||
pub fn set_name(_name: &CStr) {
|
||||
// nope
|
||||
}
|
||||
|
||||
pub fn sleep(dur: Duration) {
|
||||
for timeout in dur2reltims(dur) {
|
||||
expect_success(unsafe { abi::dly_tsk(timeout) }, &"dly_tsk");
|
||||
}
|
||||
}
|
||||
|
||||
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() };
|
||||
|
|
@ -361,10 +337,12 @@ unsafe fn terminate_and_delete_current_task() -> ! {
|
|||
unsafe { crate::hint::unreachable_unchecked() };
|
||||
}
|
||||
|
||||
pub(crate) fn current_os_id() -> Option<u64> {
|
||||
None
|
||||
pub fn yield_now() {
|
||||
expect_success(unsafe { abi::rot_rdq(abi::TPRI_SELF) }, &"rot_rdq");
|
||||
}
|
||||
|
||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||
super::unsupported()
|
||||
pub fn sleep(dur: Duration) {
|
||||
for timeout in dur2reltims(dur) {
|
||||
expect_success(unsafe { abi::dly_tsk(timeout) }, &"dly_tsk");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,18 @@
|
|||
use crate::ffi::CStr;
|
||||
use crate::mem::{self, ManuallyDrop};
|
||||
use crate::num::NonZero;
|
||||
use crate::sys::os;
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::time::Duration;
|
||||
use crate::{cmp, io, ptr};
|
||||
|
||||
pub const DEFAULT_MIN_STACK_SIZE: usize = 8 * 1024;
|
||||
|
||||
unsafe extern "C" {
|
||||
safe fn TEE_Wait(timeout: u32) -> u32;
|
||||
}
|
||||
|
||||
fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
|
||||
libc::PTHREAD_STACK_MIN.try_into().expect("Infallible")
|
||||
}
|
||||
|
||||
pub struct Thread {
|
||||
id: libc::pthread_t,
|
||||
}
|
||||
|
|
@ -16,10 +22,6 @@ pub struct Thread {
|
|||
unsafe impl Send for Thread {}
|
||||
unsafe impl Sync for Thread {}
|
||||
|
||||
unsafe extern "C" {
|
||||
pub fn TEE_Wait(timeout: u32) -> u32;
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
||||
pub unsafe fn new(
|
||||
|
|
@ -74,7 +76,7 @@ impl Thread {
|
|||
} else {
|
||||
// The new thread will start running earliest after the next yield.
|
||||
// We add a yield here, so that the user does not have to.
|
||||
Thread::yield_now();
|
||||
yield_now();
|
||||
Ok(Thread { id: native })
|
||||
};
|
||||
|
||||
|
|
@ -91,36 +93,6 @@ impl Thread {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
let ret = unsafe { libc::sched_yield() };
|
||||
debug_assert_eq!(ret, 0);
|
||||
}
|
||||
|
||||
/// This does not do anything on teeos
|
||||
pub fn set_name(_name: &CStr) {
|
||||
// Both pthread_setname_np and prctl are not available to the TA,
|
||||
// so we can't implement this currently. If the need arises please
|
||||
// contact the teeos rustzone team.
|
||||
}
|
||||
|
||||
/// only main thread could wait for sometime in teeos
|
||||
pub fn sleep(dur: Duration) {
|
||||
let sleep_millis = dur.as_millis();
|
||||
let final_sleep: u32 =
|
||||
if sleep_millis >= u32::MAX as u128 { u32::MAX } else { sleep_millis as u32 };
|
||||
unsafe {
|
||||
let _ = TEE_Wait(final_sleep);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
@ -128,10 +100,6 @@ impl Thread {
|
|||
assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret));
|
||||
}
|
||||
|
||||
pub fn id(&self) -> libc::pthread_t {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn into_id(self) -> libc::pthread_t {
|
||||
ManuallyDrop::new(self).id
|
||||
}
|
||||
|
|
@ -144,16 +112,15 @@ impl Drop for Thread {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn current_os_id() -> Option<u64> {
|
||||
None
|
||||
pub fn yield_now() {
|
||||
let ret = unsafe { libc::sched_yield() };
|
||||
debug_assert_eq!(ret, 0);
|
||||
}
|
||||
|
||||
// Note: Both `sched_getaffinity` and `sysconf` are available but not functional on
|
||||
// teeos, so this function always returns an Error!
|
||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||
Err(io::Error::UNKNOWN_THREAD_COUNT)
|
||||
}
|
||||
|
||||
fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
|
||||
libc::PTHREAD_STACK_MIN.try_into().expect("Infallible")
|
||||
/// only main thread could wait for sometime in teeos
|
||||
pub fn sleep(dur: Duration) {
|
||||
let sleep_millis = dur.as_millis();
|
||||
let final_sleep: u32 =
|
||||
if sleep_millis >= u32::MAX as u128 { u32::MAX } else { sleep_millis as u32 };
|
||||
TEE_Wait(final_sleep);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,66 +1,25 @@
|
|||
use super::unsupported;
|
||||
use crate::ffi::CStr;
|
||||
use crate::io;
|
||||
use crate::num::NonZero;
|
||||
use crate::ptr::NonNull;
|
||||
use crate::time::{Duration, Instant};
|
||||
|
||||
pub struct Thread(!);
|
||||
|
||||
pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024;
|
||||
|
||||
impl Thread {
|
||||
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
||||
pub unsafe fn new(
|
||||
_stack: usize,
|
||||
_name: Option<&str>,
|
||||
_p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
pub fn set_name(_name: &CStr) {
|
||||
// nope
|
||||
}
|
||||
|
||||
pub fn sleep(dur: Duration) {
|
||||
let boot_services: NonNull<r_efi::efi::BootServices> =
|
||||
crate::os::uefi::env::boot_services().expect("can't sleep").cast();
|
||||
let mut dur_ms = dur.as_micros();
|
||||
// ceil up to the nearest microsecond
|
||||
if dur.subsec_nanos() % 1000 > 0 {
|
||||
dur_ms += 1;
|
||||
}
|
||||
|
||||
while dur_ms > 0 {
|
||||
let ms = crate::cmp::min(dur_ms, usize::MAX as u128);
|
||||
let _ = unsafe { ((*boot_services.as_ptr()).stall)(ms as usize) };
|
||||
dur_ms -= ms;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn current_os_id() -> Option<u64> {
|
||||
None
|
||||
}
|
||||
use crate::time::Duration;
|
||||
|
||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||
// UEFI is single threaded
|
||||
Ok(NonZero::new(1).unwrap())
|
||||
}
|
||||
|
||||
pub fn sleep(dur: Duration) {
|
||||
let boot_services: NonNull<r_efi::efi::BootServices> =
|
||||
crate::os::uefi::env::boot_services().expect("can't sleep").cast();
|
||||
let mut dur_ms = dur.as_micros();
|
||||
// ceil up to the nearest microsecond
|
||||
if dur.subsec_nanos() % 1000 > 0 {
|
||||
dur_ms += 1;
|
||||
}
|
||||
|
||||
while dur_ms > 0 {
|
||||
let ms = crate::cmp::min(dur_ms, usize::MAX as u128);
|
||||
let _ = unsafe { ((*boot_services.as_ptr()).stall)(ms as usize) };
|
||||
dur_ms -= ms;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
#[cfg(not(any(
|
||||
target_env = "newlib",
|
||||
target_os = "l4re",
|
||||
target_os = "emscripten",
|
||||
target_os = "redox",
|
||||
target_os = "hurd",
|
||||
target_os = "aix",
|
||||
)))]
|
||||
use crate::ffi::CStr;
|
||||
use crate::mem::{self, ManuallyDrop};
|
||||
use crate::num::NonZero;
|
||||
|
|
@ -6,7 +14,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, Instant};
|
||||
use crate::time::Duration;
|
||||
use crate::{cmp, io, ptr};
|
||||
#[cfg(not(any(
|
||||
target_os = "l4re",
|
||||
|
|
@ -121,273 +129,6 @@ impl Thread {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
let ret = unsafe { libc::sched_yield() };
|
||||
debug_assert_eq!(ret, 0);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
pub fn set_name(name: &CStr) {
|
||||
const PR_SET_NAME: libc::c_int = 15;
|
||||
unsafe {
|
||||
let res = libc::prctl(
|
||||
PR_SET_NAME,
|
||||
name.as_ptr(),
|
||||
0 as libc::c_ulong,
|
||||
0 as libc::c_ulong,
|
||||
0 as libc::c_ulong,
|
||||
);
|
||||
// We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
|
||||
debug_assert_eq!(res, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly",
|
||||
target_os = "nuttx",
|
||||
target_os = "cygwin"
|
||||
))]
|
||||
pub fn set_name(name: &CStr) {
|
||||
unsafe {
|
||||
cfg_select! {
|
||||
any(target_os = "linux", target_os = "cygwin") => {
|
||||
// Linux and Cygwin limits the allowed length of the name.
|
||||
const TASK_COMM_LEN: usize = 16;
|
||||
let name = truncate_cstr::<{ TASK_COMM_LEN }>(name);
|
||||
}
|
||||
_ => {
|
||||
// FreeBSD, DragonFly BSD and NuttX do not enforce length limits.
|
||||
}
|
||||
};
|
||||
// Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20 for Linux,
|
||||
// FreeBSD 12.2 and 13.0, and DragonFly BSD 6.0.
|
||||
let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr());
|
||||
// We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
|
||||
debug_assert_eq!(res, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "openbsd")]
|
||||
pub fn set_name(name: &CStr) {
|
||||
unsafe {
|
||||
libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_vendor = "apple")]
|
||||
pub fn set_name(name: &CStr) {
|
||||
unsafe {
|
||||
let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name);
|
||||
let res = libc::pthread_setname_np(name.as_ptr());
|
||||
// We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
|
||||
debug_assert_eq!(res, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "netbsd")]
|
||||
pub fn set_name(name: &CStr) {
|
||||
unsafe {
|
||||
let res = libc::pthread_setname_np(
|
||||
libc::pthread_self(),
|
||||
c"%s".as_ptr(),
|
||||
name.as_ptr() as *mut libc::c_void,
|
||||
);
|
||||
debug_assert_eq!(res, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))]
|
||||
pub fn set_name(name: &CStr) {
|
||||
weak!(
|
||||
fn pthread_setname_np(
|
||||
thread: libc::pthread_t,
|
||||
name: *const libc::c_char,
|
||||
) -> libc::c_int;
|
||||
);
|
||||
|
||||
if let Some(f) = pthread_setname_np.get() {
|
||||
#[cfg(target_os = "nto")]
|
||||
const THREAD_NAME_MAX: usize = libc::_NTO_THREAD_NAME_MAX as usize;
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
const THREAD_NAME_MAX: usize = 32;
|
||||
|
||||
let name = truncate_cstr::<{ THREAD_NAME_MAX }>(name);
|
||||
let res = unsafe { f(libc::pthread_self(), name.as_ptr()) };
|
||||
debug_assert_eq!(res, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "fuchsia")]
|
||||
pub fn set_name(name: &CStr) {
|
||||
use super::fuchsia::*;
|
||||
unsafe {
|
||||
zx_object_set_property(
|
||||
zx_thread_self(),
|
||||
ZX_PROP_NAME,
|
||||
name.as_ptr() as *const libc::c_void,
|
||||
name.to_bytes().len(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "haiku")]
|
||||
pub fn set_name(name: &CStr) {
|
||||
unsafe {
|
||||
let thread_self = libc::find_thread(ptr::null_mut());
|
||||
let res = libc::rename_thread(thread_self, name.as_ptr());
|
||||
// We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
|
||||
debug_assert_eq!(res, libc::B_OK);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "vxworks")]
|
||||
pub fn set_name(name: &CStr) {
|
||||
let mut name = truncate_cstr::<{ (libc::VX_TASK_RENAME_LENGTH - 1) as usize }>(name);
|
||||
let res = unsafe { libc::taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) };
|
||||
debug_assert_eq!(res, libc::OK);
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_env = "newlib",
|
||||
target_os = "l4re",
|
||||
target_os = "emscripten",
|
||||
target_os = "redox",
|
||||
target_os = "hurd",
|
||||
target_os = "aix",
|
||||
))]
|
||||
pub fn set_name(_name: &CStr) {
|
||||
// Newlib and Emscripten have no way to set a thread name.
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "espidf"))]
|
||||
pub fn sleep(dur: Duration) {
|
||||
let mut secs = dur.as_secs();
|
||||
let mut nsecs = dur.subsec_nanos() as _;
|
||||
|
||||
// If we're awoken with a signal then the return value will be -1 and
|
||||
// nanosleep will fill in `ts` with the remaining time.
|
||||
unsafe {
|
||||
while secs > 0 || nsecs > 0 {
|
||||
let mut ts = libc::timespec {
|
||||
tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t,
|
||||
tv_nsec: nsecs,
|
||||
};
|
||||
secs -= ts.tv_sec as u64;
|
||||
let ts_ptr = &raw mut ts;
|
||||
if libc::nanosleep(ts_ptr, ts_ptr) == -1 {
|
||||
assert_eq!(os::errno(), libc::EINTR);
|
||||
secs += ts.tv_sec as u64;
|
||||
nsecs = ts.tv_nsec;
|
||||
} else {
|
||||
nsecs = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "espidf")]
|
||||
pub fn sleep(dur: Duration) {
|
||||
// ESP-IDF does not have `nanosleep`, so we use `usleep` instead.
|
||||
// As per the documentation of `usleep`, it is expected to support
|
||||
// sleep times as big as at least up to 1 second.
|
||||
//
|
||||
// ESP-IDF does support almost up to `u32::MAX`, but due to a potential integer overflow in its
|
||||
// `usleep` implementation
|
||||
// (https://github.com/espressif/esp-idf/blob/d7ca8b94c852052e3bc33292287ef4dd62c9eeb1/components/newlib/time.c#L210),
|
||||
// we limit the sleep time to the maximum one that would not cause the underlying `usleep` implementation to overflow
|
||||
// (`portTICK_PERIOD_MS` can be anything between 1 to 1000, and is 10 by default).
|
||||
const MAX_MICROS: u32 = u32::MAX - 1_000_000 - 1;
|
||||
|
||||
// Add any nanoseconds smaller than a microsecond as an extra microsecond
|
||||
// so as to comply with the `std::thread::sleep` contract which mandates
|
||||
// implementations to sleep for _at least_ the provided `dur`.
|
||||
// We can't overflow `micros` as it is a `u128`, while `Duration` is a pair of
|
||||
// (`u64` secs, `u32` nanos), where the nanos are strictly smaller than 1 second
|
||||
// (i.e. < 1_000_000_000)
|
||||
let mut micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 };
|
||||
|
||||
while micros > 0 {
|
||||
let st = if micros > MAX_MICROS as u128 { MAX_MICROS } else { micros as u32 };
|
||||
unsafe {
|
||||
libc::usleep(st);
|
||||
}
|
||||
|
||||
micros -= st as u128;
|
||||
}
|
||||
}
|
||||
|
||||
// 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()) };
|
||||
|
|
@ -410,84 +151,6 @@ impl Drop for Thread {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn current_os_id() -> Option<u64> {
|
||||
// Most Unix platforms have a way to query an integer ID of the current thread, all with
|
||||
// slightly different spellings.
|
||||
//
|
||||
// The OS thread ID is used rather than `pthread_self` so as to match what will be displayed
|
||||
// for process inspection (debuggers, trace, `top`, etc.).
|
||||
cfg_select! {
|
||||
// Most platforms have a function returning a `pid_t` or int, which is an `i32`.
|
||||
any(target_os = "android", target_os = "linux") => {
|
||||
use crate::sys::weak::syscall;
|
||||
|
||||
// `libc::gettid` is only available on glibc 2.30+, but the syscall is available
|
||||
// since Linux 2.4.11.
|
||||
syscall!(fn gettid() -> libc::pid_t;);
|
||||
|
||||
// SAFETY: FFI call with no preconditions.
|
||||
let id: libc::pid_t = unsafe { gettid() };
|
||||
Some(id as u64)
|
||||
}
|
||||
target_os = "nto" => {
|
||||
// SAFETY: FFI call with no preconditions.
|
||||
let id: libc::pid_t = unsafe { libc::gettid() };
|
||||
Some(id as u64)
|
||||
}
|
||||
target_os = "openbsd" => {
|
||||
// SAFETY: FFI call with no preconditions.
|
||||
let id: libc::pid_t = unsafe { libc::getthrid() };
|
||||
Some(id as u64)
|
||||
}
|
||||
target_os = "freebsd" => {
|
||||
// SAFETY: FFI call with no preconditions.
|
||||
let id: libc::c_int = unsafe { libc::pthread_getthreadid_np() };
|
||||
Some(id as u64)
|
||||
}
|
||||
target_os = "netbsd" => {
|
||||
// SAFETY: FFI call with no preconditions.
|
||||
let id: libc::lwpid_t = unsafe { libc::_lwp_self() };
|
||||
Some(id as u64)
|
||||
}
|
||||
any(target_os = "illumos", target_os = "solaris") => {
|
||||
// On Illumos and Solaris, the `pthread_t` is the same as the OS thread ID.
|
||||
// SAFETY: FFI call with no preconditions.
|
||||
let id: libc::pthread_t = unsafe { libc::pthread_self() };
|
||||
Some(id as u64)
|
||||
}
|
||||
target_vendor = "apple" => {
|
||||
// Apple allows querying arbitrary thread IDs, `thread=NULL` queries the current thread.
|
||||
let mut id = 0u64;
|
||||
// SAFETY: `thread_id` is a valid pointer, no other preconditions.
|
||||
let status: libc::c_int = unsafe { libc::pthread_threadid_np(0, &mut id) };
|
||||
if status == 0 {
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
// Other platforms don't have an OS thread ID or don't have a way to access it.
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "nto",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
target_os = "vxworks",
|
||||
target_os = "cygwin",
|
||||
target_vendor = "apple",
|
||||
))]
|
||||
fn truncate_cstr<const MAX_WITH_NUL: usize>(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] {
|
||||
let mut result = [0; MAX_WITH_NUL];
|
||||
for (src, dst) in cstr.to_bytes().iter().zip(&mut result[..MAX_WITH_NUL - 1]) {
|
||||
*dst = *src as libc::c_char;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||
cfg_select! {
|
||||
any(
|
||||
|
|
@ -668,6 +331,318 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn current_os_id() -> Option<u64> {
|
||||
// Most Unix platforms have a way to query an integer ID of the current thread, all with
|
||||
// slightly different spellings.
|
||||
//
|
||||
// The OS thread ID is used rather than `pthread_self` so as to match what will be displayed
|
||||
// for process inspection (debuggers, trace, `top`, etc.).
|
||||
cfg_select! {
|
||||
// Most platforms have a function returning a `pid_t` or int, which is an `i32`.
|
||||
any(target_os = "android", target_os = "linux") => {
|
||||
use crate::sys::pal::weak::syscall;
|
||||
|
||||
// `libc::gettid` is only available on glibc 2.30+, but the syscall is available
|
||||
// since Linux 2.4.11.
|
||||
syscall!(fn gettid() -> libc::pid_t;);
|
||||
|
||||
// SAFETY: FFI call with no preconditions.
|
||||
let id: libc::pid_t = unsafe { gettid() };
|
||||
Some(id as u64)
|
||||
}
|
||||
target_os = "nto" => {
|
||||
// SAFETY: FFI call with no preconditions.
|
||||
let id: libc::pid_t = unsafe { libc::gettid() };
|
||||
Some(id as u64)
|
||||
}
|
||||
target_os = "openbsd" => {
|
||||
// SAFETY: FFI call with no preconditions.
|
||||
let id: libc::pid_t = unsafe { libc::getthrid() };
|
||||
Some(id as u64)
|
||||
}
|
||||
target_os = "freebsd" => {
|
||||
// SAFETY: FFI call with no preconditions.
|
||||
let id: libc::c_int = unsafe { libc::pthread_getthreadid_np() };
|
||||
Some(id as u64)
|
||||
}
|
||||
target_os = "netbsd" => {
|
||||
// SAFETY: FFI call with no preconditions.
|
||||
let id: libc::lwpid_t = unsafe { libc::_lwp_self() };
|
||||
Some(id as u64)
|
||||
}
|
||||
any(target_os = "illumos", target_os = "solaris") => {
|
||||
// On Illumos and Solaris, the `pthread_t` is the same as the OS thread ID.
|
||||
// SAFETY: FFI call with no preconditions.
|
||||
let id: libc::pthread_t = unsafe { libc::pthread_self() };
|
||||
Some(id as u64)
|
||||
}
|
||||
target_vendor = "apple" => {
|
||||
// Apple allows querying arbitrary thread IDs, `thread=NULL` queries the current thread.
|
||||
let mut id = 0u64;
|
||||
// SAFETY: `thread_id` is a valid pointer, no other preconditions.
|
||||
let status: libc::c_int = unsafe { libc::pthread_threadid_np(0, &mut id) };
|
||||
if status == 0 {
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
// Other platforms don't have an OS thread ID or don't have a way to access it.
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "nto",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
target_os = "vxworks",
|
||||
target_os = "cygwin",
|
||||
target_vendor = "apple",
|
||||
))]
|
||||
fn truncate_cstr<const MAX_WITH_NUL: usize>(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] {
|
||||
let mut result = [0; MAX_WITH_NUL];
|
||||
for (src, dst) in cstr.to_bytes().iter().zip(&mut result[..MAX_WITH_NUL - 1]) {
|
||||
*dst = *src as libc::c_char;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
pub fn set_name(name: &CStr) {
|
||||
const PR_SET_NAME: libc::c_int = 15;
|
||||
unsafe {
|
||||
let res = libc::prctl(
|
||||
PR_SET_NAME,
|
||||
name.as_ptr(),
|
||||
0 as libc::c_ulong,
|
||||
0 as libc::c_ulong,
|
||||
0 as libc::c_ulong,
|
||||
);
|
||||
// We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
|
||||
debug_assert_eq!(res, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly",
|
||||
target_os = "nuttx",
|
||||
target_os = "cygwin"
|
||||
))]
|
||||
pub fn set_name(name: &CStr) {
|
||||
unsafe {
|
||||
cfg_select! {
|
||||
any(target_os = "linux", target_os = "cygwin") => {
|
||||
// Linux and Cygwin limits the allowed length of the name.
|
||||
const TASK_COMM_LEN: usize = 16;
|
||||
let name = truncate_cstr::<{ TASK_COMM_LEN }>(name);
|
||||
}
|
||||
_ => {
|
||||
// FreeBSD, DragonFly BSD and NuttX do not enforce length limits.
|
||||
}
|
||||
};
|
||||
// Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20 for Linux,
|
||||
// FreeBSD 12.2 and 13.0, and DragonFly BSD 6.0.
|
||||
let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr());
|
||||
// We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
|
||||
debug_assert_eq!(res, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "openbsd")]
|
||||
pub fn set_name(name: &CStr) {
|
||||
unsafe {
|
||||
libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_vendor = "apple")]
|
||||
pub fn set_name(name: &CStr) {
|
||||
unsafe {
|
||||
let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name);
|
||||
let res = libc::pthread_setname_np(name.as_ptr());
|
||||
// We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
|
||||
debug_assert_eq!(res, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "netbsd")]
|
||||
pub fn set_name(name: &CStr) {
|
||||
unsafe {
|
||||
let res = libc::pthread_setname_np(
|
||||
libc::pthread_self(),
|
||||
c"%s".as_ptr(),
|
||||
name.as_ptr() as *mut libc::c_void,
|
||||
);
|
||||
debug_assert_eq!(res, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))]
|
||||
pub fn set_name(name: &CStr) {
|
||||
weak!(
|
||||
fn pthread_setname_np(thread: libc::pthread_t, name: *const libc::c_char) -> libc::c_int;
|
||||
);
|
||||
|
||||
if let Some(f) = pthread_setname_np.get() {
|
||||
#[cfg(target_os = "nto")]
|
||||
const THREAD_NAME_MAX: usize = libc::_NTO_THREAD_NAME_MAX as usize;
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
const THREAD_NAME_MAX: usize = 32;
|
||||
|
||||
let name = truncate_cstr::<{ THREAD_NAME_MAX }>(name);
|
||||
let res = unsafe { f(libc::pthread_self(), name.as_ptr()) };
|
||||
debug_assert_eq!(res, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "fuchsia")]
|
||||
pub fn set_name(name: &CStr) {
|
||||
use crate::sys::pal::fuchsia::*;
|
||||
unsafe {
|
||||
zx_object_set_property(
|
||||
zx_thread_self(),
|
||||
ZX_PROP_NAME,
|
||||
name.as_ptr() as *const libc::c_void,
|
||||
name.to_bytes().len(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "haiku")]
|
||||
pub fn set_name(name: &CStr) {
|
||||
unsafe {
|
||||
let thread_self = libc::find_thread(ptr::null_mut());
|
||||
let res = libc::rename_thread(thread_self, name.as_ptr());
|
||||
// We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
|
||||
debug_assert_eq!(res, libc::B_OK);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "vxworks")]
|
||||
pub fn set_name(name: &CStr) {
|
||||
let mut name = truncate_cstr::<{ (libc::VX_TASK_RENAME_LENGTH - 1) as usize }>(name);
|
||||
let res = unsafe { libc::taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) };
|
||||
debug_assert_eq!(res, libc::OK);
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "espidf"))]
|
||||
pub fn sleep(dur: Duration) {
|
||||
let mut secs = dur.as_secs();
|
||||
let mut nsecs = dur.subsec_nanos() as _;
|
||||
|
||||
// If we're awoken with a signal then the return value will be -1 and
|
||||
// nanosleep will fill in `ts` with the remaining time.
|
||||
unsafe {
|
||||
while secs > 0 || nsecs > 0 {
|
||||
let mut ts = libc::timespec {
|
||||
tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t,
|
||||
tv_nsec: nsecs,
|
||||
};
|
||||
secs -= ts.tv_sec as u64;
|
||||
let ts_ptr = &raw mut ts;
|
||||
if libc::nanosleep(ts_ptr, ts_ptr) == -1 {
|
||||
assert_eq!(os::errno(), libc::EINTR);
|
||||
secs += ts.tv_sec as u64;
|
||||
nsecs = ts.tv_nsec;
|
||||
} else {
|
||||
nsecs = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "espidf")]
|
||||
pub fn sleep(dur: Duration) {
|
||||
// ESP-IDF does not have `nanosleep`, so we use `usleep` instead.
|
||||
// As per the documentation of `usleep`, it is expected to support
|
||||
// sleep times as big as at least up to 1 second.
|
||||
//
|
||||
// ESP-IDF does support almost up to `u32::MAX`, but due to a potential integer overflow in its
|
||||
// `usleep` implementation
|
||||
// (https://github.com/espressif/esp-idf/blob/d7ca8b94c852052e3bc33292287ef4dd62c9eeb1/components/newlib/time.c#L210),
|
||||
// we limit the sleep time to the maximum one that would not cause the underlying `usleep` implementation to overflow
|
||||
// (`portTICK_PERIOD_MS` can be anything between 1 to 1000, and is 10 by default).
|
||||
const MAX_MICROS: u32 = u32::MAX - 1_000_000 - 1;
|
||||
|
||||
// Add any nanoseconds smaller than a microsecond as an extra microsecond
|
||||
// so as to comply with the `std::thread::sleep` contract which mandates
|
||||
// implementations to sleep for _at least_ the provided `dur`.
|
||||
// We can't overflow `micros` as it is a `u128`, while `Duration` is a pair of
|
||||
// (`u64` secs, `u32` nanos), where the nanos are strictly smaller than 1 second
|
||||
// (i.e. < 1_000_000_000)
|
||||
let mut micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 };
|
||||
|
||||
while micros > 0 {
|
||||
let st = if micros > MAX_MICROS as u128 { MAX_MICROS } else { micros as u32 };
|
||||
unsafe {
|
||||
libc::usleep(st);
|
||||
}
|
||||
|
||||
micros -= st as u128;
|
||||
}
|
||||
}
|
||||
|
||||
// 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: crate::time::Instant) {
|
||||
use crate::time::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) {
|
||||
sleep(delay);
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
unsafe {
|
||||
// When we get interrupted (res = EINTR) call clock_nanosleep again
|
||||
loop {
|
||||
let res = libc::clock_nanosleep(
|
||||
crate::sys::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"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
let ret = unsafe { libc::sched_yield() };
|
||||
debug_assert_eq!(ret, 0);
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
mod cgroups {
|
||||
//! Currently not covered
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
use super::unsupported;
|
||||
use crate::ffi::CStr;
|
||||
use crate::io;
|
||||
use crate::num::NonZero;
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::time::Duration;
|
||||
|
||||
pub struct Thread(!);
|
||||
|
||||
|
|
@ -15,23 +14,7 @@ impl Thread {
|
|||
_name: Option<&str>,
|
||||
_p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
pub fn set_name(_name: &CStr) {
|
||||
// nope
|
||||
}
|
||||
|
||||
pub fn sleep(_dur: Duration) {
|
||||
panic!("can't sleep");
|
||||
}
|
||||
|
||||
pub fn sleep_until(_deadline: Instant) {
|
||||
panic!("can't sleep");
|
||||
Err(io::Error::UNSUPPORTED_PLATFORM)
|
||||
}
|
||||
|
||||
pub fn join(self) {
|
||||
|
|
@ -39,10 +22,22 @@ impl Thread {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn current_os_id() -> Option<u64> {
|
||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||
Err(io::Error::UNKNOWN_THREAD_COUNT)
|
||||
}
|
||||
|
||||
pub fn current_os_id() -> Option<u64> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||
unsupported()
|
||||
pub fn yield_now() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
pub fn set_name(_name: &CStr) {
|
||||
// nope
|
||||
}
|
||||
|
||||
pub fn sleep(_dur: Duration) {
|
||||
panic!("can't sleep");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,214 +1,185 @@
|
|||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
use crate::ffi::CStr;
|
||||
#[cfg(target_feature = "atomics")]
|
||||
use crate::io;
|
||||
use crate::mem;
|
||||
#[cfg(target_feature = "atomics")]
|
||||
use crate::num::NonZero;
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::{io, mem};
|
||||
#[cfg(target_feature = "atomics")]
|
||||
use crate::sys::os;
|
||||
use crate::time::Duration;
|
||||
#[cfg(target_feature = "atomics")]
|
||||
use crate::{cmp, ptr};
|
||||
|
||||
cfg_select! {
|
||||
target_feature = "atomics" => {
|
||||
use crate::cmp;
|
||||
use crate::ptr;
|
||||
use crate::sys::os;
|
||||
// Add a few symbols not in upstream `libc` just yet.
|
||||
mod libc {
|
||||
pub use crate::ffi;
|
||||
pub use libc::*;
|
||||
// Add a few symbols not in upstream `libc` just yet.
|
||||
#[cfg(target_feature = "atomics")]
|
||||
mod libc {
|
||||
pub use libc::*;
|
||||
|
||||
// defined in wasi-libc
|
||||
// https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108
|
||||
#[repr(C)]
|
||||
union pthread_attr_union {
|
||||
__i: [ffi::c_int; if size_of::<ffi::c_long>() == 8 { 14 } else { 9 }],
|
||||
__vi: [ffi::c_int; if size_of::<ffi::c_long>() == 8 { 14 } else { 9 }],
|
||||
__s: [ffi::c_ulong; if size_of::<ffi::c_long>() == 8 { 7 } else { 9 }],
|
||||
}
|
||||
pub use crate::ffi;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct pthread_attr_t {
|
||||
__u: pthread_attr_union,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type pthread_t = *mut ffi::c_void;
|
||||
|
||||
pub const _SC_NPROCESSORS_ONLN: ffi::c_int = 84;
|
||||
|
||||
unsafe extern "C" {
|
||||
pub fn pthread_create(
|
||||
native: *mut pthread_t,
|
||||
attr: *const pthread_attr_t,
|
||||
f: extern "C" fn(*mut ffi::c_void) -> *mut ffi::c_void,
|
||||
value: *mut ffi::c_void,
|
||||
) -> ffi::c_int;
|
||||
pub fn pthread_join(native: pthread_t, value: *mut *mut ffi::c_void) -> ffi::c_int;
|
||||
pub fn pthread_attr_init(attrp: *mut pthread_attr_t) -> ffi::c_int;
|
||||
pub fn pthread_attr_setstacksize(
|
||||
attr: *mut pthread_attr_t,
|
||||
stack_size: libc::size_t,
|
||||
) -> ffi::c_int;
|
||||
pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> ffi::c_int;
|
||||
pub fn pthread_detach(thread: pthread_t) -> ffi::c_int;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Thread {
|
||||
id: libc::pthread_t,
|
||||
}
|
||||
|
||||
impl Drop for Thread {
|
||||
fn drop(&mut self) {
|
||||
let ret = unsafe { libc::pthread_detach(self.id) };
|
||||
debug_assert_eq!(ret, 0);
|
||||
}
|
||||
}
|
||||
// defined in wasi-libc
|
||||
// https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108
|
||||
#[repr(C)]
|
||||
union pthread_attr_union {
|
||||
__i: [ffi::c_int; if size_of::<ffi::c_long>() == 8 { 14 } else { 9 }],
|
||||
__vi: [ffi::c_int; if size_of::<ffi::c_long>() == 8 { 14 } else { 9 }],
|
||||
__s: [ffi::c_ulong; if size_of::<ffi::c_long>() == 8 { 7 } else { 9 }],
|
||||
}
|
||||
_ => {
|
||||
pub struct Thread(!);
|
||||
|
||||
#[repr(C)]
|
||||
pub struct pthread_attr_t {
|
||||
__u: pthread_attr_union,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type pthread_t = *mut ffi::c_void;
|
||||
|
||||
pub const _SC_NPROCESSORS_ONLN: ffi::c_int = 84;
|
||||
|
||||
unsafe extern "C" {
|
||||
pub fn pthread_create(
|
||||
native: *mut pthread_t,
|
||||
attr: *const pthread_attr_t,
|
||||
f: extern "C" fn(*mut ffi::c_void) -> *mut ffi::c_void,
|
||||
value: *mut ffi::c_void,
|
||||
) -> ffi::c_int;
|
||||
pub fn pthread_join(native: pthread_t, value: *mut *mut ffi::c_void) -> ffi::c_int;
|
||||
pub fn pthread_attr_init(attrp: *mut pthread_attr_t) -> ffi::c_int;
|
||||
pub fn pthread_attr_setstacksize(
|
||||
attr: *mut pthread_attr_t,
|
||||
stack_size: libc::size_t,
|
||||
) -> ffi::c_int;
|
||||
pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> ffi::c_int;
|
||||
pub fn pthread_detach(thread: pthread_t) -> ffi::c_int;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_feature = "atomics")]
|
||||
pub struct Thread {
|
||||
id: libc::pthread_t,
|
||||
}
|
||||
|
||||
#[cfg(target_feature = "atomics")]
|
||||
impl Drop for Thread {
|
||||
fn drop(&mut self) {
|
||||
let ret = unsafe { libc::pthread_detach(self.id) };
|
||||
debug_assert_eq!(ret, 0);
|
||||
}
|
||||
}
|
||||
|
||||
pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;
|
||||
|
||||
#[cfg(target_feature = "atomics")]
|
||||
impl Thread {
|
||||
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
||||
cfg_select! {
|
||||
target_feature = "atomics" => {
|
||||
pub unsafe fn new(stack: usize, _name: Option<&str>, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
let p = Box::into_raw(Box::new(p));
|
||||
let mut native: libc::pthread_t = unsafe { mem::zeroed() };
|
||||
let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() };
|
||||
assert_eq!(unsafe { libc::pthread_attr_init(&mut attr) }, 0);
|
||||
pub unsafe fn new(
|
||||
stack: usize,
|
||||
_name: Option<&str>,
|
||||
p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
let p = Box::into_raw(Box::new(p));
|
||||
let mut native: libc::pthread_t = unsafe { mem::zeroed() };
|
||||
let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() };
|
||||
assert_eq!(unsafe { libc::pthread_attr_init(&mut attr) }, 0);
|
||||
|
||||
let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE);
|
||||
let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE);
|
||||
|
||||
match unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) } {
|
||||
0 => {}
|
||||
n => {
|
||||
assert_eq!(n, libc::EINVAL);
|
||||
// EINVAL means |stack_size| is either too small or not a
|
||||
// multiple of the system page size. Because it's definitely
|
||||
// >= PTHREAD_STACK_MIN, it must be an alignment issue.
|
||||
// Round up to the nearest page and try again.
|
||||
let page_size = os::page_size();
|
||||
let stack_size =
|
||||
(stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1);
|
||||
assert_eq!(unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) }, 0);
|
||||
}
|
||||
};
|
||||
|
||||
let ret = unsafe { libc::pthread_create(&mut native, &attr, thread_start, p as *mut _) };
|
||||
// Note: if the thread creation fails and this assert fails, then p will
|
||||
// be leaked. However, an alternative design could cause double-free
|
||||
// which is clearly worse.
|
||||
assert_eq!(unsafe {libc::pthread_attr_destroy(&mut attr) }, 0);
|
||||
|
||||
return if ret != 0 {
|
||||
// The thread failed to start and as a result p was not consumed. Therefore, it is
|
||||
// safe to reconstruct the box so that it gets deallocated.
|
||||
unsafe { drop(Box::from_raw(p)); }
|
||||
Err(io::Error::from_raw_os_error(ret))
|
||||
} else {
|
||||
Ok(Thread { id: native })
|
||||
};
|
||||
|
||||
extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
|
||||
unsafe {
|
||||
// Finally, let's run some code.
|
||||
Box::from_raw(main as *mut Box<dyn FnOnce()>)();
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
match unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) } {
|
||||
0 => {}
|
||||
n => {
|
||||
assert_eq!(n, libc::EINVAL);
|
||||
// EINVAL means |stack_size| is either too small or not a
|
||||
// multiple of the system page size. Because it's definitely
|
||||
// >= PTHREAD_STACK_MIN, it must be an alignment issue.
|
||||
// Round up to the nearest page and try again.
|
||||
let page_size = os::page_size();
|
||||
let stack_size =
|
||||
(stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1);
|
||||
assert_eq!(unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) }, 0);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
pub unsafe fn new(_stack: usize, _name: Option<&str>, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
crate::sys::unsupported()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn yield_now() {
|
||||
let ret = unsafe { wasi::sched_yield() };
|
||||
debug_assert_eq!(ret, Ok(()));
|
||||
}
|
||||
let ret = unsafe { libc::pthread_create(&mut native, &attr, thread_start, p as *mut _) };
|
||||
// Note: if the thread creation fails and this assert fails, then p will
|
||||
// be leaked. However, an alternative design could cause double-free
|
||||
// which is clearly worse.
|
||||
assert_eq!(unsafe { libc::pthread_attr_destroy(&mut attr) }, 0);
|
||||
|
||||
pub fn set_name(_name: &CStr) {
|
||||
// nope
|
||||
}
|
||||
|
||||
pub fn sleep(dur: Duration) {
|
||||
let mut nanos = 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 } },
|
||||
};
|
||||
return if ret != 0 {
|
||||
// The thread failed to start and as a result p was not consumed. Therefore, it is
|
||||
// safe to reconstruct the box so that it gets deallocated.
|
||||
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"),
|
||||
}
|
||||
drop(Box::from_raw(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(io::Error::from_raw_os_error(ret))
|
||||
} else {
|
||||
Ok(Thread { id: native })
|
||||
};
|
||||
|
||||
pub fn sleep_until(deadline: Instant) {
|
||||
let now = Instant::now();
|
||||
|
||||
if let Some(delay) = deadline.checked_duration_since(now) {
|
||||
Self::sleep(delay);
|
||||
extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
|
||||
unsafe {
|
||||
// Finally, let's run some code.
|
||||
Box::from_raw(main as *mut Box<dyn FnOnce()>)();
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn join(self) {
|
||||
cfg_select! {
|
||||
target_feature = "atomics" => {
|
||||
let id = mem::ManuallyDrop::new(self).id;
|
||||
let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
||||
if ret != 0 {
|
||||
rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.0
|
||||
}
|
||||
let id = mem::ManuallyDrop::new(self).id;
|
||||
let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
||||
if ret != 0 {
|
||||
rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn current_os_id() -> Option<u64> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(target_feature = "atomics")]
|
||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||
cfg_select! {
|
||||
target_feature = "atomics" => {
|
||||
match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } {
|
||||
-1 => Err(io::Error::last_os_error()),
|
||||
cpus => NonZero::new(cpus as usize).ok_or(io::Error::UNKNOWN_THREAD_COUNT),
|
||||
}
|
||||
}
|
||||
_ => crate::sys::unsupported(),
|
||||
match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } {
|
||||
-1 => Err(io::Error::last_os_error()),
|
||||
cpus => NonZero::new(cpus as usize).ok_or(io::Error::UNKNOWN_THREAD_COUNT),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
let ret = unsafe { wasi::sched_yield() };
|
||||
debug_assert_eq!(ret, Ok(()));
|
||||
}
|
||||
|
||||
pub fn sleep(dur: Duration) {
|
||||
let mut nanos = 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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,73 +1,32 @@
|
|||
use crate::ffi::CStr;
|
||||
use crate::io;
|
||||
use crate::num::NonZero;
|
||||
use crate::time::{Duration, Instant};
|
||||
|
||||
pub struct Thread(!);
|
||||
|
||||
pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;
|
||||
|
||||
impl Thread {
|
||||
pub unsafe fn new(
|
||||
_stack: usize,
|
||||
_name: Option<&str>,
|
||||
_p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
// Note that unlike WASIp1 even if the wasm `atomics` feature is enabled
|
||||
// there is no support for threads, not even experimentally, not even in
|
||||
// wasi-libc. Thus this is unconditionally unsupported.
|
||||
crate::sys::unsupported()
|
||||
pub fn sleep(dur: Duration) {
|
||||
// Sleep in increments of `u64::MAX` nanoseconds until the `dur` is
|
||||
// entirely drained.
|
||||
let mut remaining = dur.as_nanos();
|
||||
while remaining > 0 {
|
||||
let amt = u64::try_from(remaining).unwrap_or(u64::MAX);
|
||||
wasip2::clocks::monotonic_clock::subscribe_duration(amt).block();
|
||||
remaining -= u128::from(amt);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
// no API for this in WASIp2, but there's also no threads, so that's
|
||||
// sort of expected.
|
||||
}
|
||||
|
||||
pub fn set_name(_name: &CStr) {
|
||||
// nope
|
||||
}
|
||||
|
||||
pub fn sleep(dur: Duration) {
|
||||
// Sleep in increments of `u64::MAX` nanoseconds until the `dur` is
|
||||
// entirely drained.
|
||||
let mut remaining = dur.as_nanos();
|
||||
while remaining > 0 {
|
||||
let amt = u64::try_from(remaining).unwrap_or(u64::MAX);
|
||||
wasip2::clocks::monotonic_clock::subscribe_duration(amt).block();
|
||||
remaining -= u128::from(amt);
|
||||
pub fn sleep_until(deadline: Instant) {
|
||||
match u64::try_from(deadline.into_inner().as_duration().as_nanos()) {
|
||||
// If the point in time we're sleeping to fits within a 64-bit
|
||||
// number of nanoseconds then directly use `subscribe_instant`.
|
||||
Ok(deadline) => {
|
||||
wasip2::clocks::monotonic_clock::subscribe_instant(deadline).block();
|
||||
}
|
||||
}
|
||||
// ... otherwise we're sleeping for 500+ years relative to the
|
||||
// "start" of what the system is using as a clock so speed/accuracy
|
||||
// is not so much of a concern. Use `sleep` instead.
|
||||
Err(_) => {
|
||||
let now = Instant::now();
|
||||
|
||||
pub fn sleep_until(deadline: Instant) {
|
||||
match u64::try_from(deadline.into_inner().as_duration().as_nanos()) {
|
||||
// If the point in time we're sleeping to fits within a 64-bit
|
||||
// number of nanoseconds then directly use `subscribe_instant`.
|
||||
Ok(deadline) => {
|
||||
wasip2::clocks::monotonic_clock::subscribe_instant(deadline).block();
|
||||
}
|
||||
// ... otherwise we're sleeping for 500+ years relative to the
|
||||
// "start" of what the system is using as a clock so speed/accuracy
|
||||
// is not so much of a concern. Use `sleep` instead.
|
||||
Err(_) => {
|
||||
let now = Instant::now();
|
||||
|
||||
if let Some(delay) = deadline.checked_duration_since(now) {
|
||||
Self::sleep(delay);
|
||||
}
|
||||
if let Some(delay) = deadline.checked_duration_since(now) {
|
||||
sleep(delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn join(self) {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn current_os_id() -> Option<u64> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||
crate::sys::unsupported()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,75 +1,23 @@
|
|||
use crate::ffi::CStr;
|
||||
use crate::io;
|
||||
use crate::num::NonZero;
|
||||
use crate::sys::unsupported;
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::cmp;
|
||||
use crate::time::Duration;
|
||||
|
||||
pub struct Thread(!);
|
||||
pub fn sleep(dur: Duration) {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use core::arch::wasm32 as wasm;
|
||||
#[cfg(target_arch = "wasm64")]
|
||||
use core::arch::wasm64 as wasm;
|
||||
|
||||
pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;
|
||||
|
||||
impl Thread {
|
||||
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
||||
pub unsafe fn new(
|
||||
_stack: usize,
|
||||
_name: Option<&str>,
|
||||
_p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn yield_now() {}
|
||||
|
||||
pub fn set_name(_name: &CStr) {}
|
||||
|
||||
pub fn sleep(dur: Duration) {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use core::arch::wasm32 as wasm;
|
||||
#[cfg(target_arch = "wasm64")]
|
||||
use core::arch::wasm64 as wasm;
|
||||
|
||||
use crate::cmp;
|
||||
|
||||
// Use an atomic wait to block the current thread artificially with a
|
||||
// timeout listed. Note that we should never be notified (return value
|
||||
// of 0) or our comparison should never fail (return value of 1) so we
|
||||
// should always only resume execution through a timeout (return value
|
||||
// 2).
|
||||
let mut nanos = dur.as_nanos();
|
||||
while nanos > 0 {
|
||||
let amt = cmp::min(i64::MAX as u128, nanos);
|
||||
let mut x = 0;
|
||||
let val = unsafe { wasm::memory_atomic_wait32(&mut x, 0, amt as i64) };
|
||||
debug_assert_eq!(val, 2);
|
||||
nanos -= amt;
|
||||
}
|
||||
}
|
||||
|
||||
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) {}
|
||||
}
|
||||
|
||||
pub(crate) fn current_os_id() -> Option<u64> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub mod guard {
|
||||
pub type Guard = !;
|
||||
pub unsafe fn current() -> Option<Guard> {
|
||||
None
|
||||
}
|
||||
pub unsafe fn init() -> Option<Guard> {
|
||||
None
|
||||
// Use an atomic wait to block the current thread artificially with a
|
||||
// timeout listed. Note that we should never be notified (return value
|
||||
// of 0) or our comparison should never fail (return value of 1) so we
|
||||
// should always only resume execution through a timeout (return value
|
||||
// 2).
|
||||
let mut nanos = dur.as_nanos();
|
||||
while nanos > 0 {
|
||||
let amt = cmp::min(i64::MAX as u128, nanos);
|
||||
let mut x = 0;
|
||||
let val = unsafe { wasm::memory_atomic_wait32(&mut x, 0, amt as i64) };
|
||||
debug_assert_eq!(val, 2);
|
||||
nanos -= amt;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
use core::ffi::c_void;
|
||||
|
||||
use super::time::WaitableTimer;
|
||||
use super::to_u16s;
|
||||
use crate::ffi::CStr;
|
||||
use crate::num::NonZero;
|
||||
use crate::os::windows::io::{AsRawHandle, HandleOrNull};
|
||||
use crate::sys::handle::Handle;
|
||||
use crate::sys::pal::time::WaitableTimer;
|
||||
use crate::sys::pal::{dur2timeout, to_u16s};
|
||||
use crate::sys::{c, stack_overflow};
|
||||
use crate::sys_common::FromInner;
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::time::Duration;
|
||||
use crate::{io, ptr};
|
||||
|
||||
pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
|
||||
|
|
@ -62,24 +62,6 @@ impl Thread {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_name(name: &CStr) {
|
||||
if let Ok(utf8) = name.to_str() {
|
||||
if let Ok(utf16) = to_u16s(utf8) {
|
||||
unsafe {
|
||||
// SAFETY: the vec returned by `to_u16s` ends with a zero value
|
||||
Self::set_name_wide(&utf16)
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// `name` must end with a zero value
|
||||
pub unsafe fn set_name_wide(name: &[u16]) {
|
||||
unsafe { c::SetThreadDescription(c::GetCurrentThread(), name.as_ptr()) };
|
||||
}
|
||||
|
||||
pub fn join(self) {
|
||||
let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
|
||||
if rc == c::WAIT_FAILED {
|
||||
|
|
@ -87,37 +69,6 @@ impl Thread {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
// This function will return 0 if there are no other threads to execute,
|
||||
// but this also means that the yield was useless so this isn't really a
|
||||
// case that needs to be worried about.
|
||||
unsafe {
|
||||
c::SwitchToThread();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sleep(dur: Duration) {
|
||||
fn high_precision_sleep(dur: Duration) -> Result<(), ()> {
|
||||
let timer = WaitableTimer::high_resolution()?;
|
||||
timer.set(dur)?;
|
||||
timer.wait()
|
||||
}
|
||||
// Attempt to use high-precision sleep (Windows 10, version 1803+).
|
||||
// On error fallback to the standard `Sleep` function.
|
||||
// Also preserves the zero duration behavior of `Sleep`.
|
||||
if dur.is_zero() || high_precision_sleep(dur).is_err() {
|
||||
unsafe { c::Sleep(super::dur2timeout(dur)) }
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -127,14 +78,6 @@ impl Thread {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn current_os_id() -> Option<u64> {
|
||||
// SAFETY: FFI call with no preconditions.
|
||||
let id: u32 = unsafe { c::GetCurrentThreadId() };
|
||||
|
||||
// A return value of 0 indicates failed lookup.
|
||||
if id == 0 { None } else { Some(id.into()) }
|
||||
}
|
||||
|
||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||
let res = unsafe {
|
||||
let mut sysinfo: c::SYSTEM_INFO = crate::mem::zeroed();
|
||||
|
|
@ -146,3 +89,52 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
|||
cpus => Ok(unsafe { NonZero::new_unchecked(cpus) }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_os_id() -> Option<u64> {
|
||||
// SAFETY: FFI call with no preconditions.
|
||||
let id: u32 = unsafe { c::GetCurrentThreadId() };
|
||||
|
||||
// A return value of 0 indicates failed lookup.
|
||||
if id == 0 { None } else { Some(id.into()) }
|
||||
}
|
||||
|
||||
pub fn set_name(name: &CStr) {
|
||||
if let Ok(utf8) = name.to_str() {
|
||||
if let Ok(utf16) = to_u16s(utf8) {
|
||||
unsafe {
|
||||
// SAFETY: the vec returned by `to_u16s` ends with a zero value
|
||||
set_name_wide(&utf16)
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// `name` must end with a zero value
|
||||
pub unsafe fn set_name_wide(name: &[u16]) {
|
||||
unsafe { c::SetThreadDescription(c::GetCurrentThread(), name.as_ptr()) };
|
||||
}
|
||||
|
||||
pub fn sleep(dur: Duration) {
|
||||
fn high_precision_sleep(dur: Duration) -> Result<(), ()> {
|
||||
let timer = WaitableTimer::high_resolution()?;
|
||||
timer.set(dur)?;
|
||||
timer.wait()
|
||||
}
|
||||
// Attempt to use high-precision sleep (Windows 10, version 1803+).
|
||||
// On error fallback to the standard `Sleep` function.
|
||||
// Also preserves the zero duration behavior of `Sleep`.
|
||||
if dur.is_zero() || high_precision_sleep(dur).is_err() {
|
||||
unsafe { c::Sleep(dur2timeout(dur)) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
// This function will return 0 if there are no other threads to execute,
|
||||
// but this also means that the yield was useless so this isn't really a
|
||||
// case that needs to be worried about.
|
||||
unsafe {
|
||||
c::SwitchToThread();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use core::arch::asm;
|
||||
|
||||
use crate::ffi::CStr;
|
||||
use crate::io;
|
||||
use crate::num::NonZero;
|
||||
use crate::os::xous::ffi::{
|
||||
|
|
@ -8,7 +7,7 @@ use crate::os::xous::ffi::{
|
|||
map_memory, update_memory_flags,
|
||||
};
|
||||
use crate::os::xous::services::{TicktimerScalar, ticktimer_server};
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::time::Duration;
|
||||
|
||||
pub struct Thread {
|
||||
tid: ThreadId,
|
||||
|
|
@ -110,46 +109,29 @@ impl Thread {
|
|||
Ok(Thread { tid })
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
do_yield();
|
||||
}
|
||||
|
||||
pub fn set_name(_name: &CStr) {
|
||||
// nope
|
||||
}
|
||||
|
||||
pub fn sleep(dur: Duration) {
|
||||
// Because the sleep server works on units of `usized milliseconds`, split
|
||||
// the messages up into these chunks. This means we may run into issues
|
||||
// if you try to sleep a thread for more than 49 days on a 32-bit system.
|
||||
let mut millis = dur.as_millis();
|
||||
while millis > 0 {
|
||||
let sleep_duration =
|
||||
if millis > (usize::MAX as _) { usize::MAX } else { millis as usize };
|
||||
blocking_scalar(ticktimer_server(), TicktimerScalar::SleepMs(sleep_duration).into())
|
||||
.expect("failed to send message to ticktimer server");
|
||||
millis -= sleep_duration as u128;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn current_os_id() -> Option<u64> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||
// We're unicore right now.
|
||||
Ok(unsafe { NonZero::new_unchecked(1) })
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
do_yield();
|
||||
}
|
||||
|
||||
pub fn sleep(dur: Duration) {
|
||||
// Because the sleep server works on units of `usized milliseconds`, split
|
||||
// the messages up into these chunks. This means we may run into issues
|
||||
// if you try to sleep a thread for more than 49 days on a 32-bit system.
|
||||
let mut millis = dur.as_millis();
|
||||
while millis > 0 {
|
||||
let sleep_duration = if millis > (usize::MAX as _) { usize::MAX } else { millis as usize };
|
||||
blocking_scalar(ticktimer_server(), TicktimerScalar::SleepMs(sleep_duration).into())
|
||||
.expect("failed to send message to ticktimer server");
|
||||
millis -= sleep_duration as u128;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -550,7 +550,7 @@ impl Builder {
|
|||
}
|
||||
|
||||
if let Some(name) = their_thread.cname() {
|
||||
imp::Thread::set_name(name);
|
||||
imp::set_name(name);
|
||||
}
|
||||
|
||||
let f = f.into_inner();
|
||||
|
|
@ -763,7 +763,7 @@ where
|
|||
/// [`Mutex`]: crate::sync::Mutex
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn yield_now() {
|
||||
imp::Thread::yield_now()
|
||||
imp::yield_now()
|
||||
}
|
||||
|
||||
/// Determines whether the current thread is unwinding because of panic.
|
||||
|
|
@ -884,7 +884,7 @@ pub fn sleep_ms(ms: u32) {
|
|||
/// ```
|
||||
#[stable(feature = "thread_sleep", since = "1.4.0")]
|
||||
pub fn sleep(dur: Duration) {
|
||||
imp::Thread::sleep(dur)
|
||||
imp::sleep(dur)
|
||||
}
|
||||
|
||||
/// Puts the current thread to sleep until the specified deadline has passed.
|
||||
|
|
@ -983,7 +983,7 @@ pub fn sleep(dur: Duration) {
|
|||
/// ```
|
||||
#[unstable(feature = "thread_sleep_until", issue = "113752")]
|
||||
pub fn sleep_until(deadline: Instant) {
|
||||
imp::Thread::sleep_until(deadline)
|
||||
imp::sleep_until(deadline)
|
||||
}
|
||||
|
||||
/// Used to ensure that `park` and `park_timeout` do not unwind, as that can
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue