Skip to content

Commit 5688407

Browse files
committed
std: Move elf TLS to sys::fast_thread_local
1 parent ac968c4 commit 5688407

File tree

5 files changed

+171
-163
lines changed

5 files changed

+171
-163
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
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+
#![cfg(target_thread_local)]
12+
#![unstable(feature = "thread_local_internals", issue = "0")]
13+
14+
use cell::{Cell, UnsafeCell};
15+
use intrinsics;
16+
use ptr;
17+
18+
pub struct Key<T> {
19+
inner: UnsafeCell<Option<T>>,
20+
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>,
25+
}
26+
27+
unsafe impl<T> ::marker::Sync for Key<T> { }
28+
29+
impl<T> Key<T> {
30+
pub const fn new() -> Key<T> {
31+
Key {
32+
inner: UnsafeCell::new(None),
33+
dtor_registered: Cell::new(false),
34+
dtor_running: Cell::new(false)
35+
}
36+
}
37+
38+
pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> {
39+
unsafe {
40+
if intrinsics::needs_drop::<T>() && self.dtor_running.get() {
41+
return None
42+
}
43+
self.register_dtor();
44+
}
45+
Some(&self.inner)
46+
}
47+
48+
unsafe fn register_dtor(&self) {
49+
if !intrinsics::needs_drop::<T>() || self.dtor_registered.get() {
50+
return
51+
}
52+
53+
register_dtor(self as *const _ as *mut u8,
54+
destroy_value::<T>);
55+
self.dtor_registered.set(true);
56+
}
57+
}
58+
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.
64+
//
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;
71+
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);
77+
}
78+
let list: &mut List = &mut *(DTORS.get() as *mut List);
79+
list.push((t, dtor));
80+
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() {
85+
dtor(ptr);
86+
}
87+
ptr = DTORS.get();
88+
DTORS.set(ptr::null_mut());
89+
}
90+
}
91+
}
92+
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
95+
// let's do the same!
96+
//
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.
100+
//
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)) {
104+
use mem;
105+
use libc;
106+
107+
extern {
108+
#[linkage = "extern_weak"]
109+
static __dso_handle: *mut u8;
110+
#[linkage = "extern_weak"]
111+
static __cxa_thread_atexit_impl: *const libc::c_void;
112+
}
113+
if !__cxa_thread_atexit_impl.is_null() {
114+
type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8),
115+
arg: *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 _);
119+
return
120+
}
121+
register_dtor_fallback(t, dtor);
122+
}
123+
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)) {
129+
extern {
130+
fn _tlv_atexit(dtor: unsafe extern fn(*mut u8),
131+
arg: *mut u8);
132+
}
133+
_tlv_atexit(dtor, t);
134+
}
135+
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);
141+
}
142+
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
147+
// `None`.
148+
(*ptr).dtor_running.set(true);
149+
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.
155+
//
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!
159+
//
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());
164+
} else {
165+
ptr::drop_in_place((*ptr).inner.get());
166+
}
167+
}

src/libstd/sys/unix/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub mod backtrace;
3838
pub mod condvar;
3939
pub mod env;
4040
pub mod ext;
41+
pub mod fast_thread_local;
4142
pub mod fd;
4243
pub mod fs;
4344
pub mod memchr;

src/libstd/thread/local.rs

Lines changed: 2 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@ macro_rules! __thread_local_inner {
166166
{
167167
#[thread_local]
168168
#[cfg(target_thread_local)]
169-
static __KEY: $crate::thread::__ElfLocalKeyInner<$t> =
170-
$crate::thread::__ElfLocalKeyInner::new();
169+
static __KEY: $crate::thread::__FastLocalKeyInner<$t> =
170+
$crate::thread::__FastLocalKeyInner::new();
171171

172172
#[cfg(not(target_thread_local))]
173173
static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
@@ -310,165 +310,6 @@ impl<T: 'static> LocalKey<T> {
310310
}
311311
}
312312

