1
Fork 0

Auto merge of #23330 - alexcrichton:thread-sleep, r=aturon

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.
This commit is contained in:
bors 2015-03-17 17:15:54 +00:00
commit bfac337daa
4 changed files with 197 additions and 221 deletions

View file

@ -8,28 +8,23 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use core::prelude::*; use prelude::v1::*;
use boxed::Box;
use mem;
use usize; use usize;
use libc; use libc;
use thunk::Thunk; use thunk::Thunk;
use sys_common::stack; 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 // 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 // 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 // no_stack_check annotation), and then we extract the main function
// and invoke it. // and invoke it.
#[no_stack_check] #[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 { unsafe {
stack::record_os_managed_stack_bounds(0, usize::MAX); stack::record_os_managed_stack_bounds(0, usize::MAX);
let handler = stack_overflow::Handler::new(); let _handler = stack_overflow::Handler::new();
let f: Box<Thunk> = Box::from_raw(main as *mut Thunk); Box::from_raw(main as *mut Thunk).invoke(());
f.invoke(());
drop(handler);
mem::transmute(0 as thread::rust_thread_return)
} }
} }

View file

@ -12,44 +12,30 @@
use core::prelude::*; use core::prelude::*;
use io;
use boxed;
use boxed::Box;
use cmp; use cmp;
use ffi::CString;
use io;
use libc::consts::os::posix01::PTHREAD_STACK_MIN;
use libc;
use mem; use mem;
use ptr; use ptr;
use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN}; use sys::os;
use libc;
use thunk::Thunk; use thunk::Thunk;
use ffi::CString; use time::Duration;
use sys_common::stack::RED_ZONE; use sys_common::stack::RED_ZONE;
use sys_common::thread::*; use sys_common::thread::*;
pub type rust_thread = libc::pthread_t; 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"), #[cfg(all(not(target_os = "linux"),
not(target_os = "macos"), not(target_os = "macos"),
not(target_os = "bitrig"), not(target_os = "bitrig"),
not(target_os = "openbsd")))] not(target_os = "openbsd")))]
pub mod guard { pub mod guard {
pub unsafe fn current() -> uint { pub unsafe fn current() -> usize { 0 }
0 pub unsafe fn main() -> usize { 0 }
} pub unsafe fn init() {}
pub unsafe fn main() -> uint {
0
}
pub unsafe fn init() {
}
} }
@ -57,26 +43,22 @@ pub mod guard {
target_os = "macos", target_os = "macos",
target_os = "bitrig", target_os = "bitrig",
target_os = "openbsd"))] target_os = "openbsd"))]
#[allow(unused_imports)]
pub mod guard { pub mod guard {
use super::*; use libc::{self, pthread_t};
#[cfg(any(target_os = "linux", use libc::funcs::posix88::mman::mmap;
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::consts::os::posix88::{PROT_NONE, use libc::consts::os::posix88::{PROT_NONE,
MAP_PRIVATE, MAP_PRIVATE,
MAP_ANON, MAP_ANON,
MAP_FAILED, MAP_FAILED,
MAP_FIXED}; 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 // These are initialized in init() and only read from after
static mut PAGE_SIZE: uint = 0; static mut GUARD_PAGE: usize = 0;
static mut GUARD_PAGE: uint = 0;
#[cfg(any(target_os = "macos", #[cfg(any(target_os = "macos",
target_os = "bitrig", target_os = "bitrig",
@ -88,28 +70,16 @@ pub mod guard {
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
unsafe fn get_stack_start() -> *mut libc::c_void { unsafe fn get_stack_start() -> *mut libc::c_void {
let mut attr: libc::pthread_attr_t = mem::zeroed(); let mut attr: libc::pthread_attr_t = mem::zeroed();
if pthread_getattr_np(pthread_self(), &mut attr) != 0 { assert_eq!(pthread_getattr_np(pthread_self(), &mut attr), 0);
panic!("failed to get thread attributes");
}
let mut stackaddr = ptr::null_mut(); let mut stackaddr = ptr::null_mut();
let mut stacksize = 0; let mut stacksize = 0;
if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 { assert_eq!(pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0);
panic!("failed to get stack information"); assert_eq!(pthread_attr_destroy(&mut attr), 0);
}
if pthread_attr_destroy(&mut attr) != 0 {
panic!("failed to destroy thread attributes");
}
stackaddr stackaddr
} }
pub unsafe fn init() { pub unsafe fn init() {
let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE); let psize = os::page_size();
if psize == -1 {
panic!("failed to get page size");
}
PAGE_SIZE = psize as uint;
let mut stackaddr = get_stack_start(); let mut stackaddr = get_stack_start();
// Ensure stackaddr is page aligned! A parent process might // Ensure stackaddr is page aligned! A parent process might
@ -118,9 +88,9 @@ pub mod guard {
// stackaddr < stackaddr + stacksize, so if stackaddr is not // stackaddr < stackaddr + stacksize, so if stackaddr is not
// page-aligned, calculate the fix such that stackaddr < // page-aligned, calculate the fix such that stackaddr <
// new_page_aligned_stackaddr < stackaddr + stacksize // new_page_aligned_stackaddr < stackaddr + stacksize
let remainder = (stackaddr as usize) % (PAGE_SIZE as usize); let remainder = (stackaddr as usize) % psize;
if remainder != 0 { if remainder != 0 {
stackaddr = ((stackaddr as usize) + (PAGE_SIZE as usize) - remainder) stackaddr = ((stackaddr as usize) + psize - remainder)
as *mut libc::c_void; as *mut libc::c_void;
} }
@ -128,7 +98,7 @@ pub mod guard {
// This ensures SIGBUS will be raised on // This ensures SIGBUS will be raised on
// stack overflow. // stack overflow.
let result = mmap(stackaddr, let result = mmap(stackaddr,
PAGE_SIZE as libc::size_t, psize as libc::size_t,
PROT_NONE, PROT_NONE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, MAP_PRIVATE | MAP_ANON | MAP_FIXED,
-1, -1,
@ -138,124 +108,119 @@ pub mod guard {
panic!("failed to allocate a guard page"); panic!("failed to allocate a guard page");
} }
let offset = if cfg!(target_os = "linux") { let offset = if cfg!(target_os = "linux") {2} else {1};
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 GUARD_PAGE
} }
#[cfg(target_os = "macos")] #[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_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")] #[cfg(any(target_os = "openbsd", target_os = "bitrig"))]
pub unsafe fn current() -> uint { pub unsafe fn current() -> usize {
let mut current_stack: stack_t = mem::zeroed(); #[repr(C)]
if pthread_stackseg_np(pthread_self(), &mut current_stack) != 0 { pub struct stack_t {
panic!("failed to get current stack: pthread_stackseg_np") 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 { if pthread_main_np() == 1 {
// main thread // 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 { } else {
// new thread // 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"))] #[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(); let mut attr: libc::pthread_attr_t = mem::zeroed();
if pthread_getattr_np(pthread_self(), &mut attr) != 0 { assert_eq!(pthread_getattr_np(pthread_self(), &mut attr), 0);
panic!("failed to get thread attributes");
}
let mut guardsize = 0; let mut guardsize = 0;
if pthread_attr_getguardsize(&attr, &mut guardsize) != 0 { assert_eq!(pthread_attr_getguardsize(&attr, &mut guardsize), 0);
panic!("failed to get stack guard page");
}
if guardsize == 0 { if guardsize == 0 {
panic!("there is no guard page"); panic!("there is no guard page");
} }
let mut stackaddr = ptr::null_mut(); let mut stackaddr = ptr::null_mut();
let mut stacksize = 0; let mut size = 0;
if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 { assert_eq!(pthread_attr_getstack(&attr, &mut stackaddr, &mut size), 0);
panic!("failed to get stack information"); assert_eq!(pthread_attr_destroy(&mut attr), 0);
}
if pthread_attr_destroy(&mut attr) != 0 {
panic!("failed to destroy thread attributes");
}
stackaddr as uint + guardsize as uint stackaddr as usize + guardsize as usize
} }
#[cfg(target_os = "bitrig")] #[cfg(any(target_os = "linux", target_os = "android"))]
pub unsafe fn current() -> uint { extern {
let mut current_stack: stack_t = mem::zeroed(); fn pthread_getattr_np(native: libc::pthread_t,
if pthread_stackseg_np(pthread_self(), &mut current_stack) != 0 { attr: *mut libc::pthread_attr_t) -> libc::c_int;
panic!("failed to get current stack: pthread_stackseg_np") 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,
if pthread_main_np() == 1 { stackaddr: *mut *mut libc::c_void,
// main thread stacksize: *mut libc::size_t) -> libc::c_int;
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
}
} }
} }
pub unsafe fn create(stack: uint, p: Thunk) -> io::Result<rust_thread> { pub unsafe fn create(stack: usize, p: Thunk) -> io::Result<rust_thread> {
let p = box p;
let mut native: libc::pthread_t = mem::zeroed(); let mut native: libc::pthread_t = mem::zeroed();
let mut attr: libc::pthread_attr_t = mem::zeroed(); let mut attr: libc::pthread_attr_t = mem::zeroed();
assert_eq!(pthread_attr_init(&mut attr), 0); 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. // 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) { match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) {
0 => { 0 => {}
}, n => {
libc::EINVAL => { assert_eq!(n, libc::EINVAL);
// EINVAL means |stack_size| is either too small or not a // EINVAL means |stack_size| is either too small or not a
// multiple of the system page size. Because it's definitely // multiple of the system page size. Because it's definitely
// >= PTHREAD_STACK_MIN, it must be an alignment issue. // >= PTHREAD_STACK_MIN, it must be an alignment issue.
// Round up to the nearest page and try again. // 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) & let stack_size = (stack_size + page_size - 1) &
(-(page_size as int - 1) as uint - 1); (-(page_size as isize - 1) as usize - 1);
assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t), 0); 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);
},
}; };
// must box since sizeof(p)=2*uint let ret = pthread_create(&mut native, &attr, thread_start,
let raw_p = boxed::into_raw(box p); &*p as *const _ as *mut _);
let arg = raw_p as *mut libc::c_void;
let ret = pthread_create(&mut native, &attr, thread_start, arg);
assert_eq!(pthread_attr_destroy(&mut attr), 0); assert_eq!(pthread_attr_destroy(&mut attr), 0);
if ret != 0 { return if ret != 0 {
// be sure to not leak the closure
let _p: Box<Thunk> = Box::from_raw(raw_p);
Err(io::Error::from_os_error(ret)) Err(io::Error::from_os_error(ret))
} else { } else {
mem::forget(p); // ownership passed to pthread_create
Ok(native) 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<rust_thread> {
pub unsafe fn set_name(name: &str) { pub unsafe fn set_name(name: &str) {
// pthread_setname_np() since glibc 2.12 // pthread_setname_np() since glibc 2.12
// availability autodetected via weak linkage // availability autodetected via weak linkage
let cname = CString::new(name).unwrap(); type F = unsafe extern fn(libc::pthread_t, *const libc::c_char)
type F = unsafe extern "C" fn(libc::pthread_t, *const libc::c_char) -> libc::c_int;
-> libc::c_int;
extern { extern {
#[linkage = "extern_weak"] #[linkage = "extern_weak"]
static pthread_setname_np: *const (); static pthread_setname_np: *const ();
} }
if !pthread_setname_np.is_null() { if !pthread_setname_np.is_null() {
let cname = CString::new(name).unwrap();
mem::transmute::<*const (), F>(pthread_setname_np)(pthread_self(), mem::transmute::<*const (), F>(pthread_setname_np)(pthread_self(),
cname.as_ptr()); cname.as_ptr());
} }
@ -281,14 +246,18 @@ pub unsafe fn set_name(name: &str) {
target_os = "bitrig", target_os = "bitrig",
target_os = "openbsd"))] target_os = "openbsd"))]
pub unsafe fn set_name(name: &str) { 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(); let cname = CString::new(name).unwrap();
pthread_set_name_np(pthread_self(), cname.as_ptr()); pthread_set_name_np(pthread_self(), cname.as_ptr());
} }
#[cfg(any(target_os = "macos", target_os = "ios"))] #[cfg(any(target_os = "macos", target_os = "ios"))]
pub unsafe fn set_name(name: &str) { 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(); let cname = CString::new(name).unwrap();
pthread_setname_np(cname.as_ptr()); pthread_setname_np(cname.as_ptr());
} }
@ -301,7 +270,42 @@ pub unsafe fn detach(native: rust_thread) {
assert_eq!(pthread_detach(native), 0); 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 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 // glibc >= 2.15 has a __pthread_get_minstack() function that returns
// PTHREAD_STACK_MIN plus however many bytes are needed for thread-local // PTHREAD_STACK_MIN plus however many bytes are needed for thread-local
@ -334,67 +338,19 @@ fn min_stack_size(_: *const libc::pthread_attr_t) -> libc::size_t {
PTHREAD_STACK_MIN PTHREAD_STACK_MIN
} }
#[cfg(any(target_os = "linux", target_os = "android"))]
extern { extern {
pub fn pthread_self() -> libc::pthread_t; #[cfg(any(target_os = "bitrig", target_os = "openbsd"))]
pub fn pthread_getattr_np(native: libc::pthread_t, fn pthread_main_np() -> libc::c_uint;
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", fn pthread_self() -> libc::pthread_t;
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(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_create(native: *mut libc::pthread_t, fn pthread_create(native: *mut libc::pthread_t,
attr: *const libc::pthread_attr_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; value: *mut libc::c_void) -> libc::c_int;
fn pthread_join(native: libc::pthread_t, fn pthread_join(native: libc::pthread_t,
value: *mut *mut libc::c_void) -> libc::c_int; value: *mut *mut libc::c_void) -> libc::c_int;
fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> 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, fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
stack_size: libc::size_t) -> libc::c_int; stack_size: libc::size_t) -> libc::c_int;
fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t, fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t,

View file

@ -10,43 +10,28 @@
use prelude::v1::*; use prelude::v1::*;
use boxed;
use cmp; use cmp;
use io; use io;
use ptr; use libc::{self, c_void};
use libc;
use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL, use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL,
LPVOID, DWORD, LPDWORD, HANDLE}; LPVOID, DWORD, LPDWORD, HANDLE};
use thunk::Thunk; use mem;
use ptr;
use sys_common::stack::RED_ZONE; use sys_common::stack::RED_ZONE;
use sys_common::thread::*; use sys_common::thread::*;
use thunk::Thunk;
use time::Duration;
pub type rust_thread = HANDLE; 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 mod guard {
pub unsafe fn main() -> uint { pub unsafe fn main() -> uint { 0 }
0 pub unsafe fn current() -> uint { 0 }
} pub unsafe fn init() {}
pub unsafe fn current() -> uint {
0
}
pub unsafe fn init() {
}
} }
pub unsafe fn create(stack: uint, p: Thunk) -> io::Result<rust_thread> { pub unsafe fn create(stack: usize, p: Thunk) -> io::Result<rust_thread> {
let raw_p = boxed::into_raw(box p); let p = box p;
let arg = raw_p as *mut libc::c_void;
// FIXME On UNIX, we guard against stack sizes that are too small but // FIXME On UNIX, we guard against stack sizes that are too small but
// that's because pthreads enforces that stacks are at least // that's because pthreads enforces that stacks are at least
// PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's // PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's
@ -58,14 +43,20 @@ pub unsafe fn create(stack: uint, p: Thunk) -> io::Result<rust_thread> {
// 20 kB red zone, that makes for a 64 kB minimum stack. // 20 kB red zone, that makes for a 64 kB minimum stack.
let stack_size = (cmp::max(stack, RED_ZONE) + 0xfffe) & (-0xfffe - 1); let stack_size = (cmp::max(stack, RED_ZONE) + 0xfffe) & (-0xfffe - 1);
let ret = CreateThread(ptr::null_mut(), stack_size as libc::size_t, 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 { return if ret as usize == 0 {
// be sure to not leak the closure
let _p: Box<Thunk> = Box::from_raw(raw_p);
Err(io::Error::last_os_error()) Err(io::Error::last_os_error())
} else { } else {
mem::forget(p); // ownership passed to CreateThread
Ok(ret) Ok(ret)
};
#[no_stack_check]
extern "system" fn thread_start(main: *mut libc::c_void) -> DWORD {
start_thread(main);
0
} }
} }
@ -92,14 +83,29 @@ pub unsafe fn yield_now() {
SwitchToThread(); 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)] #[allow(non_snake_case)]
extern "system" { extern "system" {
fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES,
dwStackSize: SIZE_T, dwStackSize: SIZE_T,
lpStartAddress: StartFn, lpStartAddress: extern "system" fn(*mut c_void) -> DWORD,
lpParameter: LPVOID, lpParameter: LPVOID,
dwCreationFlags: DWORD, dwCreationFlags: DWORD,
lpThreadId: LPDWORD) -> HANDLE; lpThreadId: LPDWORD) -> HANDLE;
fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
fn SwitchToThread() -> BOOL; fn SwitchToThread() -> BOOL;
fn Sleep(dwMilliseconds: DWORD);
} }

View file

@ -379,6 +379,19 @@ pub fn panicking() -> bool {
unwind::panicking() 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). /// Block unless or until the current thread's token is made available (may wake spuriously).
/// ///
/// See the module doc for more detail. /// 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 // NOTE: the corresponding test for stderr is in run-pass/task-stderr, due
// to the test harness apparently interfering with stderr configuration. // to the test harness apparently interfering with stderr configuration.
} }