Remove rt::{local, local_data, thread_local_storage}
This commit is contained in:
parent
cac133c9a8
commit
84cb6cd938
4 changed files with 176 additions and 524 deletions
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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...
|
||||
|
|
|
|||
170
src/libstd/rt/thread.rs
Normal file
170
src/libstd/rt/thread.rs
Normal file
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue