From 970baad714d4119e5f93647163f197c9474d6ace Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 Mar 2015 20:21:17 -0700 Subject: [PATCH 1/2] std: Clean up the sys::thread modules This module had become a #[cfg] jungle, try to bring at least a small semblance of order to it! --- src/libstd/sys/common/thread.rs | 15 +- src/libstd/sys/unix/thread.rs | 304 +++++++++++++------------------ src/libstd/sys/windows/thread.rs | 50 ++--- 3 files changed, 148 insertions(+), 221 deletions(-) diff --git a/src/libstd/sys/common/thread.rs b/src/libstd/sys/common/thread.rs index 731617858e95f..f45daea18a21f 100644 --- a/src/libstd/sys/common/thread.rs +++ b/src/libstd/sys/common/thread.rs @@ -8,28 +8,23 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use core::prelude::*; +use prelude::v1::*; -use boxed::Box; -use mem; use usize; use libc; use thunk::Thunk; use sys_common::stack; -use sys::{thread, stack_overflow}; +use sys::stack_overflow; // This is the starting point of rust os threads. The first thing we do // is make sure that we don't trigger __morestack (also why this has a // no_stack_check annotation), and then we extract the main function // and invoke it. #[no_stack_check] -pub fn start_thread(main: *mut libc::c_void) -> thread::rust_thread_return { +pub fn start_thread(main: *mut libc::c_void) { unsafe { stack::record_os_managed_stack_bounds(0, usize::MAX); - let handler = stack_overflow::Handler::new(); - let f: Box = Box::from_raw(main as *mut Thunk); - f.invoke(()); - drop(handler); - mem::transmute(0 as thread::rust_thread_return) + let _handler = stack_overflow::Handler::new(); + Box::from_raw(main as *mut Thunk).invoke(()); } } diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index b4002f266a187..c5c86d8301d1f 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -12,44 +12,30 @@ use core::prelude::*; -use io; -use boxed; -use boxed::Box; use cmp; +use ffi::CString; +use io; +use libc::consts::os::posix01::PTHREAD_STACK_MIN; +use libc; use mem; use ptr; -use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN}; -use libc; +use sys::os; use thunk::Thunk; -use ffi::CString; +use time::Duration; use sys_common::stack::RED_ZONE; use sys_common::thread::*; pub type rust_thread = libc::pthread_t; -pub type rust_thread_return = *mut u8; -pub type StartFn = extern "C" fn(*mut libc::c_void) -> rust_thread_return; - -#[no_stack_check] -pub extern fn thread_start(main: *mut libc::c_void) -> rust_thread_return { - return start_thread(main); -} #[cfg(all(not(target_os = "linux"), not(target_os = "macos"), not(target_os = "bitrig"), not(target_os = "openbsd")))] pub mod guard { - pub unsafe fn current() -> uint { - 0 - } - - pub unsafe fn main() -> uint { - 0 - } - - pub unsafe fn init() { - } + pub unsafe fn current() -> usize { 0 } + pub unsafe fn main() -> usize { 0 } + pub unsafe fn init() {} } @@ -57,26 +43,22 @@ pub mod guard { target_os = "macos", target_os = "bitrig", target_os = "openbsd"))] +#[allow(unused_imports)] pub mod guard { - use super::*; - #[cfg(any(target_os = "linux", - target_os = "android", - target_os = "bitrig", - target_os = "openbsd"))] - use mem; - #[cfg(any(target_os = "linux", target_os = "android"))] - use ptr; - use libc; - use libc::funcs::posix88::mman::{mmap}; + use libc::{self, pthread_t}; + use libc::funcs::posix88::mman::mmap; use libc::consts::os::posix88::{PROT_NONE, MAP_PRIVATE, MAP_ANON, MAP_FAILED, MAP_FIXED}; + use mem; + use ptr; + use super::{pthread_self, pthread_attr_destroy}; + use sys::os; // These are initialized in init() and only read from after - static mut PAGE_SIZE: uint = 0; - static mut GUARD_PAGE: uint = 0; + static mut GUARD_PAGE: usize = 0; #[cfg(any(target_os = "macos", target_os = "bitrig", @@ -88,28 +70,16 @@ pub mod guard { #[cfg(any(target_os = "linux", target_os = "android"))] unsafe fn get_stack_start() -> *mut libc::c_void { let mut attr: libc::pthread_attr_t = mem::zeroed(); - if pthread_getattr_np(pthread_self(), &mut attr) != 0 { - panic!("failed to get thread attributes"); - } + assert_eq!(pthread_getattr_np(pthread_self(), &mut attr), 0); let mut stackaddr = ptr::null_mut(); let mut stacksize = 0; - if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 { - panic!("failed to get stack information"); - } - if pthread_attr_destroy(&mut attr) != 0 { - panic!("failed to destroy thread attributes"); - } + assert_eq!(pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0); + assert_eq!(pthread_attr_destroy(&mut attr), 0); stackaddr } pub unsafe fn init() { - let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE); - if psize == -1 { - panic!("failed to get page size"); - } - - PAGE_SIZE = psize as uint; - + let psize = os::page_size(); let mut stackaddr = get_stack_start(); // Ensure stackaddr is page aligned! A parent process might @@ -118,9 +88,9 @@ pub mod guard { // stackaddr < stackaddr + stacksize, so if stackaddr is not // page-aligned, calculate the fix such that stackaddr < // new_page_aligned_stackaddr < stackaddr + stacksize - let remainder = (stackaddr as usize) % (PAGE_SIZE as usize); + let remainder = (stackaddr as usize) % psize; if remainder != 0 { - stackaddr = ((stackaddr as usize) + (PAGE_SIZE as usize) - remainder) + stackaddr = ((stackaddr as usize) + psize - remainder) as *mut libc::c_void; } @@ -128,7 +98,7 @@ pub mod guard { // This ensures SIGBUS will be raised on // stack overflow. let result = mmap(stackaddr, - PAGE_SIZE as libc::size_t, + psize as libc::size_t, PROT_NONE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, @@ -138,124 +108,119 @@ pub mod guard { panic!("failed to allocate a guard page"); } - let offset = if cfg!(target_os = "linux") { - 2 - } else { - 1 - }; + let offset = if cfg!(target_os = "linux") {2} else {1}; - GUARD_PAGE = stackaddr as uint + offset * PAGE_SIZE; + GUARD_PAGE = stackaddr as usize + offset * psize; } - pub unsafe fn main() -> uint { + pub unsafe fn main() -> usize { GUARD_PAGE } #[cfg(target_os = "macos")] - pub unsafe fn current() -> uint { + pub unsafe fn current() -> usize { + extern { + fn pthread_get_stackaddr_np(thread: pthread_t) -> *mut libc::c_void; + fn pthread_get_stacksize_np(thread: pthread_t) -> libc::size_t; + } (pthread_get_stackaddr_np(pthread_self()) as libc::size_t - - pthread_get_stacksize_np(pthread_self())) as uint + pthread_get_stacksize_np(pthread_self())) as usize } - #[cfg(target_os = "openbsd")] - pub unsafe fn current() -> uint { - let mut current_stack: stack_t = mem::zeroed(); - if pthread_stackseg_np(pthread_self(), &mut current_stack) != 0 { - panic!("failed to get current stack: pthread_stackseg_np") + #[cfg(any(target_os = "openbsd", target_os = "bitrig"))] + pub unsafe fn current() -> usize { + #[repr(C)] + pub struct stack_t { + ss_sp: *mut libc::c_void, + ss_size: libc::size_t, + ss_flags: libc::c_int, + } + extern { + fn pthread_stackseg_np(thread: pthread_t, + sinfo: *mut stack_t) -> libc::c_uint; } + let mut current_stack: stack_t = mem::zeroed(); + assert_eq!(pthread_stackseg_np(pthread_self(), &mut current_stack), 0); + + let extra = if cfg!(target_os = "bitrig") {3} else {1} * os::page_size(); if pthread_main_np() == 1 { // main thread - current_stack.ss_sp as uint - current_stack.ss_size as uint + PAGE_SIZE as uint - + current_stack.ss_sp as usize - current_stack.ss_size as usize + extra } else { // new thread - current_stack.ss_sp as uint - current_stack.ss_size as uint + current_stack.ss_sp as usize - current_stack.ss_size as usize } } #[cfg(any(target_os = "linux", target_os = "android"))] - pub unsafe fn current() -> uint { + pub unsafe fn current() -> usize { let mut attr: libc::pthread_attr_t = mem::zeroed(); - if pthread_getattr_np(pthread_self(), &mut attr) != 0 { - panic!("failed to get thread attributes"); - } + assert_eq!(pthread_getattr_np(pthread_self(), &mut attr), 0); let mut guardsize = 0; - if pthread_attr_getguardsize(&attr, &mut guardsize) != 0 { - panic!("failed to get stack guard page"); - } + assert_eq!(pthread_attr_getguardsize(&attr, &mut guardsize), 0); if guardsize == 0 { panic!("there is no guard page"); } let mut stackaddr = ptr::null_mut(); - let mut stacksize = 0; - if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 { - panic!("failed to get stack information"); - } - if pthread_attr_destroy(&mut attr) != 0 { - panic!("failed to destroy thread attributes"); - } + let mut size = 0; + assert_eq!(pthread_attr_getstack(&attr, &mut stackaddr, &mut size), 0); + assert_eq!(pthread_attr_destroy(&mut attr), 0); - stackaddr as uint + guardsize as uint + stackaddr as usize + guardsize as usize } - #[cfg(target_os = "bitrig")] - pub unsafe fn current() -> uint { - let mut current_stack: stack_t = mem::zeroed(); - if pthread_stackseg_np(pthread_self(), &mut current_stack) != 0 { - panic!("failed to get current stack: pthread_stackseg_np") - } - - if pthread_main_np() == 1 { - // main thread - current_stack.ss_sp as uint - current_stack.ss_size as uint + 3 * PAGE_SIZE as uint - } else { - // new thread - current_stack.ss_sp as uint - current_stack.ss_size as uint - } + #[cfg(any(target_os = "linux", target_os = "android"))] + extern { + fn pthread_getattr_np(native: libc::pthread_t, + attr: *mut libc::pthread_attr_t) -> libc::c_int; + fn pthread_attr_getguardsize(attr: *const libc::pthread_attr_t, + guardsize: *mut libc::size_t) -> libc::c_int; + fn pthread_attr_getstack(attr: *const libc::pthread_attr_t, + stackaddr: *mut *mut libc::c_void, + stacksize: *mut libc::size_t) -> libc::c_int; } } -pub unsafe fn create(stack: uint, p: Thunk) -> io::Result { +pub unsafe fn create(stack: usize, p: Thunk) -> io::Result { + let p = box p; let mut native: libc::pthread_t = mem::zeroed(); let mut attr: libc::pthread_attr_t = mem::zeroed(); assert_eq!(pthread_attr_init(&mut attr), 0); - assert_eq!(pthread_attr_setdetachstate(&mut attr, - PTHREAD_CREATE_JOINABLE), 0); // Reserve room for the red zone, the runtime's stack of last resort. - let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr) as uint); + let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr) as usize); match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) { - 0 => { - }, - libc::EINVAL => { + 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 = libc::sysconf(libc::_SC_PAGESIZE) as uint; + let page_size = os::page_size(); let stack_size = (stack_size + page_size - 1) & - (-(page_size as int - 1) as uint - 1); - assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t), 0); - }, - errno => { - // This cannot really happen. - panic!("pthread_attr_setstacksize() error: {}", errno); - }, + (-(page_size as isize - 1) as usize - 1); + assert_eq!(pthread_attr_setstacksize(&mut attr, + stack_size as libc::size_t), 0); + } }; - // must box since sizeof(p)=2*uint - let raw_p = boxed::into_raw(box p); - let arg = raw_p as *mut libc::c_void; - let ret = pthread_create(&mut native, &attr, thread_start, arg); + let ret = pthread_create(&mut native, &attr, thread_start, + &*p as *const _ as *mut _); assert_eq!(pthread_attr_destroy(&mut attr), 0); - if ret != 0 { - // be sure to not leak the closure - let _p: Box = Box::from_raw(raw_p); + return if ret != 0 { Err(io::Error::from_os_error(ret)) } else { + mem::forget(p); // ownership passed to pthread_create Ok(native) + }; + + #[no_stack_check] + extern fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { + start_thread(main); + 0 as *mut _ } } @@ -263,14 +228,14 @@ pub unsafe fn create(stack: uint, p: Thunk) -> io::Result { pub unsafe fn set_name(name: &str) { // pthread_setname_np() since glibc 2.12 // availability autodetected via weak linkage - let cname = CString::new(name).unwrap(); - type F = unsafe extern "C" fn(libc::pthread_t, *const libc::c_char) - -> libc::c_int; + type F = unsafe extern fn(libc::pthread_t, *const libc::c_char) + -> libc::c_int; extern { #[linkage = "extern_weak"] static pthread_setname_np: *const (); } if !pthread_setname_np.is_null() { + let cname = CString::new(name).unwrap(); mem::transmute::<*const (), F>(pthread_setname_np)(pthread_self(), cname.as_ptr()); } @@ -281,14 +246,18 @@ pub unsafe fn set_name(name: &str) { target_os = "bitrig", target_os = "openbsd"))] pub unsafe fn set_name(name: &str) { - // pthread_set_name_np() since almost forever on all BSDs + extern { + fn pthread_set_name_np(tid: libc::pthread_t, name: *const libc::c_char); + } let cname = CString::new(name).unwrap(); pthread_set_name_np(pthread_self(), cname.as_ptr()); } #[cfg(any(target_os = "macos", target_os = "ios"))] pub unsafe fn set_name(name: &str) { - // pthread_setname_np() since OS X 10.6 and iOS 3.2 + extern { + fn pthread_setname_np(name: *const libc::c_char) -> libc::c_int; + } let cname = CString::new(name).unwrap(); pthread_setname_np(cname.as_ptr()); } @@ -301,7 +270,28 @@ pub unsafe fn detach(native: rust_thread) { assert_eq!(pthread_detach(native), 0); } -pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); } +pub unsafe fn yield_now() { + assert_eq!(sched_yield(), 0); +} + +pub fn sleep(dur: Duration) { + unsafe { + if dur < Duration::zero() { + return yield_now() + } + let seconds = dur.num_seconds(); + let ns = dur - Duration::seconds(seconds); + let mut ts = libc::timespec { + tv_sec: seconds as libc::time_t, + tv_nsec: ns.num_nanoseconds().unwrap() as libc::c_long, + }; + // If we're awoken with a signal then the return value will be -1 and + // nanosleep will fill in `ts` with the remaining time. + while libc::nanosleep(&ts, &mut ts) == -1 { + assert_eq!(os::errno(), libc::EINTR); + } + } +} // glibc >= 2.15 has a __pthread_get_minstack() function that returns // PTHREAD_STACK_MIN plus however many bytes are needed for thread-local @@ -334,67 +324,19 @@ fn min_stack_size(_: *const libc::pthread_attr_t) -> libc::size_t { PTHREAD_STACK_MIN } -#[cfg(any(target_os = "linux", target_os = "android"))] -extern { - pub fn pthread_self() -> libc::pthread_t; - pub fn pthread_getattr_np(native: libc::pthread_t, - attr: *mut libc::pthread_attr_t) -> libc::c_int; - pub fn pthread_attr_getguardsize(attr: *const libc::pthread_attr_t, - guardsize: *mut libc::size_t) -> libc::c_int; - pub fn pthread_attr_getstack(attr: *const libc::pthread_attr_t, - stackaddr: *mut *mut libc::c_void, - stacksize: *mut libc::size_t) -> libc::c_int; -} - -#[cfg(any(target_os = "freebsd", - target_os = "dragonfly", - target_os = "openbsd"))] -extern { - pub fn pthread_self() -> libc::pthread_t; - fn pthread_set_name_np(tid: libc::pthread_t, name: *const libc::c_char); -} - -#[cfg(any(target_os = "macos", target_os = "ios"))] -extern { - pub fn pthread_self() -> libc::pthread_t; - pub fn pthread_get_stackaddr_np(thread: libc::pthread_t) -> *mut libc::c_void; - pub fn pthread_get_stacksize_np(thread: libc::pthread_t) -> libc::size_t; - fn pthread_setname_np(name: *const libc::c_char) -> libc::c_int; -} - -#[cfg(target_os = "bitrig")] extern { - pub fn pthread_self() -> libc::pthread_t; - pub fn pthread_stackseg_np(thread: libc::pthread_t, - sinfo: *mut stack_t) -> libc::c_uint; - pub fn pthread_main_np() -> libc::c_uint; - fn pthread_set_name_np(tid: libc::pthread_t, name: *const libc::c_char); -} + #[cfg(any(target_os = "bitrig", target_os = "openbsd"))] + fn pthread_main_np() -> libc::c_uint; -#[cfg(target_os = "openbsd")] -extern { - pub fn pthread_stackseg_np(thread: libc::pthread_t, - sinfo: *mut stack_t) -> libc::c_uint; - pub fn pthread_main_np() -> libc::c_uint; -} - -#[cfg(any(target_os = "bitrig", target_os = "openbsd"))] -#[repr(C)] -pub struct stack_t { - pub ss_sp: *mut libc::c_void, - pub ss_size: libc::size_t, - pub ss_flags: libc::c_int, -} - -extern { + fn pthread_self() -> libc::pthread_t; fn pthread_create(native: *mut libc::pthread_t, attr: *const libc::pthread_attr_t, - f: StartFn, + f: extern fn(*mut libc::c_void) -> *mut libc::c_void, value: *mut libc::c_void) -> libc::c_int; fn pthread_join(native: libc::pthread_t, value: *mut *mut libc::c_void) -> libc::c_int; fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int; - pub fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int; + fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int; fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t, stack_size: libc::size_t) -> libc::c_int; fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t, diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs index aa22b6b1307a3..b361faba0f628 100644 --- a/src/libstd/sys/windows/thread.rs +++ b/src/libstd/sys/windows/thread.rs @@ -10,43 +10,27 @@ use prelude::v1::*; -use boxed; use cmp; use io; -use ptr; -use libc; +use libc::{self, c_void}; use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL, LPVOID, DWORD, LPDWORD, HANDLE}; -use thunk::Thunk; +use mem; +use ptr; use sys_common::stack::RED_ZONE; use sys_common::thread::*; +use thunk::Thunk; pub type rust_thread = HANDLE; -pub type rust_thread_return = DWORD; - -pub type StartFn = extern "system" fn(*mut libc::c_void) -> rust_thread_return; - -#[no_stack_check] -pub extern "system" fn thread_start(main: *mut libc::c_void) -> rust_thread_return { - return start_thread(main); -} pub mod guard { - pub unsafe fn main() -> uint { - 0 - } - - pub unsafe fn current() -> uint { - 0 - } - - pub unsafe fn init() { - } + pub unsafe fn main() -> uint { 0 } + pub unsafe fn current() -> uint { 0 } + pub unsafe fn init() {} } -pub unsafe fn create(stack: uint, p: Thunk) -> io::Result { - let raw_p = boxed::into_raw(box p); - let arg = raw_p as *mut libc::c_void; +pub unsafe fn create(stack: usize, p: Thunk) -> io::Result { + 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 @@ -58,14 +42,20 @@ pub unsafe fn create(stack: uint, p: Thunk) -> io::Result { // 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, arg, 0, ptr::null_mut()); + thread_start, &*p as *const _ as *mut _, + 0, ptr::null_mut()); - if ret as uint == 0 { - // be sure to not leak the closure - let _p: Box = Box::from_raw(raw_p); + return if ret as usize == 0 { Err(io::Error::last_os_error()) } else { + mem::forget(p); // ownership passed to CreateThread Ok(ret) + }; + + #[no_stack_check] + extern "system" fn thread_start(main: *mut libc::c_void) -> DWORD { + start_thread(main); + 0 } } @@ -96,7 +86,7 @@ pub unsafe fn yield_now() { extern "system" { fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, - lpStartAddress: StartFn, + lpStartAddress: extern "system" fn(*mut c_void) -> DWORD, lpParameter: LPVOID, dwCreationFlags: DWORD, lpThreadId: LPDWORD) -> HANDLE; From 04cf5344111c357ad80335b88709281bb4bfaa0a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 Mar 2015 20:36:31 -0700 Subject: [PATCH 2/2] std: Implement `thread::sleep` This function is the current replacement for `std::old_io::timer` which will soon be deprecated. This function is unstable and has its own feature gate as it does not yet have an RFC nor has it existed for very long. --- src/libstd/sys/unix/thread.rs | 16 +++++++++++++++- src/libstd/sys/windows/thread.rs | 16 ++++++++++++++++ src/libstd/thread.rs | 19 +++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index c5c86d8301d1f..045082949812b 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -287,10 +287,24 @@ pub fn sleep(dur: Duration) { }; // If we're awoken with a signal then the return value will be -1 and // nanosleep will fill in `ts` with the remaining time. - while libc::nanosleep(&ts, &mut ts) == -1 { + while dosleep(&mut ts) == -1 { assert_eq!(os::errno(), libc::EINTR); } } + + #[cfg(target_os = "linux")] + unsafe fn dosleep(ts: *mut libc::timespec) -> libc::c_int { + extern { + fn clock_nanosleep(clock_id: libc::c_int, flags: libc::c_int, + request: *const libc::timespec, + remain: *mut libc::timespec) -> libc::c_int; + } + clock_nanosleep(libc::CLOCK_MONOTONIC, 0, ts, ts) + } + #[cfg(not(target_os = "linux"))] + unsafe fn dosleep(ts: *mut libc::timespec) -> libc::c_int { + libc::nanosleep(ts, ts) + } } // glibc >= 2.15 has a __pthread_get_minstack() function that returns diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs index b361faba0f628..d1d4ad90081bf 100644 --- a/src/libstd/sys/windows/thread.rs +++ b/src/libstd/sys/windows/thread.rs @@ -20,6 +20,7 @@ use ptr; use sys_common::stack::RED_ZONE; use sys_common::thread::*; use thunk::Thunk; +use time::Duration; pub type rust_thread = HANDLE; @@ -82,6 +83,20 @@ pub unsafe fn yield_now() { SwitchToThread(); } +pub fn sleep(dur: Duration) { + unsafe { + if dur < Duration::zero() { + return 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}; + Sleep(ms as DWORD); + } +} + #[allow(non_snake_case)] extern "system" { fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, @@ -92,4 +107,5 @@ extern "system" { lpThreadId: LPDWORD) -> HANDLE; fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; fn SwitchToThread() -> BOOL; + fn Sleep(dwMilliseconds: DWORD); } diff --git a/src/libstd/thread.rs b/src/libstd/thread.rs index adc3b77407a61..0216f324fdbce 100644 --- a/src/libstd/thread.rs +++ b/src/libstd/thread.rs @@ -379,6 +379,19 @@ pub fn panicking() -> bool { unwind::panicking() } +/// Put the current thread to sleep for the specified amount of time. +/// +/// The thread may sleep longer than the duration specified due to scheduling +/// specifics or platform-dependent functionality. Note that on unix platforms +/// this function will not return early due to a signal being received or a +/// spurious wakeup. +#[unstable(feature = "thread_sleep", + reason = "recently added, needs an RFC, and `Duration` itself is \ + unstable")] +pub fn sleep(dur: Duration) { + imp::sleep(dur) +} + /// Block unless or until the current thread's token is made available (may wake spuriously). /// /// See the module doc for more detail. @@ -935,6 +948,12 @@ mod test { } } + #[test] + fn sleep_smoke() { + thread::sleep(Duration::milliseconds(2)); + thread::sleep(Duration::milliseconds(-2)); + } + // NOTE: the corresponding test for stderr is in run-pass/task-stderr, due // to the test harness apparently interfering with stderr configuration. }