1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
use core::ffi::c_void;
use crate::ffi::CStr;
use crate::num::NonZero;
use crate::os::windows::io::{AsRawHandle, HandleOrNull};
use crate::sys::handle::Handle;
use crate::sys::pal::time::WaitableTimer;
use crate::sys::pal::{dur2timeout, to_u16s};
use crate::sys::{c, stack_overflow};
use crate::sys_common::FromInner;
use crate::time::Duration;
use crate::{io, ptr};
pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
pub struct Thread {
handle: Handle,
}
impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub unsafe fn new(
stack: usize,
_name: Option<&str>,
p: Box<dyn FnOnce()>,
) -> io::Result<Thread> {
let p = Box::into_raw(Box::new(p));
// CreateThread rounds up values for the stack size to the nearest page size (at least 4kb).
// If a value of zero is given then the default stack size is used instead.
// SAFETY: `thread_start` has the right ABI for a thread's entry point.
// `p` is simply passed through to the new thread without being touched.
let ret = unsafe {
let ret = c::CreateThread(
ptr::null_mut(),
stack,
Some(thread_start),
p as *mut _,
c::STACK_SIZE_PARAM_IS_A_RESERVATION,
ptr::null_mut(),
);
HandleOrNull::from_raw_handle(ret)
};
return if let Ok(handle) = ret.try_into() {
Ok(Thread { handle: Handle::from_inner(handle) })
} else {
// 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::last_os_error())
};
unsafe extern "system" fn thread_start(main: *mut c_void) -> u32 {
// Next, reserve some stack space for if we otherwise run out of stack.
stack_overflow::reserve_stack();
// Finally, let's run some code.
// SAFETY: We are simply recreating the box that was leaked earlier.
// It's the responsibility of the one who call `Thread::new` to ensure this is safe to call here.
unsafe { Box::from_raw(main as *mut Box<dyn FnOnce()>)() };
0
}
}
pub fn join(self) {
let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
if rc == c::WAIT_FAILED {
panic!("failed to join on thread: {}", io::Error::last_os_error());
}
}
pub fn handle(&self) -> &Handle {
&self.handle
}
pub fn into_handle(self) -> Handle {
self.handle
}
}
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
let res = unsafe {
let mut sysinfo: c::SYSTEM_INFO = crate::mem::zeroed();
c::GetSystemInfo(&mut sysinfo);
sysinfo.dwNumberOfProcessors as usize
};
match res {
0 => Err(io::Error::UNKNOWN_THREAD_COUNT),
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();
}
}
|