diff options
| author | Aaron Turon <aturon@mozilla.com> | 2014-11-24 17:59:15 -0800 |
|---|---|---|
| committer | Aaron Turon <aturon@mozilla.com> | 2014-12-18 23:31:51 -0800 |
| commit | 84cb6cd9386ab01ba59f8ed98d698f9af74e65fe (patch) | |
| tree | ec382494b3e6e7c678540aaecb966a3c8d19ae4f /src | |
| parent | cac133c9a86a4687755aeb44908e3fbb2bb35fc2 (diff) | |
| download | rust-84cb6cd9386ab01ba59f8ed98d698f9af74e65fe.tar.gz rust-84cb6cd9386ab01ba59f8ed98d698f9af74e65fe.zip | |
Remove rt::{local, local_data, thread_local_storage}
Diffstat (limited to 'src')
| -rw-r--r-- | src/libstd/rt/local_ptr.rs | 404 | ||||
| -rw-r--r-- | src/libstd/rt/mod.rs | 11 | ||||
| -rw-r--r-- | src/libstd/rt/thread.rs | 170 | ||||
| -rw-r--r-- | src/libstd/rt/thread_local_storage.rs | 115 |
4 files changed, 176 insertions, 524 deletions
diff --git a/src/libstd/rt/local_ptr.rs b/src/libstd/rt/local_ptr.rs deleted file mode 100644 index a87bc3d2766..00000000000 --- a/src/libstd/rt/local_ptr.rs +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright 2013 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. - -//! Access to a single thread-local pointer. -//! -//! The runtime will use this for storing Box<Task>. -//! -//! FIXME: Add runtime checks for usage of inconsistent pointer types. -//! and for overwriting an existing pointer. - -#![allow(dead_code)] - -use core::prelude::*; - -use mem; -use boxed::Box; - -#[cfg(any(windows, // mingw-w32 doesn't like thread_local things - target_os = "android", // see #10686 - target_os = "ios"))] -pub use self::native::{init, cleanup, put, take, try_take, unsafe_take, exists, - unsafe_borrow, try_unsafe_borrow}; - -#[cfg(not(any(windows, target_os = "android", target_os = "ios")))] -pub use self::compiled::{init, cleanup, put, take, try_take, unsafe_take, exists, - unsafe_borrow, try_unsafe_borrow}; - -/// Encapsulates a borrowed value. When this value goes out of scope, the -/// pointer is returned. -pub struct Borrowed<T> { - val: *const (), -} - -#[unsafe_destructor] -impl<T> Drop for Borrowed<T> { - fn drop(&mut self) { - unsafe { - if self.val.is_null() { - rtabort!("Aiee, returning null borrowed object!"); - } - let val: Box<T> = mem::transmute(self.val); - put::<T>(val); - rtassert!(exists()); - } - } -} - -impl<T> Deref<T> for Borrowed<T> { - fn deref<'a>(&'a self) -> &'a T { - unsafe { &*(self.val as *const T) } - } -} - -impl<T> DerefMut<T> for Borrowed<T> { - fn deref_mut<'a>(&'a mut self) -> &'a mut T { - unsafe { &mut *(self.val as *mut T) } - } -} - -/// Borrow the thread-local value from thread-local storage. -/// While the value is borrowed it is not available in TLS. -/// -/// # Safety note -/// -/// Does not validate the pointer type. -#[inline] -pub unsafe fn borrow<T>() -> Borrowed<T> { - let val: *const () = mem::transmute(take::<T>()); - Borrowed { - val: val, - } -} - -/// Compiled implementation of accessing the runtime local pointer. This is -/// implemented using LLVM's thread_local attribute which isn't necessarily -/// working on all platforms. This implementation is faster, however, so we use -/// it wherever possible. -#[cfg(not(any(windows, target_os = "android", target_os = "ios")))] -pub mod compiled { - use core::prelude::*; - - use boxed::Box; - use mem; - - #[cfg(test)] - pub use realstd::rt::shouldnt_be_public::RT_TLS_PTR; - - #[cfg(not(test))] - #[thread_local] - pub static mut RT_TLS_PTR: *mut u8 = 0 as *mut u8; - - pub fn init() {} - - pub unsafe fn cleanup() {} - - // Rationale for all of these functions being inline(never) - // - // The #[thread_local] annotation gets propagated all the way through to - // LLVM, meaning the global is specially treated by LLVM to lower it to an - // efficient sequence of instructions. This also involves dealing with fun - // stuff in object files and whatnot. Regardless, it turns out this causes - // trouble with green threads and lots of optimizations turned on. The - // following case study was done on Linux x86_64, but I would imagine that - // other platforms are similar. - // - // On Linux, the instruction sequence for loading the tls pointer global - // looks like: - // - // mov %fs:0x0, %rax - // mov -0x8(%rax), %rbx - // - // This code leads me to believe that (%fs:0x0) is a table, and then the - // table contains the TLS values for the process. Hence, the slot at offset - // -0x8 is the task TLS pointer. This leads us to the conclusion that this - // table is the actual thread local part of each thread. The kernel sets up - // the fs segment selector to point at the right region of memory for each - // thread. - // - // Optimizations lead me to believe that this code is lowered to these - // instructions in the LLVM codegen passes, because you'll see code like - // this when everything is optimized: - // - // mov %fs:0x0, %r14 - // mov -0x8(%r14), %rbx - // // do something with %rbx, the rust Task pointer - // - // ... // <- do more things - // - // mov -0x8(%r14), %rbx - // // do something else with %rbx - // - // Note that the optimization done here is that the first load is not - // duplicated during the lower instructions. This means that the %fs:0x0 - // memory location is only dereferenced once. - // - // Normally, this is actually a good thing! With green threads, however, - // it's very possible for the code labeled "do more things" to context - // switch to another thread. If this happens, then we *must* re-load %fs:0x0 - // because it's changed (we're on a different thread). If we don't re-load - // the table location, then we'll be reading the original thread's TLS - // values, not our thread's TLS values. - // - // Hence, we never inline these functions. By never inlining, we're - // guaranteed that loading the table is a local decision which is forced to - // *always* happen. - - /// Give a pointer to thread-local storage. - /// - /// # Safety note - /// - /// Does not validate the pointer type. - #[inline(never)] // see comments above - pub unsafe fn put<T>(sched: Box<T>) { - RT_TLS_PTR = mem::transmute(sched) - } - - /// Take ownership of a pointer from thread-local storage. - /// - /// # Safety note - /// - /// Does not validate the pointer type. - #[inline(never)] // see comments above - pub unsafe fn take<T>() -> Box<T> { - let ptr = RT_TLS_PTR; - rtassert!(!ptr.is_null()); - let ptr: Box<T> = mem::transmute(ptr); - // can't use `as`, due to type not matching with `cfg(test)` - RT_TLS_PTR = mem::transmute(0u); - ptr - } - - /// Optionally take ownership of a pointer from thread-local storage. - /// - /// # Safety note - /// - /// Does not validate the pointer type. - #[inline(never)] // see comments above - pub unsafe fn try_take<T>() -> Option<Box<T>> { - let ptr = RT_TLS_PTR; - if ptr.is_null() { - None - } else { - let ptr: Box<T> = mem::transmute(ptr); - // can't use `as`, due to type not matching with `cfg(test)` - RT_TLS_PTR = mem::transmute(0u); - Some(ptr) - } - } - - /// Take ownership of a pointer from thread-local storage. - /// - /// # Safety note - /// - /// Does not validate the pointer type. - /// Leaves the old pointer in TLS for speed. - #[inline(never)] // see comments above - pub unsafe fn unsafe_take<T>() -> Box<T> { - mem::transmute(RT_TLS_PTR) - } - - /// Check whether there is a thread-local pointer installed. - #[inline(never)] // see comments above - pub fn exists() -> bool { - unsafe { - RT_TLS_PTR.is_not_null() - } - } - - #[inline(never)] // see comments above - pub unsafe fn unsafe_borrow<T>() -> *mut T { - if RT_TLS_PTR.is_null() { - rtabort!("thread-local pointer is null. bogus!"); - } - RT_TLS_PTR as *mut T - } - - #[inline(never)] // see comments above - pub unsafe fn try_unsafe_borrow<T>() -> Option<*mut T> { - if RT_TLS_PTR.is_null() { - None - } else { - Some(RT_TLS_PTR as *mut T) - } - } -} - -/// Native implementation of having the runtime thread-local pointer. This -/// implementation uses the `thread_local_storage` module to provide a -/// thread-local value. -pub mod native { - use core::prelude::*; - - use boxed::Box; - use mem; - use ptr; - use rt::thread_local_storage as tls; - - static mut RT_TLS_KEY: tls::Key = -1; - - /// Initialize the TLS key. Other ops will fail if this isn't executed - /// first. - pub fn init() { - unsafe { - tls::create(&mut RT_TLS_KEY); - } - } - - pub unsafe fn cleanup() { - rtassert!(RT_TLS_KEY != -1); - tls::destroy(RT_TLS_KEY); - } - - /// Give a pointer to thread-local storage. - /// - /// # Safety note - /// - /// Does not validate the pointer type. - #[inline] - pub unsafe fn put<T>(sched: Box<T>) { - let key = tls_key(); - let void_ptr: *mut u8 = mem::transmute(sched); - tls::set(key, void_ptr); - } - - /// Take ownership of a pointer from thread-local storage. - /// - /// # Safety note - /// - /// Does not validate the pointer type. - #[inline] - pub unsafe fn take<T>() -> Box<T> { - let key = tls_key(); - let void_ptr: *mut u8 = tls::get(key); - if void_ptr.is_null() { - rtabort!("thread-local pointer is null. bogus!"); - } - let ptr: Box<T> = mem::transmute(void_ptr); - tls::set(key, ptr::null_mut()); - return ptr; - } - - /// Optionally take ownership of a pointer from thread-local storage. - /// - /// # Safety note - /// - /// Does not validate the pointer type. - #[inline] - pub unsafe fn try_take<T>() -> Option<Box<T>> { - match maybe_tls_key() { - Some(key) => { - let void_ptr: *mut u8 = tls::get(key); - if void_ptr.is_null() { - None - } else { - let ptr: Box<T> = mem::transmute(void_ptr); - tls::set(key, ptr::null_mut()); - Some(ptr) - } - } - None => None - } - } - - /// Take ownership of a pointer from thread-local storage. - /// - /// # Safety note - /// - /// Does not validate the pointer type. - /// Leaves the old pointer in TLS for speed. - #[inline] - pub unsafe fn unsafe_take<T>() -> Box<T> { - let key = tls_key(); - let void_ptr: *mut u8 = tls::get(key); - if void_ptr.is_null() { - rtabort!("thread-local pointer is null. bogus!"); - } - let ptr: Box<T> = mem::transmute(void_ptr); - return ptr; - } - - /// Check whether there is a thread-local pointer installed. - pub fn exists() -> bool { - unsafe { - match maybe_tls_key() { - Some(key) => tls::get(key).is_not_null(), - None => false - } - } - } - - /// Borrow a mutable reference to the thread-local value - /// - /// # Safety Note - /// - /// Because this leaves the value in thread-local storage it is possible - /// For the Scheduler pointer to be aliased - pub unsafe fn unsafe_borrow<T>() -> *mut T { - let key = tls_key(); - let void_ptr = tls::get(key); - if void_ptr.is_null() { - rtabort!("thread-local pointer is null. bogus!"); - } - void_ptr as *mut T - } - - pub unsafe fn try_unsafe_borrow<T>() -> Option<*mut T> { - match maybe_tls_key() { - Some(key) => { - let void_ptr = tls::get(key); - if void_ptr.is_null() { - None - } else { - Some(void_ptr as *mut T) - } - } - None => None - } - } - - #[inline] - fn tls_key() -> tls::Key { - match maybe_tls_key() { - Some(key) => key, - None => rtabort!("runtime tls key not initialized") - } - } - - #[inline] - #[cfg(not(test))] - pub fn maybe_tls_key() -> Option<tls::Key> { - unsafe { - // NB: This is a little racy because, while the key is - // initialized under a mutex and it's assumed to be initialized - // in the Scheduler ctor by any thread that needs to use it, - // we are not accessing the key under a mutex. Threads that - // are not using the new Scheduler but still *want to check* - // whether they are running under a new Scheduler may see a 0 - // value here that is in the process of being initialized in - // another thread. I think this is fine since the only action - // they could take if it was initialized would be to check the - // thread-local value and see that it's not set. - if RT_TLS_KEY != -1 { - return Some(RT_TLS_KEY); - } else { - return None; - } - } - } - - #[inline] #[cfg(test)] - pub fn maybe_tls_key() -> Option<tls::Key> { - use rt; - unsafe { - mem::transmute(::realstd::rt::shouldnt_be_public::maybe_tls_key()) - } - } -} diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index eff80b5ab2f..8ef7ac43a30 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -75,13 +75,15 @@ pub mod mutex; pub mod thread; pub mod exclusive; pub mod util; +<<<<<<< HEAD +======= +pub mod task; +>>>>>>> Remove rt::{local, local_data, thread_local_storage} pub mod unwind; mod args; mod at_exit_imp; mod libunwind; -mod local_ptr; -mod thread_local_storage; /// The default error code of the rust runtime if the main task panics instead /// of exiting cleanly. @@ -98,8 +100,7 @@ pub fn init(argc: int, argv: *const *const u8) { // Need to propagate the unsafety to `start`. unsafe { args::init(argc, argv); - sys::thread::guard::init(); - sys::stack_overflow::init(); + thread::init(); unwind::register(failure::on_fail); } } @@ -203,7 +204,7 @@ pub fn at_exit(f: proc():Send) { /// undefined behavior. pub unsafe fn cleanup() { args::cleanup(); - sys::stack_overflow::cleanup(); + thread::cleanup(); } // FIXME: these probably shouldn't be public... diff --git a/src/libstd/rt/thread.rs b/src/libstd/rt/thread.rs new file mode 100644 index 00000000000..9eb7048f1e6 --- /dev/null +++ b/src/libstd/rt/thread.rs @@ -0,0 +1,170 @@ +// 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)] + +use core::prelude::*; + +use boxed::Box; +use mem; +use sys::stack_overflow; +use sys::thread as imp; + +pub unsafe fn init() { + imp::guard::init(); + stack_overflow::init(); +} + +pub unsafe fn cleanup() { + stack_overflow::cleanup(); +} + +/// 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; + +/// Returns the last writable byte of the main thread's stack next to the guard +/// page. Must be called from the main thread. +pub fn main_guard_page() -> uint { + unsafe { + imp::guard::main() + } +} + +/// Returns the last writable byte of the current thread's stack next to the +/// guard page. Must not be called from the main thread. +pub fn current_guard_page() -> uint { + unsafe { + imp::guard::current() + } +} + +// 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>>, *const *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(test)] +mod tests { + use super::Thread; + + #[test] + fn smoke() { Thread::start(proc (){}).join(); } + + #[test] + fn data() { assert_eq!(Thread::start(proc () { 1i }).join(), 1); } + + #[test] + fn detached() { Thread::spawn(proc () {}) } + + #[test] + fn small_stacks() { + assert_eq!(42i, Thread::start_stack(0, proc () 42i).join()); + assert_eq!(42i, Thread::start_stack(1, proc () 42i).join()); + } +} diff --git a/src/libstd/rt/thread_local_storage.rs b/src/libstd/rt/thread_local_storage.rs deleted file mode 100644 index ee6ad8a4e08..00000000000 --- a/src/libstd/rt/thread_local_storage.rs +++ /dev/null @@ -1,115 +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. - -#![allow(dead_code)] - -#[cfg(unix)] use libc::c_int; -#[cfg(unix)] use ptr::null; -#[cfg(windows)] use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL}; - -#[cfg(unix)] -pub type Key = pthread_key_t; - -#[cfg(unix)] -pub unsafe fn create(key: &mut Key) { - assert!(pthread_key_create(key, null()) == 0); -} - -#[cfg(unix)] -pub unsafe fn set(key: Key, value: *mut u8) { - assert!(pthread_setspecific(key, value) == 0); -} - -#[cfg(unix)] -pub unsafe fn get(key: Key) -> *mut u8 { - pthread_getspecific(key) -} - -#[cfg(unix)] -pub unsafe fn destroy(key: Key) { - assert!(pthread_key_delete(key) == 0); -} - -#[cfg(target_os = "macos")] -#[allow(non_camel_case_types)] // foreign type -type pthread_key_t = ::libc::c_ulong; - -#[cfg(any(target_os="linux", - target_os="freebsd", - target_os="dragonfly", - target_os="android", - target_os = "ios"))] -#[allow(non_camel_case_types)] // foreign type -type pthread_key_t = ::libc::c_uint; - -#[cfg(unix)] -extern { - fn pthread_key_create(key: *mut pthread_key_t, dtor: *const u8) -> c_int; - fn pthread_key_delete(key: pthread_key_t) -> c_int; - fn pthread_getspecific(key: pthread_key_t) -> *mut u8; - fn pthread_setspecific(key: pthread_key_t, value: *mut u8) -> c_int; -} - -#[cfg(windows)] -pub type Key = DWORD; - -#[cfg(windows)] -pub unsafe fn create(key: &mut Key) { - static TLS_OUT_OF_INDEXES: DWORD = 0xFFFFFFFF; - *key = TlsAlloc(); - assert!(*key != TLS_OUT_OF_INDEXES); -} - -#[cfg(windows)] -pub unsafe fn set(key: Key, value: *mut u8) { - assert!(0 != TlsSetValue(key, value as *mut ::libc::c_void)) -} - -#[cfg(windows)] -pub unsafe fn get(key: Key) -> *mut u8 { - TlsGetValue(key) as *mut u8 -} - -#[cfg(windows)] -pub unsafe fn destroy(key: Key) { - assert!(TlsFree(key) != 0); -} - -#[cfg(windows)] -#[allow(non_snake_case)] -extern "system" { - fn TlsAlloc() -> DWORD; - fn TlsFree(dwTlsIndex: DWORD) -> BOOL; - fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID; - fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL; -} - -#[cfg(test)] -mod test { - use prelude::*; - use super::*; - - #[test] - fn tls_smoke_test() { - use mem::transmute; - unsafe { - let mut key = 0; - let value = box 20i; - create(&mut key); - set(key, transmute(value)); - let value: Box<int> = transmute(get(key)); - assert_eq!(value, box 20i); - let value = box 30i; - set(key, transmute(value)); - let value: Box<int> = transmute(get(key)); - assert_eq!(value, box 30i); - } - } -} |
