diff options
Diffstat (limited to 'src/libstd/rt')
| -rw-r--r-- | src/libstd/rt/mod.rs | 5 | ||||
| -rw-r--r-- | src/libstd/rt/thread.rs | 348 |
2 files changed, 1 insertions, 352 deletions
diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index a68a632bc37..66e7059422b 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -63,13 +63,10 @@ pub use self::util::{default_sched_threads, min_stack, running_on_valgrind}; // Reexport functionality from librustrt and other crates underneath the // standard library which work together to create the entire runtime. pub use alloc::{heap, libc_heap}; -pub use rustrt::{task, local, mutex, exclusive, stack, args, rtio}; +pub use rustrt::{task, local, mutex, exclusive, stack, args, rtio, thread}; pub use rustrt::{Stdio, Stdout, Stderr, begin_unwind, begin_unwind_fmt}; pub use rustrt::{bookkeeping, at_exit, unwind, DEFAULT_ERROR_CODE, Runtime}; -// Bindings to system threading libraries. -pub mod thread; - // Simple backtrace functionality (to print on failure) pub mod backtrace; diff --git a/src/libstd/rt/thread.rs b/src/libstd/rt/thread.rs deleted file mode 100644 index 5a077e511c0..00000000000 --- a/src/libstd/rt/thread.rs +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Native os-thread management -//! -//! This modules contains bindings necessary for managing OS-level threads. -//! These functions operate outside of the rust runtime, creating threads -//! which are not used for scheduling in any way. - -#![allow(non_camel_case_types)] -#![allow(unsigned_negate)] - -use kinds::Send; -use libc; -use mem; -use ops::Drop; -use option::{Option, Some, None}; -use owned::Box; -use uint; - -type StartFn = extern "C" fn(*libc::c_void) -> imp::rust_thread_return; - -/// This struct represents a native thread's state. This is used to join on an -/// existing thread created in the join-able state. -pub struct Thread<T> { - native: imp::rust_thread, - joined: bool, - packet: Box<Option<T>>, -} - -static DEFAULT_STACK_SIZE: uint = 1024 * 1024; - -// 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_split_stack annotation), and then we extract the main function -// and invoke it. -#[no_split_stack] -extern fn thread_start(main: *libc::c_void) -> imp::rust_thread_return { - use rt::stack; - unsafe { - stack::record_stack_bounds(0, uint::MAX); - let f: Box<proc()> = mem::transmute(main); - (*f)(); - mem::transmute(0 as imp::rust_thread_return) - } -} - -// There are two impl blocks b/c if T were specified at the top then it's just a -// pain to specify a type parameter on Thread::spawn (which doesn't need the -// type parameter). -impl Thread<()> { - - /// Starts execution of a new OS thread. - /// - /// This function will not wait for the thread to join, but a handle to the - /// thread will be returned. - /// - /// Note that the handle returned is used to acquire the return value of the - /// procedure `main`. The `join` function will wait for the thread to finish - /// and return the value that `main` generated. - /// - /// Also note that the `Thread` returned will *always* wait for the thread - /// to finish executing. This means that even if `join` is not explicitly - /// called, when the `Thread` falls out of scope its destructor will block - /// waiting for the OS thread. - pub fn start<T: Send>(main: proc():Send -> T) -> Thread<T> { - Thread::start_stack(DEFAULT_STACK_SIZE, main) - } - - /// Performs the same functionality as `start`, but specifies an explicit - /// stack size for the new thread. - pub fn start_stack<T: Send>(stack: uint, main: proc():Send -> T) -> Thread<T> { - - // We need the address of the packet to fill in to be stable so when - // `main` fills it in it's still valid, so allocate an extra box to do - // so. - let packet = box None; - let packet2: *mut Option<T> = unsafe { - *mem::transmute::<&Box<Option<T>>, **mut Option<T>>(&packet) - }; - let main = proc() unsafe { *packet2 = Some(main()); }; - let native = unsafe { imp::create(stack, box main) }; - - Thread { - native: native, - joined: false, - packet: packet, - } - } - - /// This will spawn a new thread, but it will not wait for the thread to - /// finish, nor is it possible to wait for the thread to finish. - /// - /// This corresponds to creating threads in the 'detached' state on unix - /// systems. Note that platforms may not keep the main program alive even if - /// there are detached thread still running around. - pub fn spawn(main: proc():Send) { - Thread::spawn_stack(DEFAULT_STACK_SIZE, main) - } - - /// Performs the same functionality as `spawn`, but explicitly specifies a - /// stack size for the new thread. - pub fn spawn_stack(stack: uint, main: proc():Send) { - unsafe { - let handle = imp::create(stack, box main); - imp::detach(handle); - } - } - - /// Relinquishes the CPU slot that this OS-thread is currently using, - /// allowing another thread to run for awhile. - pub fn yield_now() { - unsafe { imp::yield_now(); } - } -} - -impl<T: Send> Thread<T> { - /// Wait for this thread to finish, returning the result of the thread's - /// calculation. - pub fn join(mut self) -> T { - assert!(!self.joined); - unsafe { imp::join(self.native) }; - self.joined = true; - assert!(self.packet.is_some()); - self.packet.take_unwrap() - } -} - -#[unsafe_destructor] -impl<T: Send> Drop for Thread<T> { - fn drop(&mut self) { - // This is required for correctness. If this is not done then the thread - // would fill in a return box which no longer exists. - if !self.joined { - unsafe { imp::join(self.native) }; - } - } -} - -#[cfg(windows)] -mod imp { - use mem; - use cmp; - use kinds::Send; - use libc; - use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL, - LPVOID, DWORD, LPDWORD, HANDLE}; - use os; - use owned::Box; - use ptr; - use rt::stack::RED_ZONE; - - pub type rust_thread = HANDLE; - pub type rust_thread_return = DWORD; - - pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread { - let arg: *mut libc::c_void = mem::transmute(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::mut_null(), stack_size as libc::size_t, - super::thread_start, arg, 0, ptr::mut_null()); - - if ret as uint == 0 { - // be sure to not leak the closure - let _p: Box<proc():Send> = mem::transmute(arg); - fail!("failed to spawn native thread: {}", os::last_os_error()); - } - return ret; - } - - pub unsafe fn join(native: rust_thread) { - use libc::consts::os::extra::INFINITE; - WaitForSingleObject(native, INFINITE); - } - - pub unsafe fn detach(native: rust_thread) { - assert!(libc::CloseHandle(native) != 0); - } - - 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(); - } - - #[allow(non_snake_case_functions)] - extern "system" { - fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, - dwStackSize: SIZE_T, - lpStartAddress: super::StartFn, - lpParameter: LPVOID, - dwCreationFlags: DWORD, - lpThreadId: LPDWORD) -> HANDLE; - fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; - fn SwitchToThread() -> BOOL; - } -} - -#[cfg(unix)] -mod imp { - use cmp; - use kinds::Send; - use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN}; - use libc; - use mem; - use os; - use owned::Box; - use ptr; - use rt::stack::RED_ZONE; - - pub type rust_thread = libc::pthread_t; - pub type rust_thread_return = *u8; - - pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread { - 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); - match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) { - 0 => { - }, - 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 = os::page_size(); - let stack_size = (stack_size + page_size - 1) & (-(page_size - 1) - 1); - assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t), 0); - }, - errno => { - // This cannot really happen. - fail!("pthread_attr_setstacksize() error: {} ({})", os::last_os_error(), errno); - }, - }; - - let arg: *libc::c_void = mem::transmute(p); - let ret = pthread_create(&mut native, &attr, super::thread_start, arg); - assert_eq!(pthread_attr_destroy(&mut attr), 0); - - if ret != 0 { - // be sure to not leak the closure - let _p: Box<proc():Send> = mem::transmute(arg); - fail!("failed to spawn native thread: {}", os::last_os_error()); - } - native - } - - pub unsafe fn join(native: rust_thread) { - assert_eq!(pthread_join(native, ptr::null()), 0); - } - - pub unsafe fn detach(native: rust_thread) { - assert_eq!(pthread_detach(native), 0); - } - - pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); } - - // glibc >= 2.15 has a __pthread_get_minstack() function that returns - // PTHREAD_STACK_MIN plus however many bytes are needed for thread-local - // storage. We need that information to avoid blowing up when a small stack - // is created in an application with big thread-local storage requirements. - // See #6233 for rationale and details. - // - // Link weakly to the symbol for compatibility with older versions of glibc. - // Assumes that we've been dynamically linked to libpthread but that is - // currently always the case. Note that you need to check that the symbol - // is non-null before calling it! - #[cfg(target_os = "linux")] - fn min_stack_size(attr: *libc::pthread_attr_t) -> libc::size_t { - use ptr::RawPtr; - type F = unsafe extern "C" fn(*libc::pthread_attr_t) -> libc::size_t; - extern { - #[linkage = "extern_weak"] - static __pthread_get_minstack: *(); - } - if __pthread_get_minstack.is_null() { - PTHREAD_STACK_MIN - } else { - unsafe { mem::transmute::<*(), F>(__pthread_get_minstack)(attr) } - } - } - - // __pthread_get_minstack() is marked as weak but extern_weak linkage is - // not supported on OS X, hence this kludge... - #[cfg(not(target_os = "linux"))] - fn min_stack_size(_: *libc::pthread_attr_t) -> libc::size_t { - PTHREAD_STACK_MIN - } - - extern { - fn pthread_create(native: *mut libc::pthread_t, - attr: *libc::pthread_attr_t, - f: super::StartFn, - value: *libc::c_void) -> libc::c_int; - fn pthread_join(native: libc::pthread_t, - value: **libc::c_void) -> libc::c_int; - fn pthread_attr_init(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, - state: libc::c_int) -> libc::c_int; - fn pthread_detach(thread: libc::pthread_t) -> libc::c_int; - fn sched_yield() -> libc::c_int; - } -} - -#[cfg(test)] -mod tests { - use super::Thread; - - #[test] - fn smoke() { Thread::start(proc (){}).join(); } - - #[test] - fn data() { assert_eq!(Thread::start(proc () { 1 }).join(), 1); } - - #[test] - fn detached() { Thread::spawn(proc () {}) } - - #[test] - fn small_stacks() { - assert_eq!(42, Thread::start_stack(0, proc () 42).join()); - assert_eq!(42, Thread::start_stack(1, proc () 42).join()); - } -} - |
