Auto merge of #145177 - joboet:move-pal-thread, r=ibraheemdev
std: move `thread` into `sys` Part of https://github.com/rust-lang/rust/issues/117276.
This commit is contained in:
commit
2a9bacf618
45 changed files with 922 additions and 1122 deletions
|
|
@ -31,6 +31,7 @@ pub mod process;
|
||||||
pub mod random;
|
pub mod random;
|
||||||
pub mod stdio;
|
pub mod stdio;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
|
pub mod thread;
|
||||||
pub mod thread_local;
|
pub mod thread_local;
|
||||||
|
|
||||||
// FIXME(117276): remove this, move feature implementations into individual
|
// FIXME(117276): remove this, move feature implementations into individual
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ pub mod futex;
|
||||||
pub mod os;
|
pub mod os;
|
||||||
#[path = "../unsupported/pipe.rs"]
|
#[path = "../unsupported/pipe.rs"]
|
||||||
pub mod pipe;
|
pub mod pipe;
|
||||||
pub mod thread;
|
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
||||||
pub fn unsupported<T>() -> crate::io::Result<T> {
|
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() };
|
let tls_guard = unsafe { tls.activate() };
|
||||||
|
|
||||||
if secondary {
|
if secondary {
|
||||||
let join_notifier = super::thread::Thread::entry();
|
let join_notifier = crate::sys::thread::Thread::entry();
|
||||||
drop(tls_guard);
|
drop(tls_guard);
|
||||||
drop(join_notifier);
|
drop(join_notifier);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ mod libunwind_integration;
|
||||||
pub mod os;
|
pub mod os;
|
||||||
#[path = "../unsupported/pipe.rs"]
|
#[path = "../unsupported/pipe.rs"]
|
||||||
pub mod pipe;
|
pub mod pipe;
|
||||||
pub mod thread;
|
|
||||||
pub mod thread_parking;
|
pub mod thread_parking;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod waitqueue;
|
pub mod waitqueue;
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,8 @@ pub mod itron {
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod spin;
|
pub mod spin;
|
||||||
pub mod task;
|
pub mod task;
|
||||||
pub mod thread;
|
|
||||||
pub mod thread_parking;
|
pub mod thread_parking;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
use super::unsupported;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// `error` is `pub(crate)` so that it can be accessed by `itron/error.rs` as
|
// `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;
|
pub mod os;
|
||||||
#[path = "../unsupported/pipe.rs"]
|
#[path = "../unsupported/pipe.rs"]
|
||||||
pub mod pipe;
|
pub mod pipe;
|
||||||
pub use self::itron::{thread, thread_parking};
|
pub use self::itron::thread_parking;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
||||||
// SAFETY: must be called only once during runtime initialization.
|
// SAFETY: must be called only once during runtime initialization.
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
pub mod os;
|
pub mod os;
|
||||||
#[path = "../unsupported/pipe.rs"]
|
#[path = "../unsupported/pipe.rs"]
|
||||||
pub mod pipe;
|
pub mod pipe;
|
||||||
pub mod thread;
|
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
#[path = "../unix/time.rs"]
|
#[path = "../unix/time.rs"]
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ pub mod helpers;
|
||||||
pub mod os;
|
pub mod os;
|
||||||
#[path = "../unsupported/pipe.rs"]
|
#[path = "../unsupported/pipe.rs"]
|
||||||
pub mod pipe;
|
pub mod pipe;
|
||||||
pub mod thread;
|
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
|
||||||
// UEFI is single threaded
|
|
||||||
Ok(NonZero::new(1).unwrap())
|
|
||||||
}
|
|
||||||
|
|
@ -17,7 +17,6 @@ pub mod os;
|
||||||
pub mod pipe;
|
pub mod pipe;
|
||||||
pub mod stack_overflow;
|
pub mod stack_overflow;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
pub mod thread;
|
|
||||||
pub mod thread_parking;
|
pub mod thread_parking;
|
||||||
pub mod time;
|
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
|
// 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.
|
// process and we only want to enable this on platforms we've tested.
|
||||||
if cfg!(target_vendor = "apple") {
|
if cfg!(target_vendor = "apple") {
|
||||||
thread::Thread::set_name(&c"main");
|
crate::sys::thread::set_name(c"main");
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn sanitize_standard_fds() {
|
unsafe fn sanitize_standard_fds() {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ pub mod futex;
|
||||||
pub mod os;
|
pub mod os;
|
||||||
#[path = "../unsupported/pipe.rs"]
|
#[path = "../unsupported/pipe.rs"]
|
||||||
pub mod pipe;
|
pub mod pipe;
|
||||||
pub mod thread;
|
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
||||||
#[path = "../unsupported/common.rs"]
|
#[path = "../unsupported/common.rs"]
|
||||||
|
|
|
||||||
|
|
@ -1,214 +0,0 @@
|
||||||
#![forbid(unsafe_op_in_unsafe_fn)]
|
|
||||||
|
|
||||||
use crate::ffi::CStr;
|
|
||||||
use crate::num::NonZero;
|
|
||||||
use crate::time::{Duration, Instant};
|
|
||||||
use crate::{io, mem};
|
|
||||||
|
|
||||||
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::*;
|
|
||||||
|
|
||||||
// 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 }],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
pub struct Thread(!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
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(()));
|
|
||||||
}
|
|
||||||
|
|
||||||
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 } },
|
|
||||||
};
|
|
||||||
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 sleep_until(deadline: Instant) {
|
|
||||||
let now = Instant::now();
|
|
||||||
|
|
||||||
if let Some(delay) = deadline.checked_duration_since(now) {
|
|
||||||
Self::sleep(delay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn current_os_id() -> Option<u64> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -14,7 +14,6 @@ pub mod futex;
|
||||||
pub mod os;
|
pub mod os;
|
||||||
#[path = "../unsupported/pipe.rs"]
|
#[path = "../unsupported/pipe.rs"]
|
||||||
pub mod pipe;
|
pub mod pipe;
|
||||||
pub mod thread;
|
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
||||||
#[path = "../unsupported/common.rs"]
|
#[path = "../unsupported/common.rs"]
|
||||||
|
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
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 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();
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
|
||||||
crate::sys::unsupported()
|
|
||||||
}
|
|
||||||
|
|
@ -25,7 +25,7 @@ impl Instant {
|
||||||
Some(Instant(self.0.checked_sub(*other)?))
|
Some(Instant(self.0.checked_sub(*other)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn as_duration(&self) -> &Duration {
|
pub(crate) fn as_duration(&self) -> &Duration {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
use crate::ffi::CStr;
|
|
||||||
use crate::io;
|
|
||||||
use crate::num::NonZero;
|
|
||||||
use crate::sys::unsupported;
|
|
||||||
use crate::time::{Duration, Instant};
|
|
||||||
|
|
||||||
pub struct Thread(!);
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -23,18 +23,9 @@ pub mod pipe;
|
||||||
#[path = "../unsupported/time.rs"]
|
#[path = "../unsupported/time.rs"]
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
||||||
cfg_select! {
|
#[cfg(target_feature = "atomics")]
|
||||||
target_feature = "atomics" => {
|
#[path = "atomics/futex.rs"]
|
||||||
#[path = "atomics/futex.rs"]
|
pub mod futex;
|
||||||
pub mod futex;
|
|
||||||
#[path = "atomics/thread.rs"]
|
|
||||||
pub mod thread;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
#[path = "../unsupported/thread.rs"]
|
|
||||||
pub mod thread;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[path = "../unsupported/common.rs"]
|
#[path = "../unsupported/common.rs"]
|
||||||
#[deny(unsafe_op_in_unsafe_fn)]
|
#[deny(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ pub mod futex;
|
||||||
pub mod handle;
|
pub mod handle;
|
||||||
pub mod os;
|
pub mod os;
|
||||||
pub mod pipe;
|
pub mod pipe;
|
||||||
pub mod thread;
|
|
||||||
pub mod time;
|
pub mod time;
|
||||||
cfg_select! {
|
cfg_select! {
|
||||||
not(target_vendor = "uwp") => {
|
not(target_vendor = "uwp") => {
|
||||||
|
|
@ -48,9 +47,9 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_overflow::init();
|
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.
|
// 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.
|
/// A timer you can wait on.
|
||||||
pub(super) struct WaitableTimer {
|
pub(crate) struct WaitableTimer {
|
||||||
handle: c::HANDLE,
|
handle: c::HANDLE,
|
||||||
}
|
}
|
||||||
impl WaitableTimer {
|
impl WaitableTimer {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ use crate::os::xous::ffi::exit;
|
||||||
pub mod os;
|
pub mod os;
|
||||||
#[path = "../unsupported/pipe.rs"]
|
#[path = "../unsupported/pipe.rs"]
|
||||||
pub mod pipe;
|
pub mod pipe;
|
||||||
pub mod thread;
|
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
||||||
#[path = "../unsupported/common.rs"]
|
#[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::num::NonZero;
|
||||||
use crate::time::{Duration, Instant};
|
use crate::time::Duration;
|
||||||
use crate::{io, ptr};
|
use crate::{io, ptr};
|
||||||
|
|
||||||
pub type Tid = hermit_abi::Tid;
|
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) {
|
pub fn join(self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = hermit_abi::join(self.tid);
|
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>> {
|
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||||
unsafe { Ok(NonZero::new_unchecked(hermit_abi::available_parallelism())) }
|
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?
|
#![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::io;
|
||||||
use crate::num::NonZero;
|
use crate::sys::pal::abi::{thread, usercalls};
|
||||||
use crate::time::{Duration, Instant};
|
use crate::time::Duration;
|
||||||
|
|
||||||
pub struct Thread(task_queue::JoinHandle);
|
pub struct Thread(task_queue::JoinHandle);
|
||||||
|
|
||||||
|
|
@ -108,51 +105,27 @@ impl Thread {
|
||||||
Ok(Thread(handle))
|
Ok(Thread(handle))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn entry() -> JoinNotifier {
|
pub(crate) fn entry() -> JoinNotifier {
|
||||||
let mut pending_tasks = task_queue::lock();
|
let mut pending_tasks = task_queue::lock();
|
||||||
let task = rtunwrap!(Some, pending_tasks.pop());
|
let task = rtunwrap!(Some, pending_tasks.pop());
|
||||||
drop(pending_tasks); // make sure to not hold the task queue lock longer than necessary
|
drop(pending_tasks); // make sure to not hold the task queue lock longer than necessary
|
||||||
task.run()
|
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) {
|
pub fn join(self) {
|
||||||
self.0.wait();
|
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)
|
Some(thread::current().addr().get() as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
pub fn sleep(dur: Duration) {
|
||||||
unsupported()
|
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
|
//! Thread implementation backed by μITRON tasks. Assumes `acre_tsk` and
|
||||||
//! `exd_tsk` are available.
|
//! `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::cell::UnsafeCell;
|
||||||
use crate::ffi::CStr;
|
|
||||||
use crate::mem::ManuallyDrop;
|
use crate::mem::ManuallyDrop;
|
||||||
use crate::num::NonZero;
|
|
||||||
use crate::ptr::NonNull;
|
use crate::ptr::NonNull;
|
||||||
use crate::sync::atomic::{Atomic, AtomicUsize, Ordering};
|
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};
|
use crate::{hint, io};
|
||||||
|
|
||||||
pub struct Thread {
|
pub struct Thread {
|
||||||
|
|
@ -195,28 +193,6 @@ impl Thread {
|
||||||
Ok(Self { p_inner, task: new_task })
|
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) {
|
pub fn join(self) {
|
||||||
// Safety: `ThreadInner` is alive at this point
|
// Safety: `ThreadInner` is alive at this point
|
||||||
let inner = unsafe { self.p_inner.as_ref() };
|
let inner = unsafe { self.p_inner.as_ref() };
|
||||||
|
|
@ -361,10 +337,12 @@ unsafe fn terminate_and_delete_current_task() -> ! {
|
||||||
unsafe { crate::hint::unreachable_unchecked() };
|
unsafe { crate::hint::unreachable_unchecked() };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn current_os_id() -> Option<u64> {
|
pub fn yield_now() {
|
||||||
None
|
expect_success(unsafe { abi::rot_rdq(abi::TPRI_SELF) }, &"rot_rdq");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
pub fn sleep(dur: Duration) {
|
||||||
super::unsupported()
|
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::mem::{self, ManuallyDrop};
|
||||||
use crate::num::NonZero;
|
|
||||||
use crate::sys::os;
|
use crate::sys::os;
|
||||||
use crate::time::{Duration, Instant};
|
use crate::time::Duration;
|
||||||
use crate::{cmp, io, ptr};
|
use crate::{cmp, io, ptr};
|
||||||
|
|
||||||
pub const DEFAULT_MIN_STACK_SIZE: usize = 8 * 1024;
|
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 {
|
pub struct Thread {
|
||||||
id: libc::pthread_t,
|
id: libc::pthread_t,
|
||||||
}
|
}
|
||||||
|
|
@ -16,10 +22,6 @@ pub struct Thread {
|
||||||
unsafe impl Send for Thread {}
|
unsafe impl Send for Thread {}
|
||||||
unsafe impl Sync for Thread {}
|
unsafe impl Sync for Thread {}
|
||||||
|
|
||||||
unsafe extern "C" {
|
|
||||||
pub fn TEE_Wait(timeout: u32) -> u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Thread {
|
impl Thread {
|
||||||
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
||||||
pub unsafe fn new(
|
pub unsafe fn new(
|
||||||
|
|
@ -74,7 +76,7 @@ impl Thread {
|
||||||
} else {
|
} else {
|
||||||
// The new thread will start running earliest after the next yield.
|
// The new thread will start running earliest after the next yield.
|
||||||
// We add a yield here, so that the user does not have to.
|
// We add a yield here, so that the user does not have to.
|
||||||
Thread::yield_now();
|
yield_now();
|
||||||
Ok(Thread { id: native })
|
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
|
/// must join, because no pthread_detach supported
|
||||||
pub fn join(self) {
|
pub fn join(self) {
|
||||||
let id = self.into_id();
|
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));
|
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 {
|
pub fn into_id(self) -> libc::pthread_t {
|
||||||
ManuallyDrop::new(self).id
|
ManuallyDrop::new(self).id
|
||||||
}
|
}
|
||||||
|
|
@ -144,16 +112,15 @@ impl Drop for Thread {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn current_os_id() -> Option<u64> {
|
pub fn yield_now() {
|
||||||
None
|
let ret = unsafe { libc::sched_yield() };
|
||||||
|
debug_assert_eq!(ret, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: Both `sched_getaffinity` and `sysconf` are available but not functional on
|
/// only main thread could wait for sometime in teeos
|
||||||
// teeos, so this function always returns an Error!
|
pub fn sleep(dur: Duration) {
|
||||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
let sleep_millis = dur.as_millis();
|
||||||
Err(io::Error::UNKNOWN_THREAD_COUNT)
|
let final_sleep: u32 =
|
||||||
}
|
if sleep_millis >= u32::MAX as u128 { u32::MAX } else { sleep_millis as u32 };
|
||||||
|
TEE_Wait(final_sleep);
|
||||||
fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
|
|
||||||
libc::PTHREAD_STACK_MIN.try_into().expect("Infallible")
|
|
||||||
}
|
}
|
||||||
25
library/std/src/sys/thread/uefi.rs
Normal file
25
library/std/src/sys/thread/uefi.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
use crate::io;
|
||||||
|
use crate::num::NonZero;
|
||||||
|
use crate::ptr::NonNull;
|
||||||
|
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::ffi::CStr;
|
||||||
use crate::mem::{self, ManuallyDrop};
|
use crate::mem::{self, ManuallyDrop};
|
||||||
use crate::num::NonZero;
|
use crate::num::NonZero;
|
||||||
|
|
@ -6,7 +14,7 @@ use crate::sys::weak::dlsym;
|
||||||
#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto",))]
|
#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto",))]
|
||||||
use crate::sys::weak::weak;
|
use crate::sys::weak::weak;
|
||||||
use crate::sys::{os, stack_overflow};
|
use crate::sys::{os, stack_overflow};
|
||||||
use crate::time::{Duration, Instant};
|
use crate::time::Duration;
|
||||||
use crate::{cmp, io, ptr};
|
use crate::{cmp, io, ptr};
|
||||||
#[cfg(not(any(
|
#[cfg(not(any(
|
||||||
target_os = "l4re",
|
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) {
|
pub fn join(self) {
|
||||||
let id = self.into_id();
|
let id = self.into_id();
|
||||||
let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
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>> {
|
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||||
cfg_select! {
|
cfg_select! {
|
||||||
any(
|
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"))]
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||||
mod cgroups {
|
mod cgroups {
|
||||||
//! Currently not covered
|
//! Currently not covered
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use super::unsupported;
|
|
||||||
use crate::ffi::CStr;
|
use crate::ffi::CStr;
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::num::NonZero;
|
use crate::num::NonZero;
|
||||||
use crate::time::{Duration, Instant};
|
use crate::time::Duration;
|
||||||
|
|
||||||
pub struct Thread(!);
|
pub struct Thread(!);
|
||||||
|
|
||||||
|
|
@ -15,23 +14,7 @@ impl Thread {
|
||||||
_name: Option<&str>,
|
_name: Option<&str>,
|
||||||
_p: Box<dyn FnOnce()>,
|
_p: Box<dyn FnOnce()>,
|
||||||
) -> io::Result<Thread> {
|
) -> io::Result<Thread> {
|
||||||
unsupported()
|
Err(io::Error::UNSUPPORTED_PLATFORM)
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn join(self) {
|
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
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
pub fn yield_now() {
|
||||||
unsupported()
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_name(_name: &CStr) {
|
||||||
|
// nope
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sleep(_dur: Duration) {
|
||||||
|
panic!("can't sleep");
|
||||||
}
|
}
|
||||||
185
library/std/src/sys/thread/wasip1.rs
Normal file
185
library/std/src/sys/thread/wasip1.rs
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
|
#[cfg(target_feature = "atomics")]
|
||||||
|
use crate::io;
|
||||||
|
use crate::mem;
|
||||||
|
#[cfg(target_feature = "atomics")]
|
||||||
|
use crate::num::NonZero;
|
||||||
|
#[cfg(target_feature = "atomics")]
|
||||||
|
use crate::sys::os;
|
||||||
|
use crate::time::Duration;
|
||||||
|
#[cfg(target_feature = "atomics")]
|
||||||
|
use crate::{cmp, ptr};
|
||||||
|
|
||||||
|
// Add a few symbols not in upstream `libc` just yet.
|
||||||
|
#[cfg(target_feature = "atomics")]
|
||||||
|
mod libc {
|
||||||
|
pub use libc::*;
|
||||||
|
|
||||||
|
pub use crate::ffi;
|
||||||
|
|
||||||
|
// 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 }],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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
|
||||||
|
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);
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn join(self) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_feature = "atomics")]
|
||||||
|
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||||
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
library/std/src/sys/thread/wasip2.rs
Normal file
32
library/std/src/sys/thread/wasip2.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
use crate::time::{Duration, Instant};
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
if let Some(delay) = deadline.checked_duration_since(now) {
|
||||||
|
sleep(delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
library/std/src/sys/thread/wasm.rs
Normal file
23
library/std/src/sys/thread/wasm.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
use crate::cmp;
|
||||||
|
use crate::time::Duration;
|
||||||
|
|
||||||
|
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 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 core::ffi::c_void;
|
||||||
|
|
||||||
use super::time::WaitableTimer;
|
|
||||||
use super::to_u16s;
|
|
||||||
use crate::ffi::CStr;
|
use crate::ffi::CStr;
|
||||||
use crate::num::NonZero;
|
use crate::num::NonZero;
|
||||||
use crate::os::windows::io::{AsRawHandle, HandleOrNull};
|
use crate::os::windows::io::{AsRawHandle, HandleOrNull};
|
||||||
use crate::sys::handle::Handle;
|
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::{c, stack_overflow};
|
||||||
use crate::sys_common::FromInner;
|
use crate::sys_common::FromInner;
|
||||||
use crate::time::{Duration, Instant};
|
use crate::time::Duration;
|
||||||
use crate::{io, ptr};
|
use crate::{io, ptr};
|
||||||
|
|
||||||
pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
|
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) {
|
pub fn join(self) {
|
||||||
let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
|
let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
|
||||||
if rc == c::WAIT_FAILED {
|
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 {
|
pub fn handle(&self) -> &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>> {
|
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||||
let res = unsafe {
|
let res = unsafe {
|
||||||
let mut sysinfo: c::SYSTEM_INFO = crate::mem::zeroed();
|
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) }),
|
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 core::arch::asm;
|
||||||
|
|
||||||
use crate::ffi::CStr;
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::num::NonZero;
|
use crate::num::NonZero;
|
||||||
use crate::os::xous::ffi::{
|
use crate::os::xous::ffi::{
|
||||||
|
|
@ -8,7 +7,7 @@ use crate::os::xous::ffi::{
|
||||||
map_memory, update_memory_flags,
|
map_memory, update_memory_flags,
|
||||||
};
|
};
|
||||||
use crate::os::xous::services::{TicktimerScalar, ticktimer_server};
|
use crate::os::xous::services::{TicktimerScalar, ticktimer_server};
|
||||||
use crate::time::{Duration, Instant};
|
use crate::time::Duration;
|
||||||
|
|
||||||
pub struct Thread {
|
pub struct Thread {
|
||||||
tid: ThreadId,
|
tid: ThreadId,
|
||||||
|
|
@ -110,46 +109,29 @@ impl Thread {
|
||||||
Ok(Thread { tid })
|
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) {
|
pub fn join(self) {
|
||||||
join_thread(self.tid).unwrap();
|
join_thread(self.tid).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn current_os_id() -> Option<u64> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||||
// We're unicore right now.
|
// We're unicore right now.
|
||||||
Ok(unsafe { NonZero::new_unchecked(1) })
|
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() {
|
if let Some(name) = their_thread.cname() {
|
||||||
imp::Thread::set_name(name);
|
imp::set_name(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
let f = f.into_inner();
|
let f = f.into_inner();
|
||||||
|
|
@ -763,7 +763,7 @@ where
|
||||||
/// [`Mutex`]: crate::sync::Mutex
|
/// [`Mutex`]: crate::sync::Mutex
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub fn yield_now() {
|
pub fn yield_now() {
|
||||||
imp::Thread::yield_now()
|
imp::yield_now()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines whether the current thread is unwinding because of panic.
|
/// 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")]
|
#[stable(feature = "thread_sleep", since = "1.4.0")]
|
||||||
pub fn sleep(dur: Duration) {
|
pub fn sleep(dur: Duration) {
|
||||||
imp::Thread::sleep(dur)
|
imp::sleep(dur)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Puts the current thread to sleep until the specified deadline has passed.
|
/// 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")]
|
#[unstable(feature = "thread_sleep_until", issue = "113752")]
|
||||||
pub fn sleep_until(deadline: Instant) {
|
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
|
/// Used to ensure that `park` and `park_timeout` do not unwind, as that can
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ LL | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _
|
||||||
= note: inside closure at tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs:LL:CC
|
= note: inside closure at tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs:LL:CC
|
||||||
|
|
||||||
error: the evaluated program deadlocked
|
error: the evaluated program deadlocked
|
||||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
--> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
||||||
| ^ this thread got stuck here
|
| ^ this thread got stuck here
|
||||||
|
|
|
|
||||||
= note: BACKTRACE:
|
= note: BACKTRACE:
|
||||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
= note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
note: inside `main`
|
note: inside `main`
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu
|
||||||
= note: inside closure at tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC
|
= note: inside closure at tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC
|
||||||
|
|
||||||
error: the evaluated program deadlocked
|
error: the evaluated program deadlocked
|
||||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
--> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
||||||
| ^ this thread got stuck here
|
| ^ this thread got stuck here
|
||||||
|
|
|
|
||||||
= note: BACKTRACE:
|
= note: BACKTRACE:
|
||||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
= note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
note: inside `main`
|
note: inside `main`
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu
|
||||||
= note: inside closure at tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC
|
= note: inside closure at tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC
|
||||||
|
|
||||||
error: the evaluated program deadlocked
|
error: the evaluated program deadlocked
|
||||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
--> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
||||||
| ^ this thread got stuck here
|
| ^ this thread got stuck here
|
||||||
|
|
|
|
||||||
= note: BACKTRACE:
|
= note: BACKTRACE:
|
||||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
= note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
note: inside `main`
|
note: inside `main`
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
error: Undefined Behavior: trying to join a detached thread
|
error: Undefined Behavior: trying to join a detached thread
|
||||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
--> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
|
LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||||
|
|
@ -7,7 +7,7 @@ LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(
|
||||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||||
= note: BACKTRACE:
|
= note: BACKTRACE:
|
||||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
= note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
note: inside `main`
|
note: inside `main`
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,13 @@ LL | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJ
|
||||||
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: the evaluated program deadlocked
|
error: the evaluated program deadlocked
|
||||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
--> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
|
LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
|
||||||
| ^ this thread got stuck here
|
| ^ this thread got stuck here
|
||||||
|
|
|
|
||||||
= note: BACKTRACE:
|
= note: BACKTRACE:
|
||||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
= note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
note: inside `main`
|
note: inside `main`
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ LL | assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0
|
||||||
= note: inside closure at tests/fail-dep/concurrency/windows_join_self.rs:LL:CC
|
= note: inside closure at tests/fail-dep/concurrency/windows_join_self.rs:LL:CC
|
||||||
|
|
||||||
error: the evaluated program deadlocked
|
error: the evaluated program deadlocked
|
||||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
--> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
|
LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
|
||||||
| ^ this thread got stuck here
|
| ^ this thread got stuck here
|
||||||
|
|
|
|
||||||
= note: BACKTRACE:
|
= note: BACKTRACE:
|
||||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
= note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
note: inside `main`
|
note: inside `main`
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
error: the evaluated program deadlocked
|
error: the evaluated program deadlocked
|
||||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
--> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
||||||
| ^ this thread got stuck here
|
| ^ this thread got stuck here
|
||||||
|
|
|
|
||||||
= note: BACKTRACE:
|
= note: BACKTRACE:
|
||||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
= note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
note: inside `main`
|
note: inside `main`
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
error: the evaluated program deadlocked
|
error: the evaluated program deadlocked
|
||||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
--> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
||||||
| ^ this thread got stuck here
|
| ^ this thread got stuck here
|
||||||
|
|
|
|
||||||
= note: BACKTRACE:
|
= note: BACKTRACE:
|
||||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
= note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
note: inside `main`
|
note: inside `main`
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@ error: the evaluated program deadlocked
|
||||||
= note: BACKTRACE on thread `unnamed-ID`:
|
= note: BACKTRACE on thread `unnamed-ID`:
|
||||||
|
|
||||||
error: the evaluated program deadlocked
|
error: the evaluated program deadlocked
|
||||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
--> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
||||||
| ^ this thread got stuck here
|
| ^ this thread got stuck here
|
||||||
|
|
|
|
||||||
= note: BACKTRACE:
|
= note: BACKTRACE:
|
||||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
= note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
note: inside `main`
|
note: inside `main`
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
error: the evaluated program deadlocked
|
error: the evaluated program deadlocked
|
||||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
--> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
||||||
| ^ this thread got stuck here
|
| ^ this thread got stuck here
|
||||||
|
|
|
|
||||||
= note: BACKTRACE:
|
= note: BACKTRACE:
|
||||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
= note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
note: inside `main`
|
note: inside `main`
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
error: the evaluated program deadlocked
|
error: the evaluated program deadlocked
|
||||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
--> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
||||||
| ^ this thread got stuck here
|
| ^ this thread got stuck here
|
||||||
|
|
|
|
||||||
= note: BACKTRACE:
|
= note: BACKTRACE:
|
||||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
= note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
note: inside `main`
|
note: inside `main`
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
error: the evaluated program deadlocked
|
error: the evaluated program deadlocked
|
||||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
--> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
||||||
| ^ this thread got stuck here
|
| ^ this thread got stuck here
|
||||||
|
|
|
|
||||||
= note: BACKTRACE:
|
= note: BACKTRACE:
|
||||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
= note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
note: inside `main`
|
note: inside `main`
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue