diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2015-04-14 22:13:57 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2015-04-22 10:42:33 -0700 |
| commit | 2e1100997863c4951371cf39554c53266cacb37d (patch) | |
| tree | e4d266578d80fa55705ba349c4508191fd5a692b /src/libstd/sys/windows | |
| parent | e9e9279d87d5786fcb8e12482f2920979602267b (diff) | |
| download | rust-2e1100997863c4951371cf39554c53266cacb37d.tar.gz rust-2e1100997863c4951371cf39554c53266cacb37d.zip | |
std: Audit std::thread implementations
Much of this code hasn't been updated in quite some time and this commit does a small audit of the functionality: * Implementation functions now centralize all functionality on a locally defined `Thread` type. * The `detach` method has been removed in favor of a `Drop` implementation. This notably fixes leaking thread handles on Windows. * The `Thread` structure is now appropriately annotated with `Send` and `Sync` automatically on Windows and in a custom fashion on Unix. * The unsafety of creating a thread has been pushed out to the right boundaries now. Closes #24442
Diffstat (limited to 'src/libstd/sys/windows')
| -rw-r--r-- | src/libstd/sys/windows/c.rs | 11 | ||||
| -rw-r--r-- | src/libstd/sys/windows/thread.rs | 143 |
2 files changed, 77 insertions, 77 deletions
diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index 331bfbfff36..b07d063de45 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -471,6 +471,17 @@ extern "system" { hWritePipe: libc::LPHANDLE, lpPipeAttributes: libc::LPSECURITY_ATTRIBUTES, nSize: libc::DWORD) -> libc::BOOL; + pub fn CreateThread(lpThreadAttributes: libc::LPSECURITY_ATTRIBUTES, + dwStackSize: libc::SIZE_T, + lpStartAddress: extern "system" fn(*mut libc::c_void) + -> libc::DWORD, + lpParameter: libc::LPVOID, + dwCreationFlags: libc::DWORD, + lpThreadId: libc::LPDWORD) -> libc::HANDLE; + pub fn WaitForSingleObject(hHandle: libc::HANDLE, + dwMilliseconds: libc::DWORD) -> libc::DWORD; + pub fn SwitchToThread() -> libc::BOOL; + pub fn Sleep(dwMilliseconds: libc::DWORD); } #[link(name = "userenv")] diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs index 98e4a737c7b..797f45f8702 100644 --- a/src/libstd/sys/windows/thread.rs +++ b/src/libstd/sys/windows/thread.rs @@ -10,102 +10,91 @@ use prelude::v1::*; +use alloc::boxed::FnBox; use cmp; use io; -use libc::{self, c_void}; -use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL, - LPVOID, DWORD, LPDWORD, HANDLE}; +use libc::{self, c_void, DWORD}; use mem; use ptr; +use sys::c; +use sys::handle::Handle; use sys_common::stack::RED_ZONE; use sys_common::thread::*; -use thunk::Thunk; use time::Duration; -pub type rust_thread = HANDLE; - -pub mod guard { - pub unsafe fn main() -> usize { 0 } - pub unsafe fn current() -> usize { 0 } - pub unsafe fn init() {} +pub struct Thread { + handle: Handle } -pub unsafe fn create(stack: usize, p: Thunk) -> io::Result<rust_thread> { - let p = box 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. - // For now, the only requirement is that it's big enough to hold the - // red zone. Round up to the next 64 kB because that's what the NT - // kernel does, might as well make it explicit. With the current - // 20 kB red zone, that makes for a 64 kB minimum stack. - let stack_size = (cmp::max(stack, RED_ZONE) + 0xfffe) & (-0xfffe - 1); - let ret = CreateThread(ptr::null_mut(), stack_size as libc::size_t, - thread_start, &*p as *const _ as *mut _, - 0, ptr::null_mut()); +impl Thread { + pub unsafe fn new<'a>(stack: usize, p: Box<FnBox() + 'a>) + -> io::Result<Thread> { + let p = box p; - return if ret as usize == 0 { - Err(io::Error::last_os_error()) - } else { - mem::forget(p); // ownership passed to CreateThread - Ok(ret) - }; + // 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. + // For now, the only requirement is that it's big enough to hold the + // red zone. Round up to the next 64 kB because that's what the NT + // kernel does, might as well make it explicit. With the current + // 20 kB red zone, that makes for a 64 kB minimum stack. + let stack_size = (cmp::max(stack, RED_ZONE) + 0xfffe) & (-0xfffe - 1); + let ret = c::CreateThread(ptr::null_mut(), stack_size as libc::size_t, + thread_start, &*p as *const _ as *mut _, + 0, ptr::null_mut()); - #[no_stack_check] - extern "system" fn thread_start(main: *mut libc::c_void) -> DWORD { - start_thread(main); - 0 - } -} + return if ret as usize == 0 { + Err(io::Error::last_os_error()) + } else { + mem::forget(p); // ownership passed to CreateThread + Ok(Thread { handle: Handle::new(ret) }) + }; -pub unsafe fn set_name(_name: &str) { - // Windows threads are nameless - // The names in MSVC debugger are obtained using a "magic" exception, - // which requires a use of MS C++ extensions. - // See https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx -} + #[no_stack_check] + extern "system" fn thread_start(main: *mut libc::c_void) -> DWORD { + unsafe { start_thread(main); } + 0 + } + } -pub unsafe fn join(native: rust_thread) { - use libc::consts::os::extra::INFINITE; - WaitForSingleObject(native, INFINITE); -} + pub fn set_name(_name: &str) { + // Windows threads are nameless + // The names in MSVC debugger are obtained using a "magic" exception, + // which requires a use of MS C++ extensions. + // See https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx + } -pub unsafe fn detach(native: rust_thread) { - assert!(libc::CloseHandle(native) != 0); -} + pub fn join(self) { + use libc::consts::os::extra::INFINITE; + unsafe { c::WaitForSingleObject(self.handle.raw(), INFINITE); } + } -pub unsafe 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. - SwitchToThread(); -} + 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) { - unsafe { - if dur < Duration::zero() { - return yield_now() + pub fn sleep(dur: Duration) { + unsafe { + if dur < Duration::zero() { + return Thread::yield_now() + } + let ms = dur.num_milliseconds(); + // if we have a fractional number of milliseconds then add an extra + // millisecond to sleep for + let extra = dur - Duration::milliseconds(ms); + let ms = ms + if extra.is_zero() {0} else {1}; + c::Sleep(ms as DWORD); } - let ms = dur.num_milliseconds(); - // if we have a fractional number of milliseconds then add an extra - // millisecond to sleep for - let extra = dur - Duration::milliseconds(ms); - let ms = ms + if extra.is_zero() {0} else {1}; - Sleep(ms as DWORD); } } -#[allow(non_snake_case)] -extern "system" { - fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, - dwStackSize: SIZE_T, - lpStartAddress: extern "system" fn(*mut c_void) -> DWORD, - lpParameter: LPVOID, - dwCreationFlags: DWORD, - lpThreadId: LPDWORD) -> HANDLE; - fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; - fn SwitchToThread() -> BOOL; - fn Sleep(dwMilliseconds: DWORD); +pub mod guard { + pub unsafe fn main() -> usize { 0 } + pub unsafe fn current() -> usize { 0 } + pub unsafe fn init() {} } |
