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.
78 lines
3.1 KiB
Rust
78 lines
3.1 KiB
Rust
// 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.
|
|
|
|
#![cfg(target_thread_local)]
|
|
#![unstable(feature = "thread_local_internals", issue = "0")]
|
|
|
|
// 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!
|
|
//
|
|
// Note, however, that we run on lots older linuxes, as well as cross
|
|
// compiling from a newer linux to an older linux, so we also have a
|
|
// fallback implementation to use as well.
|
|
//
|
|
// Due to rust-lang/rust#18804, make sure this is not generic!
|
|
#[cfg(target_os = "linux")]
|
|
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"]
|
|
static __dso_handle: *mut u8;
|
|
#[linkage = "extern_weak"]
|
|
static __cxa_thread_atexit_impl: *const libc::c_void;
|
|
}
|
|
if !__cxa_thread_atexit_impl.is_null() {
|
|
type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8),
|
|
arg: *mut u8,
|
|
dso_handle: *mut u8) -> libc::c_int;
|
|
mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)
|
|
(dtor, t, &__dso_handle as *const _ as *mut _);
|
|
return
|
|
}
|
|
register_dtor_fallback(t, dtor);
|
|
}
|
|
|
|
// macOS's analog of the above linux function is this _tlv_atexit function.
|
|
// 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")]
|
|
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);
|
|
}
|
|
_tlv_atexit(dtor, t);
|
|
}
|
|
|
|
// Just use the thread_local fallback implementation, at least until there's
|
|
// a more direct implementation.
|
|
#[cfg(target_os = "fuchsia")]
|
|
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
|
|
// trigger a re-initialization of all TLS variables, paving over at
|
|
// least some destroyed ones with initial values.
|
|
//
|
|
// This means that if we drop a TLS value in place on macOS that we could
|
|
// revert the value to its original state halfway through the
|
|
// destructor, which would be bad!
|
|
//
|
|
// Hence, we use `ptr::read` on macOS (to move to a "safe" location)
|
|
// instead of drop_in_place.
|
|
cfg!(target_os = "macos")
|
|
}
|