]>
Commit | Line | Data |
---|---|---|
c30ab7b3 | 1 | #![cfg(target_thread_local)] |
dfeec247 | 2 | #![unstable(feature = "thread_local_internals", issue = "none")] |
c30ab7b3 | 3 | |
3dfed10e XL |
4 | //! Provides thread-local destructors without an associated "key", which |
5 | //! can be more efficient. | |
6 | ||
c30ab7b3 SL |
7 | // Since what appears to be glibc 2.18 this symbol has been shipped which |
8 | // GCC and clang both use to invoke destructors in thread_local globals, so | |
9 | // let's do the same! | |
10 | // | |
11 | // Note, however, that we run on lots older linuxes, as well as cross | |
12 | // compiling from a newer linux to an older linux, so we also have a | |
13 | // fallback implementation to use as well. | |
60c5eb7d XL |
14 | #[cfg(any( |
15 | target_os = "linux", | |
16 | target_os = "fuchsia", | |
17 | target_os = "redox", | |
18 | target_os = "emscripten" | |
19 | ))] | |
20 | pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { | |
532ac7d7 | 21 | use crate::mem; |
3dfed10e | 22 | use crate::sys_common::thread_local_dtor::register_dtor_fallback; |
c30ab7b3 | 23 | |
60c5eb7d | 24 | extern "C" { |
c30ab7b3 SL |
25 | #[linkage = "extern_weak"] |
26 | static __dso_handle: *mut u8; | |
27 | #[linkage = "extern_weak"] | |
28 | static __cxa_thread_atexit_impl: *const libc::c_void; | |
29 | } | |
30 | if !__cxa_thread_atexit_impl.is_null() { | |
60c5eb7d XL |
31 | type F = unsafe extern "C" fn( |
32 | dtor: unsafe extern "C" fn(*mut u8), | |
33 | arg: *mut u8, | |
34 | dso_handle: *mut u8, | |
35 | ) -> libc::c_int; | |
36 | mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)( | |
37 | dtor, | |
38 | t, | |
39 | &__dso_handle as *const _ as *mut _, | |
40 | ); | |
41 | return; | |
c30ab7b3 SL |
42 | } |
43 | register_dtor_fallback(t, dtor); | |
44 | } | |
45 | ||
9fa01778 XL |
46 | // This implementation is very similar to register_dtor_fallback in |
47 | // sys_common/thread_local.rs. The main difference is that we want to hook into | |
48 | // macOS's analog of the above linux function, _tlv_atexit. OSX will run the | |
49 | // registered dtors before any TLS slots get freed, and when the main thread | |
50 | // exits. | |
51 | // | |
52 | // Unfortunately, calling _tlv_atexit while tls dtors are running is UB. The | |
53 | // workaround below is to register, via _tlv_atexit, a custom DTOR list once per | |
54 | // thread. thread_local dtors are pushed to the DTOR list without calling | |
55 | // _tlv_atexit. | |
c30ab7b3 | 56 | #[cfg(target_os = "macos")] |
60c5eb7d | 57 | pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { |
532ac7d7 XL |
58 | use crate::cell::Cell; |
59 | use crate::ptr; | |
9fa01778 XL |
60 | |
61 | #[thread_local] | |
62 | static REGISTERED: Cell<bool> = Cell::new(false); | |
63 | if !REGISTERED.get() { | |
64 | _tlv_atexit(run_dtors, ptr::null_mut()); | |
65 | REGISTERED.set(true); | |
66 | } | |
67 | ||
60c5eb7d | 68 | type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>; |
9fa01778 XL |
69 | |
70 | #[thread_local] | |
71 | static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut()); | |
72 | if DTORS.get().is_null() { | |
73 | let v: Box<List> = box Vec::new(); | |
74 | DTORS.set(Box::into_raw(v)); | |
75 | } | |
76 | ||
60c5eb7d XL |
77 | extern "C" { |
78 | fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8); | |
c30ab7b3 | 79 | } |
9fa01778 XL |
80 | |
81 | let list: &mut List = &mut *DTORS.get(); | |
82 | list.push((t, dtor)); | |
83 | ||
60c5eb7d | 84 | unsafe extern "C" fn run_dtors(_: *mut u8) { |
9fa01778 XL |
85 | let mut ptr = DTORS.replace(ptr::null_mut()); |
86 | while !ptr.is_null() { | |
87 | let list = Box::from_raw(ptr); | |
88 | for (ptr, dtor) in list.into_iter() { | |
89 | dtor(ptr); | |
90 | } | |
91 | ptr = DTORS.replace(ptr::null_mut()); | |
92 | } | |
93 | } | |
c30ab7b3 | 94 | } |
cdc7bbd5 | 95 | |
923072b8 | 96 | #[cfg(any(target_os = "vxworks", target_os = "horizon"))] |
cdc7bbd5 XL |
97 | pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { |
98 | use crate::sys_common::thread_local_dtor::register_dtor_fallback; | |
99 | register_dtor_fallback(t, dtor); | |
100 | } |