about summary refs log tree commit diff
path: root/src/libstd/thread
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2016-02-16 23:07:09 -0800
committerAlex Crichton <alex@alexcrichton.com>2017-06-23 16:11:39 -0700
commit06540cb205545e5e7e509933af27142ca35eae17 (patch)
tree61ae222629f05fdd91587e2b58e4dda3b95c1bea /src/libstd/thread
parent622e7e6487b6fb7fdbb901720cd4214f9179ed67 (diff)
downloadrust-06540cb205545e5e7e509933af27142ca35eae17.tar.gz
rust-06540cb205545e5e7e509933af27142ca35eae17.zip
rustc: Enable #[thread_local] for Windows
I think LLVM has had support for quite some time now for this, we just never got
around to testing it out and binding it. We've had some trouble landing this in
the past I believe, but it's time to try again!

This commit flags the `#[thread_local]` attribute as being available for Windows
targets and adds an implementation of `register_dtor` in the `thread::local`
module to ensure we can destroy these keys. The same functionality is
implemented in clang via a function called `__tlregdtor` (presumably provided in
some Windows runtime somewhere), but this function unfortunately does not take a
data pointer (just a thunk) which means we can't easily call it. For now
destructors are just run in the same way the Linux fallback is implemented,
which is just keeping track via a single OS-based TLS key.
Diffstat (limited to 'src/libstd/thread')
-rw-r--r--src/libstd/thread/local.rs82
-rw-r--r--src/libstd/thread/mod.rs2
2 files changed, 80 insertions, 4 deletions
diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs
index c2c6e6cf87d..dad21473eae 100644
--- a/src/libstd/thread/local.rs
+++ b/src/libstd/thread/local.rs
@@ -334,6 +334,82 @@ impl<T: 'static> LocalKey<T> {
 }
 
 #[doc(hidden)]
+#[cfg(target_thread_local)]
+pub mod fast {
+    use cell::{Cell, UnsafeCell};
+    use fmt;
+    use mem;
+    use ptr;
+    use sys::fast_thread_local::{register_dtor, requires_move_before_drop};
+
+    pub struct Key<T> {
+        inner: UnsafeCell<Option<T>>,
+
+        // Metadata to keep track of the state of the destructor. Remember that
+        // these variables are thread-local, not global.
+        dtor_registered: Cell<bool>,
+        dtor_running: Cell<bool>,
+    }
+
+    impl<T> fmt::Debug for Key<T> {
+        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+            f.pad("Key { .. }")
+        }
+    }
+
+    unsafe impl<T> ::marker::Sync for Key<T> { }
+
+    impl<T> Key<T> {
+        pub const fn new() -> Key<T> {
+            Key {
+                inner: UnsafeCell::new(None),
+                dtor_registered: Cell::new(false),
+                dtor_running: Cell::new(false)
+            }
+        }
+
+        pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> {
+            unsafe {
+                if mem::needs_drop::<T>() && self.dtor_running.get() {
+                    return None
+                }
+                self.register_dtor();
+            }
+            Some(&self.inner)
+        }
+
+        unsafe fn register_dtor(&self) {
+            if !mem::needs_drop::<T>() || self.dtor_registered.get() {
+                return
+            }
+
+            register_dtor(self as *const _ as *mut u8,
+                          destroy_value::<T>);
+            self.dtor_registered.set(true);
+        }
+    }
+
+    unsafe extern fn destroy_value<T>(ptr: *mut u8) {
+        let ptr = ptr as *mut Key<T>;
+        // Right before we run the user destructor be sure to flag the
+        // destructor as running for this thread so calls to `get` will return
+        // `None`.
+        (*ptr).dtor_running.set(true);
+
+        // Some implementations may require us to move the value before we drop
+        // it as it could get re-initialized in-place during destruction.
+        //
+        // Hence, we use `ptr::read` on those platforms (to move to a "safe"
+        // location) instead of drop_in_place.
+        if requires_move_before_drop() {
+            ptr::read((*ptr).inner.get());
+        } else {
+            ptr::drop_in_place((*ptr).inner.get());
+        }
+    }
+}
+
+#[doc(hidden)]
 pub mod os {
     use cell::{Cell, UnsafeCell};
     use fmt;
@@ -378,8 +454,8 @@ pub mod os {
                     return Some(&(*ptr).value);
                 }
 
-                // If the lookup returned null, we haven't initialized our own local
-                // copy, so do that now.
+                // If the lookup returned null, we haven't initialized our own
+                // local copy, so do that now.
                 let ptr: Box<Value<T>> = box Value {
                     key: self,
                     value: UnsafeCell::new(None),
@@ -391,7 +467,7 @@ pub mod os {
         }
     }
 
-    pub unsafe extern fn destroy_value<T: 'static>(ptr: *mut u8) {
+    unsafe extern fn destroy_value<T: 'static>(ptr: *mut u8) {
         // The OS TLS ensures that this key contains a NULL value when this
         // destructor starts to run. We set it back to a sentinel value of 1 to
         // ensure that any future calls to `get` for this thread will return
diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs
index dda11e50380..743b7c3220a 100644
--- a/src/libstd/thread/mod.rs
+++ b/src/libstd/thread/mod.rs
@@ -172,7 +172,7 @@ pub use self::local::{LocalKey, LocalKeyState};
 
 #[unstable(feature = "libstd_thread_internals", issue = "0")]
 #[cfg(target_thread_local)]
-#[doc(hidden)] pub use sys::fast_thread_local::Key as __FastLocalKeyInner;
+#[doc(hidden)] pub use self::local::fast::Key as __FastLocalKeyInner;
 #[unstable(feature = "libstd_thread_internals", issue = "0")]
 #[doc(hidden)] pub use self::local::os::Key as __OsLocalKeyInner;