From 4ff583b1161c5c2e08c28a0740f34a526b39a8bc Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Tue, 4 Apr 2017 12:31:38 -0400 Subject: fallout from NonZero/Unique/Shared changes --- src/libstd/collections/hash/table.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'src/libstd') diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 9623706548b..0a488dfd53a 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -49,24 +49,25 @@ impl TaggedHashUintPtr { #[inline] fn set_tag(&mut self, value: bool) { - let usize_ptr = &*self.0 as *const *mut HashUint as *mut usize; + let mut usize_ptr = self.0.as_ptr() as usize; unsafe { if value { - *usize_ptr |= 1; + usize_ptr |= 1; } else { - *usize_ptr &= !1; + usize_ptr &= !1; } + self.0 = Unique::new(usize_ptr as *mut HashUint) } } #[inline] fn tag(&self) -> bool { - (*self.0 as usize) & 1 == 1 + (self.0.as_ptr() as usize) & 1 == 1 } #[inline] fn ptr(&self) -> *mut HashUint { - (*self.0 as usize & !1) as *mut HashUint + (self.0.as_ptr() as usize & !1) as *mut HashUint } } @@ -1112,10 +1113,12 @@ impl<'a, K, V> Iterator for Drain<'a, K, V> { #[inline] fn next(&mut self) -> Option<(SafeHash, K, V)> { - self.iter.next().map(|raw| unsafe { - (*self.table.as_mut_ptr()).size -= 1; - let (k, v) = ptr::read(raw.pair()); - (SafeHash { hash: ptr::replace(&mut *raw.hash(), EMPTY_BUCKET) }, k, v) + self.iter.next().map(|raw| { + unsafe { + self.table.as_mut().size -= 1; + let (k, v) = ptr::read(raw.pair()); + (SafeHash { hash: ptr::replace(&mut *raw.hash(), EMPTY_BUCKET) }, k, v) + } }) } -- cgit 1.4.1-3-g733a5 From c7cffc5f4ef1def337ca2a294c3ca855ee703419 Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Thu, 4 May 2017 14:48:58 -0400 Subject: Deprecate heap::EMPTY in favour of Unique::empty or otherwise. --- src/liballoc/boxed.rs | 2 +- src/liballoc/heap.rs | 6 ++++-- src/liballoc/raw_vec.rs | 26 ++++++++++++-------------- src/libarena/lib.rs | 4 +--- src/libcollections/vec.rs | 7 ++++--- src/libstd/collections/hash/table.rs | 3 ++- 6 files changed, 24 insertions(+), 24 deletions(-) (limited to 'src/libstd') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index b03e3bb7a4b..fc6929f896e 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -156,7 +156,7 @@ fn make_place() -> IntermediateBox { let align = mem::align_of::(); let p = if size == 0 { - heap::EMPTY as *mut u8 + mem::align_of::() as *mut u8 } else { let p = unsafe { heap::allocate(size, align) }; if p.is_null() { diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 056af13016c..5ff21c86483 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -138,7 +138,9 @@ pub fn usable_size(size: usize, align: usize) -> usize { /// /// This preserves the non-null invariant for types like `Box`. The address /// may overlap with non-zero-size memory allocations. -pub const EMPTY: *mut () = 0x1 as *mut (); +#[rustc_deprecated(since = "1.19", reason = "Use Unique/Shared::empty() instead")] +#[unstable(feature = "heap_api", issue = "27700")] +pub const EMPTY: *mut () = 1 as *mut (); /// The allocator for unique pointers. // This function must not unwind. If it does, MIR trans will fail. @@ -147,7 +149,7 @@ pub const EMPTY: *mut () = 0x1 as *mut (); #[inline] unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { if size == 0 { - EMPTY as *mut u8 + align as *mut u8 } else { let ptr = allocate(size, align); if ptr.is_null() { diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 1f6f5ba17ed..7edf07944ec 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -22,13 +22,13 @@ use core::cmp; /// involved. This type is excellent for building your own data structures like Vec and VecDeque. /// In particular: /// -/// * Produces heap::EMPTY on zero-sized types -/// * Produces heap::EMPTY on zero-length allocations +/// * Produces Unique::empty() on zero-sized types +/// * Produces Unique::empty() on zero-length allocations /// * Catches all overflows in capacity computations (promotes them to "capacity overflow" panics) /// * Guards against 32-bit systems allocating more than isize::MAX bytes /// * Guards against overflowing your length /// * Aborts on OOM -/// * Avoids freeing heap::EMPTY +/// * Avoids freeing Unique::empty() /// * Contains a ptr::Unique and thus endows the user with all related benefits /// /// This type does not in anyway inspect the memory that it manages. When dropped it *will* @@ -55,15 +55,13 @@ impl RawVec { /// it makes a RawVec with capacity `usize::MAX`. Useful for implementing /// delayed allocation. pub fn new() -> Self { - unsafe { - // !0 is usize::MAX. This branch should be stripped at compile time. - let cap = if mem::size_of::() == 0 { !0 } else { 0 }; + // !0 is usize::MAX. This branch should be stripped at compile time. + let cap = if mem::size_of::() == 0 { !0 } else { 0 }; - // heap::EMPTY doubles as "unallocated" and "zero-sized allocation" - RawVec { - ptr: Unique::new(heap::EMPTY as *mut T), - cap: cap, - } + // Unique::empty() doubles as "unallocated" and "zero-sized allocation" + RawVec { + ptr: Unique::empty(), + cap: cap, } } @@ -101,7 +99,7 @@ impl RawVec { // handles ZSTs and `cap = 0` alike let ptr = if alloc_size == 0 { - heap::EMPTY as *mut u8 + mem::align_of::() as *mut u8 } else { let align = mem::align_of::(); let ptr = if zeroed { @@ -148,10 +146,10 @@ impl RawVec { impl RawVec { /// Gets a raw pointer to the start of the allocation. Note that this is - /// heap::EMPTY if `cap = 0` or T is zero-sized. In the former case, you must + /// Unique::empty() if `cap = 0` or T is zero-sized. In the former case, you must /// be careful. pub fn ptr(&self) -> *mut T { - self.ptr.ptr() + self.ptr.as_ptr() } /// Gets the capacity of the allocation. diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index a3cfc15895e..321fa2edd56 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -31,7 +31,6 @@ #![feature(alloc)] #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] -#![feature(heap_api)] #![feature(generic_param_attrs)] #![feature(staged_api)] #![cfg_attr(test, feature(test))] @@ -48,7 +47,6 @@ use std::mem; use std::ptr; use std::slice; -use alloc::heap; use alloc::raw_vec::RawVec; /// An arena that can hold objects of only one type. @@ -140,7 +138,7 @@ impl TypedArena { unsafe { if mem::size_of::() == 0 { self.ptr.set(intrinsics::arith_offset(self.ptr.get() as *mut u8, 1) as *mut T); - let ptr = heap::EMPTY as *mut T; + let ptr = mem::align_of::() as *mut T; // Don't drop the object. This `write` is equivalent to `forget`. ptr::write(ptr, object); &mut *ptr diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index 02ad0a67bda..7ec5c29de6b 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -67,7 +67,6 @@ #![stable(feature = "rust1", since = "1.0.0")] use alloc::boxed::Box; -use alloc::heap::EMPTY; use alloc::raw_vec::RawVec; use borrow::ToOwned; use borrow::Cow; @@ -2192,7 +2191,8 @@ impl Iterator for IntoIter { self.ptr = arith_offset(self.ptr as *const i8, 1) as *mut T; // Use a non-null pointer value - Some(ptr::read(EMPTY as *mut T)) + // (self.ptr might be null because of wrapping) + Some(ptr::read(1 as *mut T)) } else { let old = self.ptr; self.ptr = self.ptr.offset(1); @@ -2231,7 +2231,8 @@ impl DoubleEndedIterator for IntoIter { self.end = arith_offset(self.end as *const i8, -1) as *mut T; // Use a non-null pointer value - Some(ptr::read(EMPTY as *mut T)) + // (self.end might be null because of wrapping) + Some(ptr::read(1 as *mut T)) } else { self.end = self.end.offset(-1); diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 0a488dfd53a..a15269cc87c 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use alloc::heap::{EMPTY, allocate, deallocate}; +use alloc::heap::{allocate, deallocate}; use cmp; use hash::{BuildHasher, Hash, Hasher}; @@ -33,6 +33,7 @@ use self::BucketState::*; type HashUint = usize; const EMPTY_BUCKET: HashUint = 0; +const EMPTY: usize = 1; /// Special `Unique` that uses the lower bit of the pointer /// to expose a boolean tag. -- cgit 1.4.1-3-g733a5 From 68ae6173fea7411a5d9e5bde6624f5606caf8fe8 Mon Sep 17 00:00:00 2001 From: Jon Gjengset Date: Thu, 27 Apr 2017 12:58:52 -0400 Subject: Reload nameserver information on lookup failure As discussed in #41570, UNIX systems often cache the contents of /etc/resolv.conf, which can cause lookup failures to persist even after a network connection becomes available. This patch modifies lookup_host to force a reload of the nameserver entries following a lookup failure. This is in line with what many C programs already do (see #41570 for details). On systems with nscd, this should not be necessary, but not all systems run nscd. Introduces an std linkage dependency on libresolv on macOS/iOS (which also makes it necessary to update run-make/tools.mk). Fixes #41570. Depends on rust-lang/libc#585. --- src/liblibc | 2 +- src/libstd/build.rs | 5 +++++ src/libstd/sys_common/net.rs | 19 ++++++++++++++++--- src/test/run-make/tools.mk | 1 + 4 files changed, 23 insertions(+), 4 deletions(-) (limited to 'src/libstd') diff --git a/src/liblibc b/src/liblibc index c34a802d1eb..03562b0cb26 160000 --- a/src/liblibc +++ b/src/liblibc @@ -1 +1 @@ -Subproject commit c34a802d1eb037b44c5252078c7270b5472e0f65 +Subproject commit 03562b0cb26a00f49d4eaf18ca3e49608110b0c8 diff --git a/src/libstd/build.rs b/src/libstd/build.rs index 9fb83ad7598..f84662c3f86 100644 --- a/src/libstd/build.rs +++ b/src/libstd/build.rs @@ -43,11 +43,16 @@ fn main() { println!("cargo:rustc-link-lib=pthread"); } else if target.contains("apple-darwin") { println!("cargo:rustc-link-lib=System"); + + // res_init and friends require -lresolv on macOS/iOS. + // See #41582 and http://blog.achernya.com/2013/03/os-x-has-silly-libsystem.html + println!("cargo:rustc-link-lib=resolv"); } else if target.contains("apple-ios") { println!("cargo:rustc-link-lib=System"); println!("cargo:rustc-link-lib=objc"); println!("cargo:rustc-link-lib=framework=Security"); println!("cargo:rustc-link-lib=framework=Foundation"); + println!("cargo:rustc-link-lib=resolv"); } else if target.contains("windows") { println!("cargo:rustc-link-lib=advapi32"); println!("cargo:rustc-link-lib=ws2_32"); diff --git a/src/libstd/sys_common/net.rs b/src/libstd/sys_common/net.rs index 9239c18e597..a1897c8bd67 100644 --- a/src/libstd/sys_common/net.rs +++ b/src/libstd/sys_common/net.rs @@ -177,9 +177,22 @@ pub fn lookup_host(host: &str) -> io::Result { }; let mut res = ptr::null_mut(); unsafe { - cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, - &mut res))?; - Ok(LookupHost { original: res, cur: res }) + match cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) { + Ok(_) => { + Ok(LookupHost { original: res, cur: res }) + }, + #[cfg(unix)] + Err(e) => { + // The lookup failure could be caused by using a stale /etc/resolv.conf. + // See https://github.com/rust-lang/rust/issues/41570. + // We therefore force a reload of the nameserver information. + c::res_init(); + Err(e) + }, + // the cfg is needed here to avoid an "unreachable pattern" warning + #[cfg(not(unix))] + Err(e) => Err(e), + } } } diff --git a/src/test/run-make/tools.mk b/src/test/run-make/tools.mk index 1db87d474bd..693573d53a4 100644 --- a/src/test/run-make/tools.mk +++ b/src/test/run-make/tools.mk @@ -72,6 +72,7 @@ else endif else ifeq ($(UNAME),Darwin) + EXTRACFLAGS := -lresolv else ifeq ($(UNAME),FreeBSD) EXTRACFLAGS := -lm -lpthread -lgcc_s -- cgit 1.4.1-3-g733a5 From 495c998508039764b07a64303ae2c9461ec86a7b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 24 Apr 2017 11:34:16 -0700 Subject: std: Avoid locks during TLS destruction on Windows Gecko recently had a bug reported [1] with a deadlock in the Rust TLS implementation for Windows. TLS destructors are implemented in a sort of ad-hoc fashion on Windows as it doesn't natively support destructors for TLS keys. To work around this the runtime manages a list of TLS destructors and registers a hook to get run whenever a thread exits. When a thread exits it takes a look at the list and runs all destructors. Unfortunately it turns out that there's a lock which is held when our "at thread exit" callback is run. The callback then attempts to acquire a lock protecting the list of TLS destructors. Elsewhere in the codebase while we hold a lock over the TLS destructors we try to acquire the same lock held first before our special callback is run. And as a result, deadlock! This commit sidesteps the issue with a few small refactorings: * Removed support for destroying a TLS key on Windows. We don't actually ever exercise this as a public-facing API, and it's only used during `lazy_init` during racy situations. To handle that we just synchronize `lazy_init` globally on Windows so we never have to call `destroy`. * With no need to support removal the global synchronized `Vec` was tranformed to a lock-free linked list. With the removal of locks this means that iteration no long requires a lock and as such we won't run into the deadlock problem mentioned above. Note that it's still a general problem that you have to be extra super careful in TLS destructors. For example no code which runs a TLS destructor on Windows can call back into the Windows API to do a dynamic library lookup. Unfortunately I don't know of a great way around that, but this at least fixes the immediate problem that Gecko was seeing which is that with "well behaved" destructors the system would still deadlock! [1]: https://bugzilla.mozilla.org/show_bug.cgi?id=1358151 --- src/libstd/sys/unix/thread_local.rs | 5 + src/libstd/sys/windows/c.rs | 1 - src/libstd/sys/windows/thread_local.rs | 165 +++++++++++++-------------------- src/libstd/sys_common/thread_local.rs | 37 +++++--- 4 files changed, 93 insertions(+), 115 deletions(-) (limited to 'src/libstd') diff --git a/src/libstd/sys/unix/thread_local.rs b/src/libstd/sys/unix/thread_local.rs index d22118d4d79..2487f6bcaf7 100644 --- a/src/libstd/sys/unix/thread_local.rs +++ b/src/libstd/sys/unix/thread_local.rs @@ -38,3 +38,8 @@ 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/c.rs b/src/libstd/sys/windows/c.rs index 4daab31c28f..4e08c7a0125 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -935,7 +935,6 @@ extern "system" { args: *const c_void) -> DWORD; pub fn TlsAlloc() -> DWORD; - pub fn TlsFree(dwTlsIndex: DWORD) -> BOOL; pub fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID; pub fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL; pub fn GetLastError() -> DWORD; diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs index 597f05622a5..ad57f87dc1f 100644 --- a/src/libstd/sys/windows/thread_local.rs +++ b/src/libstd/sys/windows/thread_local.rs @@ -8,10 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use mem; use ptr; +use sync::atomic::AtomicPtr; +use sync::atomic::Ordering::SeqCst; use sys::c; -use sys_common::mutex::Mutex; -use sys_common; pub type Key = c::DWORD; pub type Dtor = unsafe extern fn(*mut u8); @@ -34,8 +35,6 @@ pub type Dtor = unsafe extern fn(*mut u8); // * 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 TLS key is destroyed, we're sure to remove it from the dtor list -// if it's in there. // * 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. // @@ -50,13 +49,6 @@ pub type Dtor = unsafe extern fn(*mut u8); // [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base // /threading/thread_local_storage_win.cc#L42 -// NB these are specifically not types from `std::sync` as they currently rely -// on poisoning and this module needs to operate at a lower level than requiring -// the thread infrastructure to be in place (useful on the borders of -// initialization/destruction). -static DTOR_LOCK: Mutex = Mutex::new(); -static mut DTORS: *mut Vec<(Key, Dtor)> = ptr::null_mut(); - // ------------------------------------------------------------------------- // Native bindings // @@ -85,81 +77,64 @@ pub unsafe fn get(key: Key) -> *mut u8 { } #[inline] -pub unsafe fn destroy(key: Key) { - if unregister_dtor(key) { - // FIXME: Currently if a key has a destructor associated with it we - // can't actually ever unregister it. If we were to - // unregister it, then any key destruction would have to be - // serialized with respect to actually running destructors. - // - // We want to avoid a race where right before run_dtors runs - // some destructors TlsFree is called. Allowing the call to - // TlsFree would imply that the caller understands that *all - // known threads* are not exiting, which is quite a difficult - // thing to know! - // - // For now we just leak all keys with dtors to "fix" this. - // Note that source [2] above shows precedent for this sort - // of strategy. - } else { - let r = c::TlsFree(key); - debug_assert!(r != 0); - } +pub unsafe fn destroy(_key: Key) { + rtabort!("can't destroy tls keys on windows") +} + +#[inline] +pub fn requires_synchronized_create() -> bool { + true } // ------------------------------------------------------------------------- // Dtor registration // -// These functions are associated with registering and unregistering -// destructors. They're pretty simple, they just push onto a vector and scan -// a vector currently. +// 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. // -// FIXME: This could probably be at least a little faster with a BTree. - -unsafe fn init_dtors() { - if !DTORS.is_null() { return } +// 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! - let dtors = box Vec::<(Key, Dtor)>::new(); +static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); - let res = sys_common::at_exit(move|| { - DTOR_LOCK.lock(); - let dtors = DTORS; - DTORS = 1 as *mut _; - Box::from_raw(dtors); - assert!(DTORS as usize == 1); // can't re-init after destructing - DTOR_LOCK.unlock(); - }); - if res.is_ok() { - DTORS = Box::into_raw(dtors); - } else { - DTORS = 1 as *mut _; - } +struct Node { + dtor: Dtor, + key: Key, + next: *mut Node, } unsafe fn register_dtor(key: Key, dtor: Dtor) { - DTOR_LOCK.lock(); - init_dtors(); - assert!(DTORS as usize != 0); - assert!(DTORS as usize != 1, - "cannot create new TLS keys after the main thread has exited"); - (*DTORS).push((key, dtor)); - DTOR_LOCK.unlock(); -} + let mut node = Box::new(Node { + key: key, + dtor: dtor, + next: ptr::null_mut(), + }); -unsafe fn unregister_dtor(key: Key) -> bool { - DTOR_LOCK.lock(); - init_dtors(); - assert!(DTORS as usize != 0); - assert!(DTORS as usize != 1, - "cannot unregister destructors after the main thread has exited"); - let ret = { - let dtors = &mut *DTORS; - let before = dtors.len(); - dtors.retain(|&(k, _)| k != key); - dtors.len() != before - }; - DTOR_LOCK.unlock(); - ret + 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, + } + } } // ------------------------------------------------------------------------- @@ -196,16 +171,12 @@ unsafe fn unregister_dtor(key: Key) -> bool { // # 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. To do this we clone a -// local copy of the dtor list to start out with. This is our fudgy attempt -// to not hold the lock while destructors run and not worry about the list -// changing while we're looking at it. -// -// 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. +// 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"? // @@ -259,25 +230,21 @@ unsafe extern "system" fn on_tls_callback(h: c::LPVOID, unsafe fn run_dtors() { let mut any_run = true; for _ in 0..5 { - if !any_run { break } + if !any_run { + break + } any_run = false; - let dtors = { - DTOR_LOCK.lock(); - let ret = if DTORS as usize <= 1 { - Vec::new() - } else { - (*DTORS).iter().map(|s| *s).collect() - }; - DTOR_LOCK.unlock(); - ret - }; - for &(key, dtor) in &dtors { - let ptr = c::TlsGetValue(key); + let mut cur = DTORS.load(SeqCst); + while !cur.is_null() { + let ptr = c::TlsGetValue((*cur).key); + if !ptr.is_null() { - c::TlsSetValue(key, ptr::null_mut()); - dtor(ptr as *mut _); + c::TlsSetValue((*cur).key, ptr::null_mut()); + ((*cur).dtor)(ptr as *mut _); any_run = true; } + + cur = (*cur).next; } } } diff --git a/src/libstd/sys_common/thread_local.rs b/src/libstd/sys_common/thread_local.rs index 25a9d5720d9..0ade90e64c3 100644 --- a/src/libstd/sys_common/thread_local.rs +++ b/src/libstd/sys_common/thread_local.rs @@ -61,6 +61,7 @@ use sync::atomic::{self, AtomicUsize, Ordering}; use sys::thread_local as imp; +use sys_common::mutex::Mutex; /// A type for TLS keys that are statically allocated. /// @@ -145,20 +146,6 @@ impl StaticKey { #[inline] pub unsafe fn set(&self, val: *mut u8) { imp::set(self.key(), val) } - /// Deallocates this OS TLS key. - /// - /// This function is unsafe as there is no guarantee that the key is not - /// currently in use by other threads or will not ever be used again. - /// - /// Note that this does *not* run the user-provided destructor if one was - /// specified at definition time. Doing so must be done manually. - pub unsafe fn destroy(&self) { - match self.key.swap(0, Ordering::SeqCst) { - 0 => {} - n => { imp::destroy(n as imp::Key) } - } - } - #[inline] unsafe fn key(&self) -> imp::Key { match self.key.load(Ordering::Relaxed) { @@ -168,6 +155,24 @@ impl StaticKey { } unsafe fn lazy_init(&self) -> usize { + // Currently the Windows implementation of TLS is pretty hairy, and + // it greatly simplifies creation if we just synchronize everything. + // + // Additionally a 0-index of a tls key hasn't been seen on windows, so + // we just simplify the whole branch. + if imp::requires_synchronized_create() { + static INIT_LOCK: Mutex = Mutex::new(); + INIT_LOCK.lock(); + let mut key = self.key.load(Ordering::SeqCst); + if key == 0 { + key = imp::create(self.dtor) as usize; + self.key.store(key, Ordering::SeqCst); + } + INIT_LOCK.unlock(); + assert!(key != 0); + return key + } + // POSIX allows the key created here to be 0, but the compare_and_swap // below relies on using 0 as a sentinel value to check who won the // race to set the shared TLS key. As far as I know, there is no @@ -227,7 +232,9 @@ impl Key { impl Drop for Key { fn drop(&mut self) { - unsafe { imp::destroy(self.key) } + // Right now Windows doesn't support TLS key destruction, but this also + // isn't used anywhere other than tests, so just leak the TLS key. + // unsafe { imp::destroy(self.key) } } } -- cgit 1.4.1-3-g733a5