From 8082fb988a5915e693f18e6d299deaae64834079 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 12 Jul 2020 11:37:11 +0200 Subject: rename fast_thread_local -> thread_local_dtor; thread_local -> thread_local_key --- src/libstd/sys/unix/fast_thread_local.rs | 91 ----------- src/libstd/sys/unix/mod.rs | 4 +- src/libstd/sys/unix/thread_local.rs | 34 ---- src/libstd/sys/unix/thread_local_dtor.rs | 94 +++++++++++ src/libstd/sys/unix/thread_local_key.rs | 34 ++++ src/libstd/sys/windows/fast_thread_local.rs | 4 - src/libstd/sys/windows/mod.rs | 4 +- src/libstd/sys/windows/thread_local.rs | 236 ---------------------------- src/libstd/sys/windows/thread_local_dtor.rs | 4 + src/libstd/sys/windows/thread_local_key.rs | 236 ++++++++++++++++++++++++++++ 10 files changed, 372 insertions(+), 369 deletions(-) delete mode 100644 src/libstd/sys/unix/fast_thread_local.rs delete mode 100644 src/libstd/sys/unix/thread_local.rs create mode 100644 src/libstd/sys/unix/thread_local_dtor.rs create mode 100644 src/libstd/sys/unix/thread_local_key.rs delete mode 100644 src/libstd/sys/windows/fast_thread_local.rs delete mode 100644 src/libstd/sys/windows/thread_local.rs create mode 100644 src/libstd/sys/windows/thread_local_dtor.rs create mode 100644 src/libstd/sys/windows/thread_local_key.rs (limited to 'src/libstd/sys') diff --git a/src/libstd/sys/unix/fast_thread_local.rs b/src/libstd/sys/unix/fast_thread_local.rs deleted file mode 100644 index 8730b4de8be..00000000000 --- a/src/libstd/sys/unix/fast_thread_local.rs +++ /dev/null @@ -1,91 +0,0 @@ -#![cfg(target_thread_local)] -#![unstable(feature = "thread_local_internals", issue = "none")] - -// 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. -#[cfg(any( - target_os = "linux", - target_os = "fuchsia", - target_os = "redox", - target_os = "emscripten" -))] -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - use crate::mem; - use crate::sys_common::thread_local::register_dtor_fallback; - - extern "C" { - #[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 "C" fn( - dtor: unsafe extern "C" 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); -} - -// This implementation is very similar to register_dtor_fallback in -// sys_common/thread_local.rs. The main difference is that we want to hook into -// macOS's analog of the above linux function, _tlv_atexit. OSX will run the -// registered dtors before any TLS slots get freed, and when the main thread -// exits. -// -// Unfortunately, calling _tlv_atexit while tls dtors are running is UB. The -// workaround below is to register, via _tlv_atexit, a custom DTOR list once per -// thread. thread_local dtors are pushed to the DTOR list without calling -// _tlv_atexit. -#[cfg(target_os = "macos")] -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - use crate::cell::Cell; - use crate::ptr; - - #[thread_local] - static REGISTERED: Cell = Cell::new(false); - if !REGISTERED.get() { - _tlv_atexit(run_dtors, ptr::null_mut()); - REGISTERED.set(true); - } - - type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>; - - #[thread_local] - static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut()); - if DTORS.get().is_null() { - let v: Box = box Vec::new(); - DTORS.set(Box::into_raw(v)); - } - - extern "C" { - fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8); - } - - let list: &mut List = &mut *DTORS.get(); - list.push((t, dtor)); - - unsafe extern "C" fn run_dtors(_: *mut u8) { - let mut ptr = DTORS.replace(ptr::null_mut()); - while !ptr.is_null() { - let list = Box::from_raw(ptr); - for (ptr, dtor) in list.into_iter() { - dtor(ptr); - } - ptr = DTORS.replace(ptr::null_mut()); - } - } -} diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index b1688e74173..7cde02baedb 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -47,7 +47,6 @@ pub mod cmath; pub mod condvar; pub mod env; pub mod ext; -pub mod fast_thread_local; pub mod fd; pub mod fs; pub mod io; @@ -68,7 +67,8 @@ pub mod rwlock; pub mod stack_overflow; pub mod stdio; pub mod thread; -pub mod thread_local; +pub mod thread_local_key; +pub mod thread_local_dtor; pub mod time; pub use crate::sys_common::os_str_bytes as os_str; diff --git a/src/libstd/sys/unix/thread_local.rs b/src/libstd/sys/unix/thread_local.rs deleted file mode 100644 index 2c5b94b1e61..00000000000 --- a/src/libstd/sys/unix/thread_local.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![allow(dead_code)] // not used on all platforms - -use crate::mem; - -pub type Key = libc::pthread_key_t; - -#[inline] -pub unsafe fn create(dtor: Option) -> Key { - let mut key = 0; - assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0); - key -} - -#[inline] -pub unsafe fn set(key: Key, value: *mut u8) { - let r = libc::pthread_setspecific(key, value as *mut _); - debug_assert_eq!(r, 0); -} - -#[inline] -pub unsafe fn get(key: Key) -> *mut u8 { - libc::pthread_getspecific(key) as *mut u8 -} - -#[inline] -pub unsafe fn destroy(key: Key) { - let r = libc::pthread_key_delete(key); - debug_assert_eq!(r, 0); -} - -#[inline] -pub fn requires_synchronized_create() -> bool { - false -} diff --git a/src/libstd/sys/unix/thread_local_dtor.rs b/src/libstd/sys/unix/thread_local_dtor.rs new file mode 100644 index 00000000000..c3275eb6f0e --- /dev/null +++ b/src/libstd/sys/unix/thread_local_dtor.rs @@ -0,0 +1,94 @@ +#![cfg(target_thread_local)] +#![unstable(feature = "thread_local_internals", issue = "none")] + +//! Provides thread-local destructors without an associated "key", which +//! can be more efficient. + +// 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. +#[cfg(any( + target_os = "linux", + target_os = "fuchsia", + target_os = "redox", + target_os = "emscripten" +))] +pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + use crate::mem; + use crate::sys_common::thread_local_dtor::register_dtor_fallback; + + extern "C" { + #[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 "C" fn( + dtor: unsafe extern "C" 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); +} + +// This implementation is very similar to register_dtor_fallback in +// sys_common/thread_local.rs. The main difference is that we want to hook into +// macOS's analog of the above linux function, _tlv_atexit. OSX will run the +// registered dtors before any TLS slots get freed, and when the main thread +// exits. +// +// Unfortunately, calling _tlv_atexit while tls dtors are running is UB. The +// workaround below is to register, via _tlv_atexit, a custom DTOR list once per +// thread. thread_local dtors are pushed to the DTOR list without calling +// _tlv_atexit. +#[cfg(target_os = "macos")] +pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + use crate::cell::Cell; + use crate::ptr; + + #[thread_local] + static REGISTERED: Cell = Cell::new(false); + if !REGISTERED.get() { + _tlv_atexit(run_dtors, ptr::null_mut()); + REGISTERED.set(true); + } + + type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>; + + #[thread_local] + static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut()); + if DTORS.get().is_null() { + let v: Box = box Vec::new(); + DTORS.set(Box::into_raw(v)); + } + + extern "C" { + fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8); + } + + let list: &mut List = &mut *DTORS.get(); + list.push((t, dtor)); + + unsafe extern "C" fn run_dtors(_: *mut u8) { + let mut ptr = DTORS.replace(ptr::null_mut()); + while !ptr.is_null() { + let list = Box::from_raw(ptr); + for (ptr, dtor) in list.into_iter() { + dtor(ptr); + } + ptr = DTORS.replace(ptr::null_mut()); + } + } +} diff --git a/src/libstd/sys/unix/thread_local_key.rs b/src/libstd/sys/unix/thread_local_key.rs new file mode 100644 index 00000000000..2c5b94b1e61 --- /dev/null +++ b/src/libstd/sys/unix/thread_local_key.rs @@ -0,0 +1,34 @@ +#![allow(dead_code)] // not used on all platforms + +use crate::mem; + +pub type Key = libc::pthread_key_t; + +#[inline] +pub unsafe fn create(dtor: Option) -> Key { + let mut key = 0; + assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0); + key +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + let r = libc::pthread_setspecific(key, value as *mut _); + debug_assert_eq!(r, 0); +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + libc::pthread_getspecific(key) as *mut u8 +} + +#[inline] +pub unsafe fn destroy(key: Key) { + let r = libc::pthread_key_delete(key); + debug_assert_eq!(r, 0); +} + +#[inline] +pub fn requires_synchronized_create() -> bool { + false +} diff --git a/src/libstd/sys/windows/fast_thread_local.rs b/src/libstd/sys/windows/fast_thread_local.rs deleted file mode 100644 index 191fa07f32a..00000000000 --- a/src/libstd/sys/windows/fast_thread_local.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![unstable(feature = "thread_local_internals", issue = "none")] -#![cfg(target_thread_local)] - -pub use crate::sys_common::thread_local::register_dtor_fallback as register_dtor; diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 193ab5b47ef..d6a8eec4b80 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -20,7 +20,6 @@ pub mod cmath; pub mod condvar; pub mod env; pub mod ext; -pub mod fast_thread_local; pub mod fs; pub mod handle; pub mod io; @@ -35,7 +34,8 @@ pub mod process; pub mod rand; pub mod rwlock; pub mod thread; -pub mod thread_local; +pub mod thread_local_key; +pub mod thread_local_dtor; pub mod time; cfg_if::cfg_if! { if #[cfg(not(target_vendor = "uwp"))] { diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs deleted file mode 100644 index e0bb102b3af..00000000000 --- a/src/libstd/sys/windows/thread_local.rs +++ /dev/null @@ -1,236 +0,0 @@ -use crate::mem; -use crate::ptr; -use crate::sync::atomic::AtomicPtr; -use crate::sync::atomic::Ordering::SeqCst; -use crate::sys::c; - -pub type Key = c::DWORD; -pub type Dtor = unsafe extern "C" fn(*mut u8); - -// Turns out, like pretty much everything, Windows is pretty close the -// functionality that Unix provides, but slightly different! In the case of -// TLS, Windows does not provide an API to provide a destructor for a TLS -// variable. This ends up being pretty crucial to this implementation, so we -// need a way around this. -// -// The solution here ended up being a little obscure, but fear not, the -// internet has informed me [1][2] that this solution is not unique (no way -// I could have thought of it as well!). The key idea is to insert some hook -// somewhere to run arbitrary code on thread termination. With this in place -// we'll be able to run anything we like, including all TLS destructors! -// -// To accomplish this feat, we perform a number of threads, all contained -// within this module: -// -// * All TLS destructors are tracked by *us*, not the windows runtime. This -// means that we have a global list of destructors for each TLS key that -// we know about. -// * When a thread exits, we run over the entire list and run dtors for all -// non-null keys. This attempts to match Unix semantics in this regard. -// -// This ends up having the overhead of using a global list, having some -// locks here and there, and in general just adding some more code bloat. We -// attempt to optimize runtime by forgetting keys that don't have -// destructors, but this only gets us so far. -// -// For more details and nitty-gritty, see the code sections below! -// -// [1]: http://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way -// [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base -// /threading/thread_local_storage_win.cc#L42 - -// ------------------------------------------------------------------------- -// Native bindings -// -// This section is just raw bindings to the native functions that Windows -// provides, There's a few extra calls to deal with destructors. - -#[inline] -pub unsafe fn create(dtor: Option) -> Key { - let key = c::TlsAlloc(); - assert!(key != c::TLS_OUT_OF_INDEXES); - if let Some(f) = dtor { - register_dtor(key, f); - } - key -} - -#[inline] -pub unsafe fn set(key: Key, value: *mut u8) { - let r = c::TlsSetValue(key, value as c::LPVOID); - debug_assert!(r != 0); -} - -#[inline] -pub unsafe fn get(key: Key) -> *mut u8 { - c::TlsGetValue(key) as *mut u8 -} - -#[inline] -pub unsafe fn destroy(_key: Key) { - rtabort!("can't destroy tls keys on windows") -} - -#[inline] -pub fn requires_synchronized_create() -> bool { - true -} - -// ------------------------------------------------------------------------- -// Dtor registration -// -// Windows has no native support for running destructors so we manage our own -// list of destructors to keep track of how to destroy keys. We then install a -// callback later to get invoked whenever a thread exits, running all -// appropriate destructors. -// -// Currently unregistration from this list is not supported. A destructor can be -// registered but cannot be unregistered. There's various simplifying reasons -// for doing this, the big ones being: -// -// 1. Currently we don't even support deallocating TLS keys, so normal operation -// doesn't need to deallocate a destructor. -// 2. There is no point in time where we know we can unregister a destructor -// because it could always be getting run by some remote thread. -// -// Typically processes have a statically known set of TLS keys which is pretty -// small, and we'd want to keep this memory alive for the whole process anyway -// really. -// -// Perhaps one day we can fold the `Box` here into a static allocation, -// expanding the `StaticKey` structure to contain not only a slot for the TLS -// key but also a slot for the destructor queue on windows. An optimization for -// another day! - -static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); - -struct Node { - dtor: Dtor, - key: Key, - next: *mut Node, -} - -unsafe fn register_dtor(key: Key, dtor: Dtor) { - let mut node = Box::new(Node { key, dtor, next: ptr::null_mut() }); - - let mut head = DTORS.load(SeqCst); - loop { - node.next = head; - match DTORS.compare_exchange(head, &mut *node, SeqCst, SeqCst) { - Ok(_) => return mem::forget(node), - Err(cur) => head = cur, - } - } -} - -// ------------------------------------------------------------------------- -// Where the Magic (TM) Happens -// -// If you're looking at this code, and wondering "what is this doing?", -// you're not alone! I'll try to break this down step by step: -// -// # What's up with CRT$XLB? -// -// For anything about TLS destructors to work on Windows, we have to be able -// to run *something* when a thread exits. To do so, we place a very special -// static in a very special location. If this is encoded in just the right -// way, the kernel's loader is apparently nice enough to run some function -// of ours whenever a thread exits! How nice of the kernel! -// -// Lots of detailed information can be found in source [1] above, but the -// gist of it is that this is leveraging a feature of Microsoft's PE format -// (executable format) which is not actually used by any compilers today. -// This apparently translates to any callbacks in the ".CRT$XLB" section -// being run on certain events. -// -// So after all that, we use the compiler's #[link_section] feature to place -// a callback pointer into the magic section so it ends up being called. -// -// # What's up with this callback? -// -// The callback specified receives a number of parameters from... someone! -// (the kernel? the runtime? I'm not quite sure!) There are a few events that -// this gets invoked for, but we're currently only interested on when a -// thread or a process "detaches" (exits). The process part happens for the -// last thread and the thread part happens for any normal thread. -// -// # Ok, what's up with running all these destructors? -// -// This will likely need to be improved over time, but this function -// attempts a "poor man's" destructor callback system. Once we've got a list -// of what to run, we iterate over all keys, check their values, and then run -// destructors if the values turn out to be non null (setting them to null just -// beforehand). We do this a few times in a loop to basically match Unix -// semantics. If we don't reach a fixed point after a short while then we just -// inevitably leak something most likely. -// -// # The article mentions weird stuff about "/INCLUDE"? -// -// It sure does! Specifically we're talking about this quote: -// -// The Microsoft run-time library facilitates this process by defining a -// memory image of the TLS Directory and giving it the special name -// “__tls_used” (Intel x86 platforms) or “_tls_used” (other platforms). The -// linker looks for this memory image and uses the data there to create the -// TLS Directory. Other compilers that support TLS and work with the -// Microsoft linker must use this same technique. -// -// Basically what this means is that if we want support for our TLS -// destructors/our hook being called then we need to make sure the linker does -// not omit this symbol. Otherwise it will omit it and our callback won't be -// wired up. -// -// We don't actually use the `/INCLUDE` linker flag here like the article -// mentions because the Rust compiler doesn't propagate linker flags, but -// instead we use a shim function which performs a volatile 1-byte load from -// the address of the symbol to ensure it sticks around. - -#[link_section = ".CRT$XLB"] -#[allow(dead_code, unused_variables)] -#[used] // we don't want LLVM eliminating this symbol for any reason, and -// when the symbol makes it to the linker the linker will take over -pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c::LPVOID) = - on_tls_callback; - -#[allow(dead_code, unused_variables)] -unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID) { - if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH { - run_dtors(); - } - - // See comments above for what this is doing. Note that we don't need this - // trickery on GNU windows, just on MSVC. - reference_tls_used(); - #[cfg(target_env = "msvc")] - unsafe fn reference_tls_used() { - extern "C" { - static _tls_used: u8; - } - crate::intrinsics::volatile_load(&_tls_used); - } - #[cfg(not(target_env = "msvc"))] - unsafe fn reference_tls_used() {} -} - -#[allow(dead_code)] // actually called above -unsafe fn run_dtors() { - let mut any_run = true; - for _ in 0..5 { - if !any_run { - break; - } - any_run = false; - let mut cur = DTORS.load(SeqCst); - while !cur.is_null() { - let ptr = c::TlsGetValue((*cur).key); - - if !ptr.is_null() { - c::TlsSetValue((*cur).key, ptr::null_mut()); - ((*cur).dtor)(ptr as *mut _); - any_run = true; - } - - cur = (*cur).next; - } - } -} diff --git a/src/libstd/sys/windows/thread_local_dtor.rs b/src/libstd/sys/windows/thread_local_dtor.rs new file mode 100644 index 00000000000..7be13bc4b2b --- /dev/null +++ b/src/libstd/sys/windows/thread_local_dtor.rs @@ -0,0 +1,4 @@ +#![unstable(feature = "thread_local_internals", issue = "none")] +#![cfg(target_thread_local)] + +pub use crate::sys_common::thread_local_dtor::register_dtor_fallback as register_dtor; diff --git a/src/libstd/sys/windows/thread_local_key.rs b/src/libstd/sys/windows/thread_local_key.rs new file mode 100644 index 00000000000..e0bb102b3af --- /dev/null +++ b/src/libstd/sys/windows/thread_local_key.rs @@ -0,0 +1,236 @@ +use crate::mem; +use crate::ptr; +use crate::sync::atomic::AtomicPtr; +use crate::sync::atomic::Ordering::SeqCst; +use crate::sys::c; + +pub type Key = c::DWORD; +pub type Dtor = unsafe extern "C" fn(*mut u8); + +// Turns out, like pretty much everything, Windows is pretty close the +// functionality that Unix provides, but slightly different! In the case of +// TLS, Windows does not provide an API to provide a destructor for a TLS +// variable. This ends up being pretty crucial to this implementation, so we +// need a way around this. +// +// The solution here ended up being a little obscure, but fear not, the +// internet has informed me [1][2] that this solution is not unique (no way +// I could have thought of it as well!). The key idea is to insert some hook +// somewhere to run arbitrary code on thread termination. With this in place +// we'll be able to run anything we like, including all TLS destructors! +// +// To accomplish this feat, we perform a number of threads, all contained +// within this module: +// +// * All TLS destructors are tracked by *us*, not the windows runtime. This +// means that we have a global list of destructors for each TLS key that +// we know about. +// * When a thread exits, we run over the entire list and run dtors for all +// non-null keys. This attempts to match Unix semantics in this regard. +// +// This ends up having the overhead of using a global list, having some +// locks here and there, and in general just adding some more code bloat. We +// attempt to optimize runtime by forgetting keys that don't have +// destructors, but this only gets us so far. +// +// For more details and nitty-gritty, see the code sections below! +// +// [1]: http://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way +// [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base +// /threading/thread_local_storage_win.cc#L42 + +// ------------------------------------------------------------------------- +// Native bindings +// +// This section is just raw bindings to the native functions that Windows +// provides, There's a few extra calls to deal with destructors. + +#[inline] +pub unsafe fn create(dtor: Option) -> Key { + let key = c::TlsAlloc(); + assert!(key != c::TLS_OUT_OF_INDEXES); + if let Some(f) = dtor { + register_dtor(key, f); + } + key +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + let r = c::TlsSetValue(key, value as c::LPVOID); + debug_assert!(r != 0); +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + c::TlsGetValue(key) as *mut u8 +} + +#[inline] +pub unsafe fn destroy(_key: Key) { + rtabort!("can't destroy tls keys on windows") +} + +#[inline] +pub fn requires_synchronized_create() -> bool { + true +} + +// ------------------------------------------------------------------------- +// Dtor registration +// +// Windows has no native support for running destructors so we manage our own +// list of destructors to keep track of how to destroy keys. We then install a +// callback later to get invoked whenever a thread exits, running all +// appropriate destructors. +// +// Currently unregistration from this list is not supported. A destructor can be +// registered but cannot be unregistered. There's various simplifying reasons +// for doing this, the big ones being: +// +// 1. Currently we don't even support deallocating TLS keys, so normal operation +// doesn't need to deallocate a destructor. +// 2. There is no point in time where we know we can unregister a destructor +// because it could always be getting run by some remote thread. +// +// Typically processes have a statically known set of TLS keys which is pretty +// small, and we'd want to keep this memory alive for the whole process anyway +// really. +// +// Perhaps one day we can fold the `Box` here into a static allocation, +// expanding the `StaticKey` structure to contain not only a slot for the TLS +// key but also a slot for the destructor queue on windows. An optimization for +// another day! + +static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + +struct Node { + dtor: Dtor, + key: Key, + next: *mut Node, +} + +unsafe fn register_dtor(key: Key, dtor: Dtor) { + let mut node = Box::new(Node { key, dtor, next: ptr::null_mut() }); + + let mut head = DTORS.load(SeqCst); + loop { + node.next = head; + match DTORS.compare_exchange(head, &mut *node, SeqCst, SeqCst) { + Ok(_) => return mem::forget(node), + Err(cur) => head = cur, + } + } +} + +// ------------------------------------------------------------------------- +// Where the Magic (TM) Happens +// +// If you're looking at this code, and wondering "what is this doing?", +// you're not alone! I'll try to break this down step by step: +// +// # What's up with CRT$XLB? +// +// For anything about TLS destructors to work on Windows, we have to be able +// to run *something* when a thread exits. To do so, we place a very special +// static in a very special location. If this is encoded in just the right +// way, the kernel's loader is apparently nice enough to run some function +// of ours whenever a thread exits! How nice of the kernel! +// +// Lots of detailed information can be found in source [1] above, but the +// gist of it is that this is leveraging a feature of Microsoft's PE format +// (executable format) which is not actually used by any compilers today. +// This apparently translates to any callbacks in the ".CRT$XLB" section +// being run on certain events. +// +// So after all that, we use the compiler's #[link_section] feature to place +// a callback pointer into the magic section so it ends up being called. +// +// # What's up with this callback? +// +// The callback specified receives a number of parameters from... someone! +// (the kernel? the runtime? I'm not quite sure!) There are a few events that +// this gets invoked for, but we're currently only interested on when a +// thread or a process "detaches" (exits). The process part happens for the +// last thread and the thread part happens for any normal thread. +// +// # Ok, what's up with running all these destructors? +// +// This will likely need to be improved over time, but this function +// attempts a "poor man's" destructor callback system. Once we've got a list +// of what to run, we iterate over all keys, check their values, and then run +// destructors if the values turn out to be non null (setting them to null just +// beforehand). We do this a few times in a loop to basically match Unix +// semantics. If we don't reach a fixed point after a short while then we just +// inevitably leak something most likely. +// +// # The article mentions weird stuff about "/INCLUDE"? +// +// It sure does! Specifically we're talking about this quote: +// +// The Microsoft run-time library facilitates this process by defining a +// memory image of the TLS Directory and giving it the special name +// “__tls_used” (Intel x86 platforms) or “_tls_used” (other platforms). The +// linker looks for this memory image and uses the data there to create the +// TLS Directory. Other compilers that support TLS and work with the +// Microsoft linker must use this same technique. +// +// Basically what this means is that if we want support for our TLS +// destructors/our hook being called then we need to make sure the linker does +// not omit this symbol. Otherwise it will omit it and our callback won't be +// wired up. +// +// We don't actually use the `/INCLUDE` linker flag here like the article +// mentions because the Rust compiler doesn't propagate linker flags, but +// instead we use a shim function which performs a volatile 1-byte load from +// the address of the symbol to ensure it sticks around. + +#[link_section = ".CRT$XLB"] +#[allow(dead_code, unused_variables)] +#[used] // we don't want LLVM eliminating this symbol for any reason, and +// when the symbol makes it to the linker the linker will take over +pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c::LPVOID) = + on_tls_callback; + +#[allow(dead_code, unused_variables)] +unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID) { + if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH { + run_dtors(); + } + + // See comments above for what this is doing. Note that we don't need this + // trickery on GNU windows, just on MSVC. + reference_tls_used(); + #[cfg(target_env = "msvc")] + unsafe fn reference_tls_used() { + extern "C" { + static _tls_used: u8; + } + crate::intrinsics::volatile_load(&_tls_used); + } + #[cfg(not(target_env = "msvc"))] + unsafe fn reference_tls_used() {} +} + +#[allow(dead_code)] // actually called above +unsafe fn run_dtors() { + let mut any_run = true; + for _ in 0..5 { + if !any_run { + break; + } + any_run = false; + let mut cur = DTORS.load(SeqCst); + while !cur.is_null() { + let ptr = c::TlsGetValue((*cur).key); + + if !ptr.is_null() { + c::TlsSetValue((*cur).key, ptr::null_mut()); + ((*cur).dtor)(ptr as *mut _); + any_run = true; + } + + cur = (*cur).next; + } + } +} -- cgit 1.4.1-3-g733a5 From 7dc388654d6ef038065db23340e8eff7a567e5b4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 12 Jul 2020 11:45:04 +0200 Subject: adjust remaining targets --- src/libstd/sys/cloudabi/mod.rs | 4 ++-- src/libstd/sys/hermit/fast_thread_local.rs | 36 ----------------------------- src/libstd/sys/hermit/mod.rs | 4 ++-- src/libstd/sys/hermit/thread_local.rs | 26 --------------------- src/libstd/sys/hermit/thread_local_dtor.rs | 36 +++++++++++++++++++++++++++++ src/libstd/sys/hermit/thread_local_key.rs | 26 +++++++++++++++++++++ src/libstd/sys/sgx/mod.rs | 2 +- src/libstd/sys/sgx/thread_local.rs | 28 ---------------------- src/libstd/sys/sgx/thread_local_key.rs | 28 ++++++++++++++++++++++ src/libstd/sys/unix/mod.rs | 2 +- src/libstd/sys/vxworks/fast_thread_local.rs | 11 --------- src/libstd/sys/vxworks/mod.rs | 4 ++-- src/libstd/sys/vxworks/thread_local.rs | 34 --------------------------- src/libstd/sys/vxworks/thread_local_dtor.rs | 7 ++++++ src/libstd/sys/vxworks/thread_local_key.rs | 34 +++++++++++++++++++++++++++ src/libstd/sys/wasi/mod.rs | 8 +++---- src/libstd/sys/wasm/fast_thread_local.rs | 9 -------- src/libstd/sys/wasm/mod.rs | 4 ++-- src/libstd/sys/wasm/thread_local.rs | 26 --------------------- src/libstd/sys/wasm/thread_local_dtor.rs | 9 ++++++++ src/libstd/sys/wasm/thread_local_key.rs | 26 +++++++++++++++++++++ src/libstd/sys/windows/mod.rs | 2 +- src/libstd/sys_common/mod.rs | 2 +- 23 files changed, 182 insertions(+), 186 deletions(-) delete mode 100644 src/libstd/sys/hermit/fast_thread_local.rs delete mode 100644 src/libstd/sys/hermit/thread_local.rs create mode 100644 src/libstd/sys/hermit/thread_local_dtor.rs create mode 100644 src/libstd/sys/hermit/thread_local_key.rs delete mode 100644 src/libstd/sys/sgx/thread_local.rs create mode 100644 src/libstd/sys/sgx/thread_local_key.rs delete mode 100644 src/libstd/sys/vxworks/fast_thread_local.rs delete mode 100644 src/libstd/sys/vxworks/thread_local.rs create mode 100644 src/libstd/sys/vxworks/thread_local_dtor.rs create mode 100644 src/libstd/sys/vxworks/thread_local_key.rs delete mode 100644 src/libstd/sys/wasm/fast_thread_local.rs delete mode 100644 src/libstd/sys/wasm/thread_local.rs create mode 100644 src/libstd/sys/wasm/thread_local_dtor.rs create mode 100644 src/libstd/sys/wasm/thread_local_key.rs (limited to 'src/libstd/sys') diff --git a/src/libstd/sys/cloudabi/mod.rs b/src/libstd/sys/cloudabi/mod.rs index 8dbc31472d6..f7dd2c8d00f 100644 --- a/src/libstd/sys/cloudabi/mod.rs +++ b/src/libstd/sys/cloudabi/mod.rs @@ -16,8 +16,8 @@ pub mod rwlock; pub mod stack_overflow; pub mod stdio; pub mod thread; -#[path = "../unix/thread_local.rs"] -pub mod thread_local; +#[path = "../unix/thread_local_key.rs"] +pub mod thread_local_key; pub mod time; pub use crate::sys_common::os_str_bytes as os_str; diff --git a/src/libstd/sys/hermit/fast_thread_local.rs b/src/libstd/sys/hermit/fast_thread_local.rs deleted file mode 100644 index 9b683fce157..00000000000 --- a/src/libstd/sys/hermit/fast_thread_local.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![cfg(target_thread_local)] -#![unstable(feature = "thread_local_internals", issue = "none")] - -// Simplify dtor registration by using a list of destructors. -// The this solution works like the implementation of macOS and -// doesn't additional OS support - -use crate::cell::Cell; -use crate::ptr; - -#[thread_local] -static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut()); - -type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>; - -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - if DTORS.get().is_null() { - let v: Box = box Vec::new(); - DTORS.set(Box::into_raw(v)); - } - - let list: &mut List = &mut *DTORS.get(); - list.push((t, dtor)); -} - -// every thread call this function to run through all possible destructors -pub unsafe fn run_dtors() { - let mut ptr = DTORS.replace(ptr::null_mut()); - while !ptr.is_null() { - let list = Box::from_raw(ptr); - for (ptr, dtor) in list.into_iter() { - dtor(ptr); - } - ptr = DTORS.replace(ptr::null_mut()); - } -} diff --git a/src/libstd/sys/hermit/mod.rs b/src/libstd/sys/hermit/mod.rs index 7bdc1be3b17..675b82ceb77 100644 --- a/src/libstd/sys/hermit/mod.rs +++ b/src/libstd/sys/hermit/mod.rs @@ -22,7 +22,6 @@ pub mod cmath; pub mod condvar; pub mod env; pub mod ext; -pub mod fast_thread_local; pub mod fd; pub mod fs; pub mod io; @@ -37,7 +36,8 @@ pub mod rwlock; pub mod stack_overflow; pub mod stdio; pub mod thread; -pub mod thread_local; +pub mod thread_local_dtor; +pub mod thread_local_key; pub mod time; use crate::io::ErrorKind; diff --git a/src/libstd/sys/hermit/thread_local.rs b/src/libstd/sys/hermit/thread_local.rs deleted file mode 100644 index f8be9863ed5..00000000000 --- a/src/libstd/sys/hermit/thread_local.rs +++ /dev/null @@ -1,26 +0,0 @@ -pub type Key = usize; - -#[inline] -pub unsafe fn create(_dtor: Option) -> Key { - panic!("should not be used on the wasm target"); -} - -#[inline] -pub unsafe fn set(_key: Key, _value: *mut u8) { - panic!("should not be used on the wasm target"); -} - -#[inline] -pub unsafe fn get(_key: Key) -> *mut u8 { - panic!("should not be used on the wasm target"); -} - -#[inline] -pub unsafe fn destroy(_key: Key) { - panic!("should not be used on the wasm target"); -} - -#[inline] -pub fn requires_synchronized_create() -> bool { - panic!("should not be used on the wasm target"); -} diff --git a/src/libstd/sys/hermit/thread_local_dtor.rs b/src/libstd/sys/hermit/thread_local_dtor.rs new file mode 100644 index 00000000000..9b683fce157 --- /dev/null +++ b/src/libstd/sys/hermit/thread_local_dtor.rs @@ -0,0 +1,36 @@ +#![cfg(target_thread_local)] +#![unstable(feature = "thread_local_internals", issue = "none")] + +// Simplify dtor registration by using a list of destructors. +// The this solution works like the implementation of macOS and +// doesn't additional OS support + +use crate::cell::Cell; +use crate::ptr; + +#[thread_local] +static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut()); + +type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>; + +pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + if DTORS.get().is_null() { + let v: Box = box Vec::new(); + DTORS.set(Box::into_raw(v)); + } + + let list: &mut List = &mut *DTORS.get(); + list.push((t, dtor)); +} + +// every thread call this function to run through all possible destructors +pub unsafe fn run_dtors() { + let mut ptr = DTORS.replace(ptr::null_mut()); + while !ptr.is_null() { + let list = Box::from_raw(ptr); + for (ptr, dtor) in list.into_iter() { + dtor(ptr); + } + ptr = DTORS.replace(ptr::null_mut()); + } +} diff --git a/src/libstd/sys/hermit/thread_local_key.rs b/src/libstd/sys/hermit/thread_local_key.rs new file mode 100644 index 00000000000..bf1b49eb83b --- /dev/null +++ b/src/libstd/sys/hermit/thread_local_key.rs @@ -0,0 +1,26 @@ +pub type Key = usize; + +#[inline] +pub unsafe fn create(_dtor: Option) -> Key { + panic!("should not be used on the hermit target"); +} + +#[inline] +pub unsafe fn set(_key: Key, _value: *mut u8) { + panic!("should not be used on the hermit target"); +} + +#[inline] +pub unsafe fn get(_key: Key) -> *mut u8 { + panic!("should not be used on the hermit target"); +} + +#[inline] +pub unsafe fn destroy(_key: Key) { + panic!("should not be used on the hermit target"); +} + +#[inline] +pub fn requires_synchronized_create() -> bool { + panic!("should not be used on the hermit target"); +} diff --git a/src/libstd/sys/sgx/mod.rs b/src/libstd/sys/sgx/mod.rs index 397dd496ae8..a4968ff7d4f 100644 --- a/src/libstd/sys/sgx/mod.rs +++ b/src/libstd/sys/sgx/mod.rs @@ -30,7 +30,7 @@ pub mod rwlock; pub mod stack_overflow; pub mod stdio; pub mod thread; -pub mod thread_local; +pub mod thread_local_key; pub mod time; pub use crate::sys_common::os_str_bytes as os_str; diff --git a/src/libstd/sys/sgx/thread_local.rs b/src/libstd/sys/sgx/thread_local.rs deleted file mode 100644 index b21784475f0..00000000000 --- a/src/libstd/sys/sgx/thread_local.rs +++ /dev/null @@ -1,28 +0,0 @@ -use super::abi::tls::{Key as AbiKey, Tls}; - -pub type Key = usize; - -#[inline] -pub unsafe fn create(dtor: Option) -> Key { - Tls::create(dtor).as_usize() -} - -#[inline] -pub unsafe fn set(key: Key, value: *mut u8) { - Tls::set(AbiKey::from_usize(key), value) -} - -#[inline] -pub unsafe fn get(key: Key) -> *mut u8 { - Tls::get(AbiKey::from_usize(key)) -} - -#[inline] -pub unsafe fn destroy(key: Key) { - Tls::destroy(AbiKey::from_usize(key)) -} - -#[inline] -pub fn requires_synchronized_create() -> bool { - false -} diff --git a/src/libstd/sys/sgx/thread_local_key.rs b/src/libstd/sys/sgx/thread_local_key.rs new file mode 100644 index 00000000000..b21784475f0 --- /dev/null +++ b/src/libstd/sys/sgx/thread_local_key.rs @@ -0,0 +1,28 @@ +use super::abi::tls::{Key as AbiKey, Tls}; + +pub type Key = usize; + +#[inline] +pub unsafe fn create(dtor: Option) -> Key { + Tls::create(dtor).as_usize() +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + Tls::set(AbiKey::from_usize(key), value) +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + Tls::get(AbiKey::from_usize(key)) +} + +#[inline] +pub unsafe fn destroy(key: Key) { + Tls::destroy(AbiKey::from_usize(key)) +} + +#[inline] +pub fn requires_synchronized_create() -> bool { + false +} diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 7cde02baedb..eddf00d3979 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -67,8 +67,8 @@ pub mod rwlock; pub mod stack_overflow; pub mod stdio; pub mod thread; -pub mod thread_local_key; pub mod thread_local_dtor; +pub mod thread_local_key; pub mod time; pub use crate::sys_common::os_str_bytes as os_str; diff --git a/src/libstd/sys/vxworks/fast_thread_local.rs b/src/libstd/sys/vxworks/fast_thread_local.rs deleted file mode 100644 index 098668cf521..00000000000 --- a/src/libstd/sys/vxworks/fast_thread_local.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![cfg(target_thread_local)] -#![unstable(feature = "thread_local_internals", issue = "none")] - -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - use crate::sys_common::thread_local::register_dtor_fallback; - register_dtor_fallback(t, dtor); -} - -pub fn requires_move_before_drop() -> bool { - false -} diff --git a/src/libstd/sys/vxworks/mod.rs b/src/libstd/sys/vxworks/mod.rs index 0787e709898..1132a849e2f 100644 --- a/src/libstd/sys/vxworks/mod.rs +++ b/src/libstd/sys/vxworks/mod.rs @@ -13,7 +13,6 @@ pub mod cmath; pub mod condvar; pub mod env; pub mod ext; -pub mod fast_thread_local; pub mod fd; pub mod fs; pub mod io; @@ -29,7 +28,8 @@ pub mod rwlock; pub mod stack_overflow; pub mod stdio; pub mod thread; -pub mod thread_local; +pub mod thread_local_dtor; +pub mod thread_local_key; pub mod time; pub use crate::sys_common::os_str_bytes as os_str; diff --git a/src/libstd/sys/vxworks/thread_local.rs b/src/libstd/sys/vxworks/thread_local.rs deleted file mode 100644 index 2c5b94b1e61..00000000000 --- a/src/libstd/sys/vxworks/thread_local.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![allow(dead_code)] // not used on all platforms - -use crate::mem; - -pub type Key = libc::pthread_key_t; - -#[inline] -pub unsafe fn create(dtor: Option) -> Key { - let mut key = 0; - assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0); - key -} - -#[inline] -pub unsafe fn set(key: Key, value: *mut u8) { - let r = libc::pthread_setspecific(key, value as *mut _); - debug_assert_eq!(r, 0); -} - -#[inline] -pub unsafe fn get(key: Key) -> *mut u8 { - libc::pthread_getspecific(key) as *mut u8 -} - -#[inline] -pub unsafe fn destroy(key: Key) { - let r = libc::pthread_key_delete(key); - debug_assert_eq!(r, 0); -} - -#[inline] -pub fn requires_synchronized_create() -> bool { - false -} diff --git a/src/libstd/sys/vxworks/thread_local_dtor.rs b/src/libstd/sys/vxworks/thread_local_dtor.rs new file mode 100644 index 00000000000..3f73f6c4903 --- /dev/null +++ b/src/libstd/sys/vxworks/thread_local_dtor.rs @@ -0,0 +1,7 @@ +#![cfg(target_thread_local)] +#![unstable(feature = "thread_local_internals", issue = "none")] + +pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + use crate::sys_common::thread_local::register_dtor_fallback; + register_dtor_fallback(t, dtor); +} diff --git a/src/libstd/sys/vxworks/thread_local_key.rs b/src/libstd/sys/vxworks/thread_local_key.rs new file mode 100644 index 00000000000..2c5b94b1e61 --- /dev/null +++ b/src/libstd/sys/vxworks/thread_local_key.rs @@ -0,0 +1,34 @@ +#![allow(dead_code)] // not used on all platforms + +use crate::mem; + +pub type Key = libc::pthread_key_t; + +#[inline] +pub unsafe fn create(dtor: Option) -> Key { + let mut key = 0; + assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0); + key +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + let r = libc::pthread_setspecific(key, value as *mut _); + debug_assert_eq!(r, 0); +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + libc::pthread_getspecific(key) as *mut u8 +} + +#[inline] +pub unsafe fn destroy(key: Key) { + let r = libc::pthread_key_delete(key); + debug_assert_eq!(r, 0); +} + +#[inline] +pub fn requires_synchronized_create() -> bool { + false +} diff --git a/src/libstd/sys/wasi/mod.rs b/src/libstd/sys/wasi/mod.rs index 4fe9661421b..85f5282034f 100644 --- a/src/libstd/sys/wasi/mod.rs +++ b/src/libstd/sys/wasi/mod.rs @@ -36,8 +36,6 @@ pub mod net; pub mod os; pub use crate::sys_common::os_str_bytes as os_str; pub mod ext; -#[path = "../wasm/fast_thread_local.rs"] -pub mod fast_thread_local; pub mod path; pub mod pipe; pub mod process; @@ -47,8 +45,10 @@ pub mod rwlock; pub mod stack_overflow; pub mod stdio; pub mod thread; -#[path = "../wasm/thread_local.rs"] -pub mod thread_local; +#[path = "../wasm/thread_local_dtor.rs"] +pub mod thread_local_dtor; +#[path = "../wasm/thread_local_key.rs"] +pub mod thread_local_key; pub mod time; #[cfg(not(test))] diff --git a/src/libstd/sys/wasm/fast_thread_local.rs b/src/libstd/sys/wasm/fast_thread_local.rs deleted file mode 100644 index 85d66098302..00000000000 --- a/src/libstd/sys/wasm/fast_thread_local.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![unstable(feature = "thread_local_internals", issue = "none")] - -pub unsafe fn register_dtor(_t: *mut u8, _dtor: unsafe extern "C" fn(*mut u8)) { - // FIXME: right now there is no concept of "thread exit", but this is likely - // going to show up at some point in the form of an exported symbol that the - // wasm runtime is going to be expected to call. For now we basically just - // ignore the arguments, but if such a function starts to exist it will - // likely look like the OSX implementation in `unix/fast_thread_local.rs` -} diff --git a/src/libstd/sys/wasm/mod.rs b/src/libstd/sys/wasm/mod.rs index 050e8099af4..6939596e52d 100644 --- a/src/libstd/sys/wasm/mod.rs +++ b/src/libstd/sys/wasm/mod.rs @@ -20,7 +20,6 @@ pub mod alloc; pub mod args; pub mod cmath; pub mod env; -pub mod fast_thread_local; pub mod fs; pub mod io; pub mod memchr; @@ -32,7 +31,8 @@ pub mod process; pub mod stack_overflow; pub mod stdio; pub mod thread; -pub mod thread_local; +pub mod thread_local_dtor; +pub mod thread_local_key; pub mod time; pub use crate::sys_common::os_str_bytes as os_str; diff --git a/src/libstd/sys/wasm/thread_local.rs b/src/libstd/sys/wasm/thread_local.rs deleted file mode 100644 index f8be9863ed5..00000000000 --- a/src/libstd/sys/wasm/thread_local.rs +++ /dev/null @@ -1,26 +0,0 @@ -pub type Key = usize; - -#[inline] -pub unsafe fn create(_dtor: Option) -> Key { - panic!("should not be used on the wasm target"); -} - -#[inline] -pub unsafe fn set(_key: Key, _value: *mut u8) { - panic!("should not be used on the wasm target"); -} - -#[inline] -pub unsafe fn get(_key: Key) -> *mut u8 { - panic!("should not be used on the wasm target"); -} - -#[inline] -pub unsafe fn destroy(_key: Key) { - panic!("should not be used on the wasm target"); -} - -#[inline] -pub fn requires_synchronized_create() -> bool { - panic!("should not be used on the wasm target"); -} diff --git a/src/libstd/sys/wasm/thread_local_dtor.rs b/src/libstd/sys/wasm/thread_local_dtor.rs new file mode 100644 index 00000000000..85d66098302 --- /dev/null +++ b/src/libstd/sys/wasm/thread_local_dtor.rs @@ -0,0 +1,9 @@ +#![unstable(feature = "thread_local_internals", issue = "none")] + +pub unsafe fn register_dtor(_t: *mut u8, _dtor: unsafe extern "C" fn(*mut u8)) { + // FIXME: right now there is no concept of "thread exit", but this is likely + // going to show up at some point in the form of an exported symbol that the + // wasm runtime is going to be expected to call. For now we basically just + // ignore the arguments, but if such a function starts to exist it will + // likely look like the OSX implementation in `unix/fast_thread_local.rs` +} diff --git a/src/libstd/sys/wasm/thread_local_key.rs b/src/libstd/sys/wasm/thread_local_key.rs new file mode 100644 index 00000000000..f8be9863ed5 --- /dev/null +++ b/src/libstd/sys/wasm/thread_local_key.rs @@ -0,0 +1,26 @@ +pub type Key = usize; + +#[inline] +pub unsafe fn create(_dtor: Option) -> Key { + panic!("should not be used on the wasm target"); +} + +#[inline] +pub unsafe fn set(_key: Key, _value: *mut u8) { + panic!("should not be used on the wasm target"); +} + +#[inline] +pub unsafe fn get(_key: Key) -> *mut u8 { + panic!("should not be used on the wasm target"); +} + +#[inline] +pub unsafe fn destroy(_key: Key) { + panic!("should not be used on the wasm target"); +} + +#[inline] +pub fn requires_synchronized_create() -> bool { + panic!("should not be used on the wasm target"); +} diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index d6a8eec4b80..9a52371280e 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -34,8 +34,8 @@ pub mod process; pub mod rand; pub mod rwlock; pub mod thread; -pub mod thread_local_key; pub mod thread_local_dtor; +pub mod thread_local_key; pub mod time; cfg_if::cfg_if! { if #[cfg(not(target_vendor = "uwp"))] { diff --git a/src/libstd/sys_common/mod.rs b/src/libstd/sys_common/mod.rs index 1212b05c88a..e57bb267cbd 100644 --- a/src/libstd/sys_common/mod.rs +++ b/src/libstd/sys_common/mod.rs @@ -65,8 +65,8 @@ pub mod remutex; pub mod rwlock; pub mod thread; pub mod thread_info; -pub mod thread_local_key; pub mod thread_local_dtor; +pub mod thread_local_key; pub mod util; pub mod wtf8; -- cgit 1.4.1-3-g733a5