]> git.lizzy.rs Git - rust.git/commitdiff
rustc: Enable #[thread_local] for Windows
authorAlex Crichton <alex@alexcrichton.com>
Wed, 17 Feb 2016 07:07:09 +0000 (23:07 -0800)
committerAlex Crichton <alex@alexcrichton.com>
Fri, 23 Jun 2017 23:11:39 +0000 (16:11 -0700)
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.

src/librustc_back/target/x86_64_pc_windows_msvc.rs
src/libstd/sys/unix/fast_thread_local.rs
src/libstd/sys/windows/fast_thread_local.rs [new file with mode: 0644]
src/libstd/sys/windows/mod.rs
src/libstd/sys_common/thread_local.rs
src/libstd/thread/local.rs
src/libstd/thread/mod.rs

index b07031c4bf1a3de5b13ba9d196a50b1d17860c8a..7eb673d8b363cdb2c7f20fc2ea8e1cb0ebd4ea97 100644 (file)
@@ -15,6 +15,7 @@ pub fn target() -> TargetResult {
     let mut base = super::windows_msvc_base::opts();
     base.cpu = "x86-64".to_string();
     base.max_atomic_width = Some(64);
+    base.has_elf_tls = true;
 
     Ok(Target {
         llvm_target: "x86_64-pc-windows-msvc".to_string(),
index 6b3973de84c97d940a575ff1ea78561fb3954348..6cdbe5df75d5162b2d0f051fb134af80ea95f612 100644 (file)
 #![cfg(target_thread_local)]
 #![unstable(feature = "thread_local_internals", issue = "0")]
 
-use cell::{Cell, UnsafeCell};
-use fmt;
-use mem;
-use ptr;
-
-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);
-    }
-}
-
-#[cfg(any(target_os = "linux", target_os = "fuchsia"))]
-unsafe fn register_dtor_fallback(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
-    // The fallback implementation uses a vanilla OS-based TLS key to track
-    // the list of destructors that need to be run for this thread. The key
-    // then has its own destructor which runs all the other destructors.
-    //
-    // The destructor for DTORS is a little special in that it has a `while`
-    // loop to continuously drain the list of registered destructors. It
-    // *should* be the case that this loop always terminates because we
-    // provide the guarantee that a TLS key cannot be set after it is
-    // flagged for destruction.
-    use sys_common::thread_local as os;
-
-    static DTORS: os::StaticKey = os::StaticKey::new(Some(run_dtors));
-    type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>;
-    if DTORS.get().is_null() {
-        let v: Box<List> = box Vec::new();
-        DTORS.set(Box::into_raw(v) as *mut u8);
-    }
-    let list: &mut List = &mut *(DTORS.get() as *mut List);
-    list.push((t, dtor));
-
-    unsafe extern fn run_dtors(mut ptr: *mut u8) {
-        while !ptr.is_null() {
-            let list: Box<List> = Box::from_raw(ptr as *mut List);
-            for &(ptr, dtor) in list.iter() {
-                dtor(ptr);
-            }
-            ptr = DTORS.get();
-            DTORS.set(ptr::null_mut());
-        }
-    }
-}
-
 // Since what appears to be glibc 2.18 this symbol has been shipped which
 // GCC and clang both use to invoke destructors in thread_local globals, so
 // let's do the same!
