Skip to content

Commit 06540cb

Browse files
committedJun 23, 2017
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.
1 parent 622e7e6 commit 06540cb

File tree

7 files changed

+141
-109
lines changed

7 files changed

+141
-109
lines changed
 

‎src/librustc_back/target/x86_64_pc_windows_msvc.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub fn target() -> TargetResult {
1515
let mut base = super::windows_msvc_base::opts();
1616
base.cpu = "x86-64".to_string();
1717
base.max_atomic_width = Some(64);
18+
base.has_elf_tls = true;
1819

1920
Ok(Target {
2021
llvm_target: "x86_64-pc-windows-msvc".to_string(),

‎src/libstd/sys/unix/fast_thread_local.rs

+7-104
Original file line numberDiff line numberDiff line change
@@ -11,92 +11,6 @@
1111
#![cfg(target_thread_local)]
1212
#![unstable(feature = "thread_local_internals", issue = "0")]
1313

14-
use cell::{Cell, UnsafeCell};
15-
use fmt;
16-
use mem;
17-
use ptr;
18-
19-
pub struct Key<T> {
20-
inner: UnsafeCell<Option<T>>,
21-
22-
// Metadata to keep track of the state of the destructor. Remember that
23-
// these variables are thread-local, not global.
24-
dtor_registered: Cell<bool>,
25-
dtor_running: Cell<bool>,
26-
}
27-
28-
impl<T> fmt::Debug for Key<T> {
29-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30-
f.pad("Key { .. }")
31-
}
32-
}
33-
34-
unsafe impl<T> ::marker::Sync for Key<T> { }
35-
36-
impl<T> Key<T> {
37-
pub const fn new() -> Key<T> {
38-
Key {
39-
inner: UnsafeCell::new(None),
40-
dtor_registered: Cell::new(false),
41-
dtor_running: Cell::new(false)
42-
}
43-
}
44-
45-
pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> {
46-
unsafe {
47-
if mem::needs_drop::<T>() && self.dtor_running.get() {
48-
return None
49-
}
50-
self.register_dtor();
51-
}
52-
Some(&self.inner)
53-
}
54-
55-
unsafe fn register_dtor(&self) {
56-
if !mem::needs_drop::<T>() || self.dtor_registered.get() {
57-
return
58-
}
59-
60-
register_dtor(self as *const _ as *mut u8,
61-
destroy_value::<T>);
62-
self.dtor_registered.set(true);
63-
}
64-
}
65-
66-
#[cfg(any(target_os = "linux", target_os = "fuchsia"))]
67-
unsafe fn register_dtor_fallback(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
68-
// The fallback implementation uses a vanilla OS-based TLS key to track
69-
// the list of destructors that need to be run for this thread. The key
70-
// then has its own destructor which runs all the other destructors.
71-
//
72-
// The destructor for DTORS is a little special in that it has a `while`
73-
// loop to continuously drain the list of registered destructors. It
74-
// *should* be the case that this loop always terminates because we
75-
// provide the guarantee that a TLS key cannot be set after it is
76-
// flagged for destruction.
77-
use sys_common::thread_local as os;
78-
79-
static DTORS: os::StaticKey = os::StaticKey::new(Some(run_dtors));
80-
type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>;
81-
if DTORS.get().is_null() {
82-
let v: Box<List> = box Vec::new();
83-
DTORS.set(Box::into_raw(v) as *mut u8);
84-
}
85-
let list: &mut List = &mut *(DTORS.get() as *mut List);
86-
list.push((t, dtor));
87-
88-
unsafe extern fn run_dtors(mut ptr: *mut u8) {
89-
while !ptr.is_null() {
90-
let list: Box<List> = Box::from_raw(ptr as *mut List);
91-
for &(ptr, dtor) in list.iter() {
92-
dtor(ptr);
93-
}
94-
ptr = DTORS.get();
95-
DTORS.set(ptr::null_mut());
96-
}
97-
}
98-
}
99-
10014
// Since what appears to be glibc 2.18 this symbol has been shipped which
10115
// GCC and clang both use to invoke destructors in thread_local globals, so
10216
// let's do the same!
@@ -107,9 +21,10 @@ unsafe fn register_dtor_fallback(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
10721
//
10822
// Due to rust-lang/rust#18804, make sure this is not generic!
10923
#[cfg(target_os = "linux")]
110-
unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
111-
use mem;
24+
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
11225
use libc;
26+
use mem;
27+
use sys_common::thread_local::register_dtor_fallback;
11328

11429
extern {
11530
#[linkage = "extern_weak"]
@@ -132,7 +47,7 @@ unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
13247
// The disassembly of thread_local globals in C++ (at least produced by
13348
// clang) will have this show up in the output.
13449
#[cfg(target_os = "macos")]
135-
unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
50+
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
13651
extern {
13752
fn _tlv_atexit(dtor: unsafe extern fn(*mut u8),
13853
arg: *mut u8);
@@ -143,17 +58,9 @@ unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
14358
// Just use the thread_local fallback implementation, at least until there's
14459
// a more direct implementation.
14560
#[cfg(target_os = "fuchsia")]
146-
unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
147-
register_dtor_fallback(t, dtor);
148-
}
149-
150-
pub unsafe extern fn destroy_value<T>(ptr: *mut u8) {
151-
let ptr = ptr as *mut Key<T>;
152-
// Right before we run the user destructor be sure to flag the
153-
// destructor as running for this thread so calls to `get` will return
154-
// `None`.
155-
(*ptr).dtor_running.set(true);
61+
pub use sys_common::thread_local::register_dtor_fallback as register_dtor;
15662

63+
pub fn requires_move_before_drop() -> bool {
15764
// The macOS implementation of TLS apparently had an odd aspect to it
15865
// where the pointer we have may be overwritten while this destructor
15966
// is running. Specifically if a TLS destructor re-accesses TLS it may
@@ -166,9 +73,5 @@ pub unsafe extern fn destroy_value<T>(ptr: *mut u8) {
16673
//
16774
// Hence, we use `ptr::read` on macOS (to move to a "safe" location)
16875
// instead of drop_in_place.
169-
if cfg!(target_os = "macos") {
170-
ptr::read((*ptr).inner.get());
171-
} else {
172-
ptr::drop_in_place((*ptr).inner.get());
173-
}
76+
cfg!(target_os = "macos")
17477
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![unstable(feature = "thread_local_internals", issue = "0")]
12+
#![cfg(target_thread_local)]
13+
14+
pub use sys_common::thread_local::register_dtor_fallback as register_dtor;
15+
16+
pub fn requires_move_before_drop() -> bool {
17+
false
18+
}

‎src/libstd/sys/windows/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub mod condvar;
2626
pub mod dynamic_lib;
2727
pub mod env;
2828
pub mod ext;
29+
pub mod fast_thread_local;
2930
pub mod fs;
3031
pub mod handle;
3132
pub mod memchr;

‎src/libstd/sys_common/thread_local.rs

+34-1
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@
5858
#![unstable(feature = "thread_local_internals", issue = "0")]
5959
#![allow(dead_code)] // sys isn't exported yet
6060

61+
use ptr;
6162
use sync::atomic::{self, AtomicUsize, Ordering};
62-
6363
use sys::thread_local as imp;
6464
use sys_common::mutex::Mutex;
6565

@@ -238,6 +238,39 @@ impl Drop for Key {
238238
}
239239
}
240240

241+
pub unsafe fn register_dtor_fallback(t: *mut u8,
242+
dtor: unsafe extern fn(*mut u8)) {
243+
// The fallback implementation uses a vanilla OS-based TLS key to track
244+
// the list of destructors that need to be run for this thread. The key
245+
// then has its own destructor which runs all the other destructors.
246+
//
247+
// The destructor for DTORS is a little special in that it has a `while`
248+
// loop to continuously drain the list of registered destructors. It
249+
// *should* be the case that this loop always terminates because we
250+
// provide the guarantee that a TLS key cannot be set after it is
251+
// flagged for destruction.
252+
253+
static DTORS: StaticKey = StaticKey::new(Some(run_dtors));
254+
type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>;
255+
if DTORS.get().is_null() {
256+
let v: Box<List> = box Vec::new();
257+
DTORS.set(Box::into_raw(v) as *mut u8);
258+
}
259+
let list: &mut List = &mut *(DTORS.get() as *mut List);
260+
list.push((t, dtor));
261+
262+
unsafe extern fn run_dtors(mut ptr: *mut u8) {
263+
while !ptr.is_null() {
264+
let list: Box<List> = Box::from_raw(ptr as *mut List);
265+
for &(ptr, dtor) in list.iter() {
266+
dtor(ptr);
267+
}
268+
ptr = DTORS.get();
269+
DTORS.set(ptr::null_mut());
270+
}
271+
}
272+
}
273+
241274
#[cfg(test)]
242275
mod tests {
243276
use super::{Key, StaticKey};

‎src/libstd/thread/local.rs

+79-3
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,82 @@ impl<T: 'static> LocalKey<T> {
333333
}
334334
}
335335

336+
#[doc(hidden)]
337+
#[cfg(target_thread_local)]
338+
pub mod fast {
339+
use cell::{Cell, UnsafeCell};
340+
use fmt;
341+
use mem;
342+
use ptr;
343+
use sys::fast_thread_local::{register_dtor, requires_move_before_drop};
344+
345+
pub struct Key<T> {
346+
inner: UnsafeCell<Option<T>>,
347+
348+
// Metadata to keep track of the state of the destructor. Remember that
349+
// these variables are thread-local, not global.
350+
dtor_registered: Cell<bool>,
351+
dtor_running: Cell<bool>,
352+
}
353+
354+
impl<T> fmt::Debug for Key<T> {
355+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
356+
f.pad("Key { .. }")
357+
}
358+
}
359+
360+
unsafe impl<T> ::marker::Sync for Key<T> { }
361+
362+
impl<T> Key<T> {
363+
pub const fn new() -> Key<T> {
364+
Key {
365+
inner: UnsafeCell::new(None),
366+
dtor_registered: Cell::new(false),
367+
dtor_running: Cell::new(false)
368+
}
369+
}
370+
371+
pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> {
372+
unsafe {
373+
if mem::needs_drop::<T>() && self.dtor_running.get() {
374+
return None
375+
}
376+
self.register_dtor();
377+
}
378+
Some(&self.inner)
379+
}
380+
381+
unsafe fn register_dtor(&self) {
382+
if !mem::needs_drop::<T>() || self.dtor_registered.get() {
383+
return
384+
}
385+
386+
register_dtor(self as *const _ as *mut u8,
387+
destroy_value::<T>);
388+
self.dtor_registered.set(true);
389+
}
390+
}
391+
392+
unsafe extern fn destroy_value<T>(ptr: *mut u8) {
393+
let ptr = ptr as *mut Key<T>;
394+
// Right before we run the user destructor be sure to flag the
395+
// destructor as running for this thread so calls to `get` will return
396+
// `None`.
397+
(*ptr).dtor_running.set(true);
398+
399+
// Some implementations may require us to move the value before we drop
400+
// it as it could get re-initialized in-place during destruction.
401+
//
402+
// Hence, we use `ptr::read` on those platforms (to move to a "safe"
403+
// location) instead of drop_in_place.
404+
if requires_move_before_drop() {
405+
ptr::read((*ptr).inner.get());
406+
} else {
407+
ptr::drop_in_place((*ptr).inner.get());
408+
}
409+
}
410+
}
411+
336412
#[doc(hidden)]
337413
pub mod os {
338414
use cell::{Cell, UnsafeCell};
@@ -378,8 +454,8 @@ pub mod os {
378454
return Some(&(*ptr).value);
379455
}
380456

381-
// If the lookup returned null, we haven't initialized our own local
382-
// copy, so do that now.
457+
// If the lookup returned null, we haven't initialized our own
458+
// local copy, so do that now.
383459
let ptr: Box<Value<T>> = box Value {
384460
key: self,
385461
value: UnsafeCell::new(None),
@@ -391,7 +467,7 @@ pub mod os {
391467
}
392468
}
393469

394-
pub unsafe extern fn destroy_value<T: 'static>(ptr: *mut u8) {
470+
unsafe extern fn destroy_value<T: 'static>(ptr: *mut u8) {
395471
// The OS TLS ensures that this key contains a NULL value when this
396472
// destructor starts to run. We set it back to a sentinel value of 1 to
397473
// ensure that any future calls to `get` for this thread will return

‎src/libstd/thread/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ pub use self::local::{LocalKey, LocalKeyState};
172172

173173
#[unstable(feature = "libstd_thread_internals", issue = "0")]
174174
#[cfg(target_thread_local)]
175-
#[doc(hidden)] pub use sys::fast_thread_local::Key as __FastLocalKeyInner;
175+
#[doc(hidden)] pub use self::local::fast::Key as __FastLocalKeyInner;
176176
#[unstable(feature = "libstd_thread_internals", issue = "0")]
177177
#[doc(hidden)] pub use self::local::os::Key as __OsLocalKeyInner;
178178

0 commit comments

Comments
 (0)
Please sign in to comment.