summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys/windows/thread.rs
blob: 1fe744935193cc1d60037e30f5e6b2a0047215e8 (plain)
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
use crate::ffi::CStr;
use crate::io;
use crate::num::NonZeroUsize;
use crate::os::windows::io::AsRawHandle;
use crate::os::windows::io::HandleOrNull;
use crate::ptr;
use crate::sys::c;
use crate::sys::handle::Handle;
use crate::sys::stack_overflow;
use crate::sys_common::FromInner;
use crate::time::Duration;

use core::ffi::c_void;

use super::time::WaitableTimer;
use super::to_u16s;

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
    pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
        let p = Box::into_raw(Box::new(p));

        // FIXME On UNIX, we guard against stack sizes that are too small but
        // that's because pthreads enforces that stacks are at least
        // PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's
        // just that below a certain threshold you can't do anything useful.
        // That threshold is application and architecture-specific, however.
        let ret = c::CreateThread(
            ptr::null_mut(),
            stack,
            Some(thread_start),
            p as *mut _,
            c::STACK_SIZE_PARAM_IS_A_RESERVATION,
            ptr::null_mut(),
        );
        let ret = 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.
            drop(Box::from_raw(p));
            Err(io::Error::last_os_error())
        };

        extern "system" fn thread_start(main: *mut c_void) -> c::DWORD {
            unsafe {
                // Next, set up our stack overflow handler which may get triggered if we run
                // out of stack.
                let _handler = stack_overflow::Handler::new();
                // Finally, let's run some code.
                Box::from_raw(main as *mut Box<dyn FnOnce()>)();
            }
            0
        }
    }

    pub fn set_name(name: &CStr) {
        if let Ok(utf8) = name.to_str() {
            if let Ok(utf16) = to_u16s(utf8) {
                unsafe {
                    c::SetThreadDescription(c::GetCurrentThread(), utf16.as_ptr());
                };
            };
        };
    }

    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 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 behaviour of `Sleep`.
        if dur.is_zero() || high_precision_sleep(dur).is_err() {
            unsafe { c::Sleep(super::dur2timeout(dur)) }
        }
    }

    pub fn handle(&self) -> &Handle {
        &self.handle
    }

    pub fn into_handle(self) -> Handle {
        self.handle
    }
}

pub fn available_parallelism() -> io::Result<NonZeroUsize> {
    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::const_io_error!(
            io::ErrorKind::NotFound,
            "The number of hardware threads is not known for the target platform",
        )),
        cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus) }),
    }
}

#[cfg_attr(test, allow(dead_code))]
pub mod guard {
    pub type Guard = !;
    pub unsafe fn current() -> Option<Guard> {
        None
    }
    pub unsafe fn init() -> Option<Guard> {
        None
    }
}