@@ -107,9 +21,10 @@ unsafe fn register_dtor_fallback(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
 //
 // Due to rust-lang/rust#18804, make sure this is not generic!
 #[cfg(target_os = "linux")]
-unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
-    use mem;
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
     use libc;
+    use mem;
+    use sys_common::thread_local::register_dtor_fallback;
 
     extern {
         #[linkage = "extern_weak"]
@@ -132,7 +47,7 @@ unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
 // The disassembly of thread_local globals in C++ (at least produced by
 // clang) will have this show up in the output.
 #[cfg(target_os = "macos")]
-unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
     extern {
         fn _tlv_atexit(dtor: unsafe extern fn(*mut u8),
                        arg: *mut u8);
@@ -143,17 +58,9 @@ fn _tlv_atexit(dtor: unsafe extern fn(*mut u8),
 // Just use the thread_local fallback implementation, at least until there's
 // a more direct implementation.
 #[cfg(target_os = "fuchsia")]
-unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
-    register_dtor_fallback(t, dtor);
-}
-
-pub 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);
+pub use sys_common::thread_local::register_dtor_fallback as register_dtor;
 
+pub fn requires_move_before_drop() -> bool {
     // The macOS implementation of TLS apparently had an odd aspect to it
     // where the pointer we have may be overwritten while this destructor
     // is running. Specifically if a TLS destructor re-accesses TLS it may
@@ -166,9 +73,5 @@ unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
     //
     // Hence, we use `ptr::read` on macOS (to move to a "safe" location)
     // instead of drop_in_place.
-    if cfg!(target_os = "macos") {
-        ptr::read((*ptr).inner.get());
-    } else {
-        ptr::drop_in_place((*ptr).inner.get());
-    }
+    cfg!(target_os = "macos")
 }
diff --git a/src/libstd/sys/windows/fast_thread_local.rs b/src/libstd/sys/windows/fast_thread_local.rs
new file mode 100644 (file)
index 0000000..9fee9bd
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2014-2015 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.
+
+#![unstable(feature = "thread_local_internals", issue = "0")]
+#![cfg(target_thread_local)]
+
+pub use sys_common::thread_local::register_dtor_fallback as register_dtor;
+
+pub fn requires_move_before_drop() -> bool {
+    false
+}
index 4424c6c6136c5ff94521be912b206bdb0ed482c0..840e7fdfc9b26942c56e4e5d11d8c038707072ad 100644 (file)
@@ -26,6 +26,7 @@
 pub mod dynamic_lib;
 pub mod env;
 pub mod ext;
+pub mod fast_thread_local;
 pub mod fs;
 pub mod handle;
 pub mod memchr;
index 0ade90e64c307e66e98edce22e3923ae9a74f1ef..7592eda16f061c9ef058f1f9678b5664aa86874b 100644 (file)
@@ -58,8 +58,8 @@
 #![unstable(feature = "thread_local_internals", issue = "0")]
 #![allow(dead_code)] // sys isn't exported yet
 
+use ptr;
 use sync::atomic::{self, AtomicUsize, Ordering};
-
 use sys::thread_local as imp;
 use sys_common::mutex::Mutex;
 
@@ -238,6 +238,39 @@ fn drop(&mut self) {
     }
 }
 
+pub unsafe fn register_dtor_fallback(t: *mut u8,
+                                     dtor: unsafe extern fn(*mut u8)) {
+    // The fallback implementation uses a vanilla OS-based TLS key to track
+    // the list of destructors that need to be run for this thread. The key
+    // then has its own destructor which runs all the other destructors.
+    //
+    // The destructor for DTORS is a little special in that it has a `while`
+    // loop to continuously drain the list of registered destructors. It
+    // *should* be the case that this loop always terminates because we
+    // provide the guarantee that a TLS key cannot be set after it is
+    // flagged for destruction.
+
+    static DTORS: StaticKey = StaticKey::new(Some(run_dtors));
+    type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>;
+    if DTORS.get().is_null() {
+        let v: Box<List> = box Vec::new();
+        DTORS.set(Box::into_raw(v) as *mut u8);
+    }
+    let list: &mut List = &mut *(DTORS.get() as *mut List);
+    list.push((t, dtor));
+
+    unsafe extern fn run_dtors(mut ptr: *mut u8) {
+        while !ptr.is_null() {
+            let list: Box<List> = Box::from_raw(ptr as *mut List);
+            for &(ptr, dtor) in list.iter() {
+                dtor(ptr);
+            }
+            ptr = DTORS.get();
+            DTORS.set(ptr::null_mut());
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::{Key, StaticKey};
index c2c6e6cf87dff4ad71fcde7e8f721f342ef3d37c..dad21473eae1e043f61c45aa96efc79b6c450e1a 100644 (file)
@@ -333,6 +333,82 @@ pub fn state(&'static self) -> LocalKeyState {
     }
 }
 
+#[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};
@@ -378,8 +454,8 @@ pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> {
                     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 fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> {
         }
     }
 
-    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
index dda11e50380f516f6a031ebd18f6411c5f68ee80..743b7c3220a8995457c48ba571f7a5d96b530fb9 100644 (file)
 
 #[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;