313-
#[cfg(target_thread_local)]
314-
#[doc(hidden)]
315-
pub mod elf {
316-
use cell::{Cell, UnsafeCell};
317-
use intrinsics;
318-
use ptr;
319-
320-
pub struct Key<T> {
321-
inner: UnsafeCell<Option<T>>,
322-
323-
// Metadata to keep track of the state of the destructor. Remember that
324-
// these variables are thread-local, not global.
325-
dtor_registered: Cell<bool>,
326-
dtor_running: Cell<bool>,
327-
}
328-
329-
unsafe impl<T> ::marker::Sync for Key<T> { }
330-
331-
impl<T> Key<T> {
332-
pub const fn new() -> Key<T> {
333-
Key {
334-
inner: UnsafeCell::new(None),
335-
dtor_registered: Cell::new(false),
336-
dtor_running: Cell::new(false)
337-
}
338-
}
339-
340-
pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> {
341-
unsafe {
342-
if intrinsics::needs_drop::<T>() && self.dtor_running.get() {
343-
return None
344-
}
345-
self.register_dtor();
346-
}
347-
Some(&self.inner)
348-
}
349-
350-
unsafe fn register_dtor(&self) {
351-
if !intrinsics::needs_drop::<T>() || self.dtor_registered.get() {
352-
return
353-
}
354-
355-
register_dtor(self as *const _ as *mut u8,
356-
destroy_value::<T>);
357-
self.dtor_registered.set(true);
358-
}
359-
}
360-
361-
#[cfg(any(target_os = "linux", target_os = "fuchsia"))]
362-
unsafe fn register_dtor_fallback(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
363-
// The fallback implementation uses a vanilla OS-based TLS key to track
364-
// the list of destructors that need to be run for this thread. The key
365-
// then has its own destructor which runs all the other destructors.
366-
//
367-
// The destructor for DTORS is a little special in that it has a `while`
368-
// loop to continuously drain the list of registered destructors. It
369-
// *should* be the case that this loop always terminates because we
370-
// provide the guarantee that a TLS key cannot be set after it is
371-
// flagged for destruction.
372-
use sys_common::thread_local as os;
373-
374-
static DTORS: os::StaticKey = os::StaticKey::new(Some(run_dtors));
375-
type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>;
376-
if DTORS.get().is_null() {
377-
let v: Box<List> = box Vec::new();
378-
DTORS.set(Box::into_raw(v) as *mut u8);
379-
}
380-
let list: &mut List = &mut *(DTORS.get() as *mut List);
381-
list.push((t, dtor));
382-
383-
unsafe extern fn run_dtors(mut ptr: *mut u8) {
384-
while !ptr.is_null() {
385-
let list: Box<List> = Box::from_raw(ptr as *mut List);
386-
for &(ptr, dtor) in list.iter() {
387-
dtor(ptr);
388-
}
389-
ptr = DTORS.get();
390-
DTORS.set(ptr::null_mut());
391-
}
392-
}
393-
}
394-
395-
// Since what appears to be glibc 2.18 this symbol has been shipped which
396-
// GCC and clang both use to invoke destructors in thread_local globals, so
397-
// let's do the same!
398-
//
399-
// Note, however, that we run on lots older linuxes, as well as cross
400-
// compiling from a newer linux to an older linux, so we also have a
401-
// fallback implementation to use as well.
402-
//
403-
// Due to rust-lang/rust#18804, make sure this is not generic!
404-
#[cfg(target_os = "linux")]
405-
unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
406-
use mem;
407-
use libc;
408-
409-
extern {
410-
#[linkage = "extern_weak"]
411-
static __dso_handle: *mut u8;
412-
#[linkage = "extern_weak"]
413-
static __cxa_thread_atexit_impl: *const libc::c_void;
414-
}
415-
if !__cxa_thread_atexit_impl.is_null() {
416-
type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8),
417-
arg: *mut u8,
418-
dso_handle: *mut u8) -> libc::c_int;
419-
mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)
420-
(dtor, t, &__dso_handle as *const _ as *mut _);
421-
return
422-
}
423-
register_dtor_fallback(t, dtor);
424-
}
425-
426-
// OSX's analog of the above linux function is this _tlv_atexit function.
427-
// The disassembly of thread_local globals in C++ (at least produced by
428-
// clang) will have this show up in the output.
429-
#[cfg(target_os = "macos")]
430-
unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
431-
extern {
432-
fn _tlv_atexit(dtor: unsafe extern fn(*mut u8),
433-
arg: *mut u8);
434-
}
435-
_tlv_atexit(dtor, t);
436-
}
437-
438-
// Just use the thread_local fallback implementation, at least until there's
439-
// a more direct implementation.
440-
#[cfg(target_os = "fuchsia")]
441-
unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
442-
register_dtor_fallback(t, dtor);
443-
}
444-
445-
pub unsafe extern fn destroy_value<T>(ptr: *mut u8) {
446-
let ptr = ptr as *mut Key<T>;
447-
// Right before we run the user destructor be sure to flag the
448-
// destructor as running for this thread so calls to `get` will return
449-
// `None`.
450-
(*ptr).dtor_running.set(true);
451-
452-
// The OSX implementation of TLS apparently had an odd aspect to it
453-
// where the pointer we have may be overwritten while this destructor
454-
// is running. Specifically if a TLS destructor re-accesses TLS it may
455-
// trigger a re-initialization of all TLS variables, paving over at
456-
// least some destroyed ones with initial values.
457-
//
458-
// This means that if we drop a TLS value in place on OSX that we could
459-
// revert the value to its original state halfway through the
460-
// destructor, which would be bad!
461-
//
462-
// Hence, we use `ptr::read` on OSX (to move to a "safe" location)
463-
// instead of drop_in_place.
464-
if cfg!(target_os = "macos") {
465-
ptr::read((*ptr).inner.get());
466-
} else {
467-
ptr::drop_in_place((*ptr).inner.get());
468-
}
469-
}
470-
}
471-
472313
#[doc(hidden)]
473314
pub mod os {
474315
use cell::{Cell, UnsafeCell};

src/libstd/thread/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ pub use self::local::{LocalKey, LocalKeyState};
183183

184184
#[unstable(feature = "libstd_thread_internals", issue = "0")]
185185
#[cfg(target_thread_local)]
186-
#[doc(hidden)] pub use self::local::elf::Key as __ElfLocalKeyInner;
186+
#[doc(hidden)] pub use sys::fast_thread_local::Key as __FastLocalKeyInner;
187187
#[unstable(feature = "libstd_thread_internals", issue = "0")]
188188
#[doc(hidden)] pub use self::local::os::Key as __OsLocalKeyInner;
189189

src/tools/tidy/src/pal.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ const EXCEPTION_PATHS: &'static [&'static str] = &[
6969
"src/libstd/io/stdio.rs",
7070
"src/libstd/num/f32.rs",
7171
"src/libstd/num/f64.rs",
72-
"src/libstd/thread/local.rs",
7372
"src/libstd/sys/common/mod.rs",
7473
"src/libstd/sys/common/net.rs",
7574
"src/libstd/sys/common/util.rs",

0 commit comments

Comments
 (0)