diff options
| author | bors <bors@rust-lang.org> | 2019-01-20 11:08:37 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2019-01-20 11:08:37 +0000 |
| commit | d38d6be336e87055d90375c981806d5d133f446a (patch) | |
| tree | 633c25a2d88501d0fe178f944055264d5a36d1c9 /src/libstd/sys | |
| parent | 2ab5d8ac447f1f3f865dbae5bafbdeacacdea6d9 (diff) | |
| parent | 1a51bb8174e97251a37fcd83ff8750b7773e762a (diff) | |
| download | rust-d38d6be336e87055d90375c981806d5d133f446a.tar.gz rust-d38d6be336e87055d90375c981806d5d133f446a.zip | |
Auto merge of #57655 - mtak-:fix-tls-dtors-macos, r=alexcrichton
OSX: fix #57534 registering thread dtors while running thread dtors r? @alexcrichton - "fast" `thread_local` destructors get run even on the main thread - "fast" `thread_local` dtors, can initialize other `thread_local`'s One corner case where this fix doesn't work, is when a C++ `thread_local` triggers the initialization of a rust `thread_local`. I did not add any std::thread specific flag to indicate that the thread is currently exiting, which would be checked before registering a new dtor (I didn't really know where to stick that). I think this does the trick tho! Let me know if anything needs tweaking/fixing/etc. resolves this for macos: https://github.com/rust-lang/rust/issues/28129 fixes: https://github.com/rust-lang/rust/issues/57534
Diffstat (limited to 'src/libstd/sys')
| -rw-r--r-- | src/libstd/sys/unix/fast_thread_local.rs | 61 |
1 files changed, 44 insertions, 17 deletions
diff --git a/src/libstd/sys/unix/fast_thread_local.rs b/src/libstd/sys/unix/fast_thread_local.rs index d48d701dd50..742ffd12b88 100644 --- a/src/libstd/sys/unix/fast_thread_local.rs +++ b/src/libstd/sys/unix/fast_thread_local.rs @@ -33,30 +33,57 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { 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. +// 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 fn(*mut u8)) { + use cell::Cell; + use ptr; + + #[thread_local] + static REGISTERED: Cell<bool> = Cell::new(false); + if !REGISTERED.get() { + _tlv_atexit(run_dtors, ptr::null_mut()); + REGISTERED.set(true); + } + + type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>; + + #[thread_local] + static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut()); + if DTORS.get().is_null() { + let v: Box<List> = box Vec::new(); + DTORS.set(Box::into_raw(v)); + } + extern { fn _tlv_atexit(dtor: unsafe extern fn(*mut u8), arg: *mut u8); } - _tlv_atexit(dtor, t); + + let list: &mut List = &mut *DTORS.get(); + list.push((t, dtor)); + + unsafe extern 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()); + } + } } 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") + false } |
