]>
git.proxmox.com Git - rustc.git/blob - src/libstd/sys/unix/fast_thread_local.rs
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.
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.
11 #![cfg(target_thread_local)]
12 #![unstable(feature = "thread_local_internals", issue = "0")]
14 use cell
::{Cell, UnsafeCell}
;
19 inner
: UnsafeCell
<Option
<T
>>,
21 // Metadata to keep track of the state of the destructor. Remember that
22 // these variables are thread-local, not global.
23 dtor_registered
: Cell
<bool
>,
24 dtor_running
: Cell
<bool
>,
27 unsafe impl<T
> ::marker
::Sync
for Key
<T
> { }
30 pub const fn new() -> Key
<T
> {
32 inner
: UnsafeCell
::new(None
),
33 dtor_registered
: Cell
::new(false),
34 dtor_running
: Cell
::new(false)
38 pub fn get(&'
static self) -> Option
<&'
static UnsafeCell
<Option
<T
>>> {
40 if intrinsics
::needs_drop
::<T
>() && self.dtor_running
.get() {
48 unsafe fn register_dtor(&self) {
49 if !intrinsics
::needs_drop
::<T
>() || self.dtor_registered
.get() {
53 register_dtor(self as *const _
as *mut u8,
55 self.dtor_registered
.set(true);
59 #[cfg(any(target_os = "linux", target_os = "fuchsia"))]
60 unsafe fn register_dtor_fallback(t
: *mut u8, dtor
: unsafe extern fn(*mut u8)) {
61 // The fallback implementation uses a vanilla OS-based TLS key to track
62 // the list of destructors that need to be run for this thread. The key
63 // then has its own destructor which runs all the other destructors.
65 // The destructor for DTORS is a little special in that it has a `while`
66 // loop to continuously drain the list of registered destructors. It
67 // *should* be the case that this loop always terminates because we
68 // provide the guarantee that a TLS key cannot be set after it is
69 // flagged for destruction.
70 use sys_common
::thread_local
as os
;
72 static DTORS
: os
::StaticKey
= os
::StaticKey
::new(Some(run_dtors
));
73 type List
= Vec
<(*mut u8, unsafe extern fn(*mut u8))>;
74 if DTORS
.get().is_null() {
75 let v
: Box
<List
> = box Vec
::new();
76 DTORS
.set(Box
::into_raw(v
) as *mut u8);
78 let list
: &mut List
= &mut *(DTORS
.get() as *mut List
);
81 unsafe extern fn run_dtors(mut ptr
: *mut u8) {
82 while !ptr
.is_null() {
83 let list
: Box
<List
> = Box
::from_raw(ptr
as *mut List
);
84 for &(ptr
, dtor
) in list
.iter() {
88 DTORS
.set(ptr
::null_mut());
93 // Since what appears to be glibc 2.18 this symbol has been shipped which
94 // GCC and clang both use to invoke destructors in thread_local globals, so
97 // Note, however, that we run on lots older linuxes, as well as cross
98 // compiling from a newer linux to an older linux, so we also have a
99 // fallback implementation to use as well.
101 // Due to rust-lang/rust#18804, make sure this is not generic!
102 #[cfg(target_os = "linux")]
103 unsafe fn register_dtor(t
: *mut u8, dtor
: unsafe extern fn(*mut u8)) {
108 #[linkage = "extern_weak"]
109 static __dso_handle
: *mut u8;
110 #[linkage = "extern_weak"]
111 static __cxa_thread_atexit_impl
: *const libc
::c_void
;
113 if !__cxa_thread_atexit_impl
.is_null() {
114 type F
= unsafe extern fn(dtor
: unsafe extern fn(*mut u8),
116 dso_handle
: *mut u8) -> libc
::c_int
;
117 mem
::transmute
::<*const libc
::c_void
, F
>(__cxa_thread_atexit_impl
)
118 (dtor
, t
, &__dso_handle
as *const _
as *mut _
);
121 register_dtor_fallback(t
, dtor
);
124 // OSX's analog of the above linux function is this _tlv_atexit function.
125 // The disassembly of thread_local globals in C++ (at least produced by
126 // clang) will have this show up in the output.
127 #[cfg(target_os = "macos")]
128 unsafe fn register_dtor(t
: *mut u8, dtor
: unsafe extern fn(*mut u8)) {
130 fn _tlv_atexit(dtor
: unsafe extern fn(*mut u8),
133 _tlv_atexit(dtor
, t
);
136 // Just use the thread_local fallback implementation, at least until there's
137 // a more direct implementation.
138 #[cfg(target_os = "fuchsia")]
139 unsafe fn register_dtor(t
: *mut u8, dtor
: unsafe extern fn(*mut u8)) {
140 register_dtor_fallback(t
, dtor
);
143 pub unsafe extern fn destroy_value
<T
>(ptr
: *mut u8) {
144 let ptr
= ptr
as *mut Key
<T
>;
145 // Right before we run the user destructor be sure to flag the
146 // destructor as running for this thread so calls to `get` will return
148 (*ptr
).dtor_running
.set(true);
150 // The OSX implementation of TLS apparently had an odd aspect to it
151 // where the pointer we have may be overwritten while this destructor
152 // is running. Specifically if a TLS destructor re-accesses TLS it may
153 // trigger a re-initialization of all TLS variables, paving over at
154 // least some destroyed ones with initial values.
156 // This means that if we drop a TLS value in place on OSX that we could
157 // revert the value to its original state halfway through the
158 // destructor, which would be bad!
160 // Hence, we use `ptr::read` on OSX (to move to a "safe" location)
161 // instead of drop_in_place.
162 if cfg
!(target_os
= "macos") {
163 ptr
::read((*ptr
).inner
.get());
165 ptr
::drop_in_place((*ptr
).inner
.get());