]> git.lizzy.rs Git - rust.git/commitdiff
Remove rt::{local, local_data, thread_local_storage}
authorAaron Turon <aturon@mozilla.com>
Tue, 25 Nov 2014 01:59:15 +0000 (17:59 -0800)
committerAaron Turon <aturon@mozilla.com>
Fri, 19 Dec 2014 07:31:51 +0000 (23:31 -0800)
src/libstd/rt/local_ptr.rs [deleted file]
src/libstd/rt/mod.rs
src/libstd/rt/thread.rs [new file with mode: 0644]
src/libstd/rt/thread_local_storage.rs [deleted file]

diff --git a/src/libstd/rt/local_ptr.rs b/src/libstd/rt/local_ptr.rs
deleted file mode 100644 (file)
index a87bc3d..0000000
+++ /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())
-        }
-    }
-}
index eff80b5ab2f7bc50f5617019527d307c7cf839dd..8ef7ac43a30efe8d4c5a2f59dd5c26f56f7ea26a 100644 (file)
 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 (file)
index 0000000..9eb7048
--- /dev/null
@@ -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 (file)
index ee6ad8a..0000000
+++ /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);
-        }
-    }
-}