diff options
| author | Sayan Nandan <17377258+sntdevco@users.noreply.github.com> | 2019-08-09 13:01:05 +0530 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-08-09 13:01:05 +0530 |
| commit | fb3a01354ffecc41d7a189e4dd225d706387a522 (patch) | |
| tree | 41492dfe93f1dccba847dadb56ac6aa079edaaa9 /src/libstd/thread | |
| parent | 33445aea509cadcd715009c79795d289268daa7c (diff) | |
| parent | 5aa3d9a7b5d3a46a7f158e8881146331a6bc9243 (diff) | |
| download | rust-fb3a01354ffecc41d7a189e4dd225d706387a522.tar.gz rust-fb3a01354ffecc41d7a189e4dd225d706387a522.zip | |
Merge pull request #1 from rust-lang/master
Merge recent changes into master
Diffstat (limited to 'src/libstd/thread')
| -rw-r--r-- | src/libstd/thread/local.rs | 313 | ||||
| -rw-r--r-- | src/libstd/thread/mod.rs | 39 |
2 files changed, 224 insertions, 128 deletions
diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 7ad6b124e3a..f85b5d632f1 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -2,10 +2,8 @@ #![unstable(feature = "thread_local_internals", issue = "0")] -use crate::cell::UnsafeCell; +use crate::error::Error; use crate::fmt; -use crate::hint; -use crate::mem; /// A thread local storage key which owns its contents. /// @@ -92,15 +90,12 @@ pub struct LocalKey<T: 'static> { // trivially devirtualizable by LLVM because the value of `inner` never // changes and the constant should be readonly within a crate. This mainly // only runs into problems when TLS statics are exported across crates. - inner: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>, - - // initialization routine to invoke to create a value - init: fn() -> T, + inner: unsafe fn() -> Option<&'static T>, } #[stable(feature = "std_debug", since = "1.16.0")] impl<T: 'static> fmt::Debug for LocalKey<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("LocalKey { .. }") } } @@ -159,10 +154,7 @@ macro_rules! __thread_local_inner { #[inline] fn __init() -> $t { $init } - unsafe fn __getit() -> $crate::option::Option< - &'static $crate::cell::UnsafeCell< - $crate::option::Option<$t>>> - { + unsafe fn __getit() -> $crate::option::Option<&'static $t> { #[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))] static __KEY: $crate::thread::__StaticLocalKeyInner<$t> = $crate::thread::__StaticLocalKeyInner::new(); @@ -182,11 +174,11 @@ macro_rules! __thread_local_inner { static __KEY: $crate::thread::__OsLocalKeyInner<$t> = $crate::thread::__OsLocalKeyInner::new(); - __KEY.get() + __KEY.get(__init) } unsafe { - $crate::thread::LocalKey::new(__getit, __init) + $crate::thread::LocalKey::new(__getit) } } }; @@ -198,34 +190,36 @@ macro_rules! __thread_local_inner { /// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with). #[stable(feature = "thread_local_try_with", since = "1.26.0")] +#[derive(Clone, Copy, Eq, PartialEq)] pub struct AccessError { _private: (), } #[stable(feature = "thread_local_try_with", since = "1.26.0")] impl fmt::Debug for AccessError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("AccessError").finish() } } #[stable(feature = "thread_local_try_with", since = "1.26.0")] impl fmt::Display for AccessError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt("already destroyed", f) } } +#[stable(feature = "thread_local_try_with", since = "1.26.0")] +impl Error for AccessError {} + impl<T: 'static> LocalKey<T> { #[doc(hidden)] #[unstable(feature = "thread_local_internals", reason = "recently added to create a key", issue = "0")] - pub const unsafe fn new(inner: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>, - init: fn() -> T) -> LocalKey<T> { + pub const unsafe fn new(inner: unsafe fn() -> Option<&'static T>) -> LocalKey<T> { LocalKey { inner, - init, } } @@ -246,37 +240,6 @@ impl<T: 'static> LocalKey<T> { after it is destroyed") } - unsafe fn init(&self, slot: &UnsafeCell<Option<T>>) -> &T { - // Execute the initialization up front, *then* move it into our slot, - // just in case initialization fails. - let value = (self.init)(); - let ptr = slot.get(); - - // note that this can in theory just be `*ptr = Some(value)`, but due to - // the compiler will currently codegen that pattern with something like: - // - // ptr::drop_in_place(ptr) - // ptr::write(ptr, Some(value)) - // - // Due to this pattern it's possible for the destructor of the value in - // `ptr` (e.g., if this is being recursively initialized) to re-access - // TLS, in which case there will be a `&` and `&mut` pointer to the same - // value (an aliasing violation). To avoid setting the "I'm running a - // destructor" flag we just use `mem::replace` which should sequence the - // operations a little differently and make this safe to call. - mem::replace(&mut *ptr, Some(value)); - - // After storing `Some` we want to get a reference to the contents of - // what we just stored. While we could use `unwrap` here and it should - // always work it empirically doesn't seem to always get optimized away, - // which means that using something like `try_with` can pull in - // panicking code and cause a large size bloat. - match *ptr { - Some(ref x) => x, - None => hint::unreachable_unchecked(), - } - } - /// Acquires a reference to the value in this TLS key. /// /// This will lazily initialize the value if this thread has not referenced @@ -293,13 +256,68 @@ impl<T: 'static> LocalKey<T> { F: FnOnce(&T) -> R, { unsafe { - let slot = (self.inner)().ok_or(AccessError { + let thread_local = (self.inner)().ok_or(AccessError { _private: (), })?; - Ok(f(match *slot.get() { - Some(ref inner) => inner, - None => self.init(slot), - })) + Ok(f(thread_local)) + } + } +} + +mod lazy { + use crate::cell::UnsafeCell; + use crate::mem; + use crate::hint; + + pub struct LazyKeyInner<T> { + inner: UnsafeCell<Option<T>>, + } + + impl<T> LazyKeyInner<T> { + pub const fn new() -> LazyKeyInner<T> { + LazyKeyInner { + inner: UnsafeCell::new(None), + } + } + + pub unsafe fn get(&self) -> Option<&'static T> { + (*self.inner.get()).as_ref() + } + + pub unsafe fn initialize<F: FnOnce() -> T>(&self, init: F) -> &'static T { + // Execute the initialization up front, *then* move it into our slot, + // just in case initialization fails. + let value = init(); + let ptr = self.inner.get(); + + // note that this can in theory just be `*ptr = Some(value)`, but due to + // the compiler will currently codegen that pattern with something like: + // + // ptr::drop_in_place(ptr) + // ptr::write(ptr, Some(value)) + // + // Due to this pattern it's possible for the destructor of the value in + // `ptr` (e.g., if this is being recursively initialized) to re-access + // TLS, in which case there will be a `&` and `&mut` pointer to the same + // value (an aliasing violation). To avoid setting the "I'm running a + // destructor" flag we just use `mem::replace` which should sequence the + // operations a little differently and make this safe to call. + mem::replace(&mut *ptr, Some(value)); + + // After storing `Some` we want to get a reference to the contents of + // what we just stored. While we could use `unwrap` here and it should + // always work it empirically doesn't seem to always get optimized away, + // which means that using something like `try_with` can pull in + // panicking code and cause a large size bloat. + match *ptr { + Some(ref x) => x, + None => hint::unreachable_unchecked(), + } + } + + #[allow(unused)] + pub unsafe fn take(&mut self) -> Option<T> { + (*self.inner.get()).take() } } } @@ -309,17 +327,17 @@ impl<T: 'static> LocalKey<T> { #[doc(hidden)] #[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))] pub mod statik { - use crate::cell::UnsafeCell; + use super::lazy::LazyKeyInner; use crate::fmt; pub struct Key<T> { - inner: UnsafeCell<Option<T>>, + inner: LazyKeyInner<T>, } unsafe impl<T> Sync for Key<T> { } impl<T> fmt::Debug for Key<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Key { .. }") } } @@ -327,12 +345,16 @@ pub mod statik { impl<T> Key<T> { pub const fn new() -> Key<T> { Key { - inner: UnsafeCell::new(None), + inner: LazyKeyInner::new(), } } - pub unsafe fn get(&self) -> Option<&'static UnsafeCell<Option<T>>> { - Some(&*(&self.inner as *const _)) + pub unsafe fn get(&self, init: fn() -> T) -> Option<&'static T> { + let value = match self.inner.get() { + Some(ref value) => value, + None => self.inner.initialize(init), + }; + Some(value) } } } @@ -340,23 +362,42 @@ pub mod statik { #[doc(hidden)] #[cfg(target_thread_local)] pub mod fast { - use crate::cell::{Cell, UnsafeCell}; + use super::lazy::LazyKeyInner; + use crate::cell::Cell; use crate::fmt; use crate::mem; - use crate::ptr; - use crate::sys::fast_thread_local::{register_dtor, requires_move_before_drop}; + use crate::sys::fast_thread_local::register_dtor; + + #[derive(Copy, Clone)] + enum DtorState { + Unregistered, + Registered, + RunningOrHasRun, + } + // This data structure has been carefully constructed so that the fast path + // only contains one branch on x86. That optimization is necessary to avoid + // duplicated tls lookups on OSX. + // + // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 pub struct Key<T> { - inner: UnsafeCell<Option<T>>, + // If `LazyKeyInner::get` returns `None`, that indicates either: + // * The value has never been initialized + // * The value is being recursively initialized + // * The value has already been destroyed or is being destroyed + // To determine which kind of `None`, check `dtor_state`. + // + // This is very optimizer friendly for the fast path - initialized but + // not yet dropped. + inner: LazyKeyInner<T>, // Metadata to keep track of the state of the destructor. Remember that - // these variables are thread-local, not global. - dtor_registered: Cell<bool>, - dtor_running: Cell<bool>, + // this variable is thread-local, not global. + dtor_state: Cell<DtorState>, } impl<T> fmt::Debug for Key<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Key { .. }") } } @@ -364,54 +405,75 @@ pub mod fast { impl<T> Key<T> { pub const fn new() -> Key<T> { Key { - inner: UnsafeCell::new(None), - dtor_registered: Cell::new(false), - dtor_running: Cell::new(false) + inner: LazyKeyInner::new(), + dtor_state: Cell::new(DtorState::Unregistered), } } - pub unsafe fn get(&self) -> Option<&'static UnsafeCell<Option<T>>> { - if mem::needs_drop::<T>() && self.dtor_running.get() { - return None + pub unsafe fn get<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> { + match self.inner.get() { + Some(val) => Some(val), + None => self.try_initialize(init), } - self.register_dtor(); - Some(&*(&self.inner as *const _)) } - unsafe fn register_dtor(&self) { - if !mem::needs_drop::<T>() || self.dtor_registered.get() { - return + // `try_initialize` is only called once per fast thread local variable, + // except in corner cases where thread_local dtors reference other + // thread_local's, or it is being recursively initialized. + // + // Macos: Inlining this function can cause two `tlv_get_addr` calls to + // be performed for every call to `Key::get`. The #[cold] hint makes + // that less likely. + // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 + #[cold] + unsafe fn try_initialize<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> { + if !mem::needs_drop::<T>() || self.try_register_dtor() { + Some(self.inner.initialize(init)) + } else { + None } + } - register_dtor(self as *const _ as *mut u8, - destroy_value::<T>); - self.dtor_registered.set(true); + // `try_register_dtor` is only called once per fast thread local + // variable, except in corner cases where thread_local dtors reference + // other thread_local's, or it is being recursively initialized. + unsafe fn try_register_dtor(&self) -> bool { + match self.dtor_state.get() { + DtorState::Unregistered => { + // dtor registration happens before initialization. + register_dtor(self as *const _ as *mut u8, + destroy_value::<T>); + self.dtor_state.set(DtorState::Registered); + true + } + DtorState::Registered => { + // recursively initialized + true + } + DtorState::RunningOrHasRun => { + false + } + } } } unsafe extern fn destroy_value<T>(ptr: *mut u8) { let ptr = ptr as *mut Key<T>; - // Right before we run the user destructor be sure to flag the - // destructor as running for this thread so calls to `get` will return - // `None`. - (*ptr).dtor_running.set(true); - // Some implementations may require us to move the value before we drop - // it as it could get re-initialized in-place during destruction. - // - // Hence, we use `ptr::read` on those platforms (to move to a "safe" - // location) instead of drop_in_place. - if requires_move_before_drop() { - ptr::read((*ptr).inner.get()); - } else { - ptr::drop_in_place((*ptr).inner.get()); - } + // Right before we run the user destructor be sure to set the + // `Option<T>` to `None`, and `dtor_state` to `RunningOrHasRun`. This + // causes future calls to `get` to run `try_initialize_drop` again, + // which will now fail, and return `None`. + let value = (*ptr).inner.take(); + (*ptr).dtor_state.set(DtorState::RunningOrHasRun); + drop(value); } } #[doc(hidden)] pub mod os { - use crate::cell::{Cell, UnsafeCell}; + use super::lazy::LazyKeyInner; + use crate::cell::Cell; use crate::fmt; use crate::marker; use crate::ptr; @@ -424,7 +486,7 @@ pub mod os { } impl<T> fmt::Debug for Key<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Key { .. }") } } @@ -432,8 +494,8 @@ pub mod os { unsafe impl<T> Sync for Key<T> { } struct Value<T: 'static> { + inner: LazyKeyInner<T>, key: &'static Key<T>, - value: UnsafeCell<Option<T>>, } impl<T: 'static> Key<T> { @@ -444,24 +506,43 @@ pub mod os { } } - pub unsafe fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> { + pub unsafe fn get(&'static self, init: fn() -> T) -> Option<&'static T> { let ptr = self.os.get() as *mut Value<T>; - if !ptr.is_null() { - if ptr as usize == 1 { - return None + if ptr as usize > 1 { + match (*ptr).inner.get() { + Some(ref value) => return Some(value), + None => {}, } - return Some(&(*ptr).value); } + self.try_initialize(init) + } - // If the lookup returned null, we haven't initialized our own - // local copy, so do that now. - let ptr: Box<Value<T>> = box Value { - key: self, - value: UnsafeCell::new(None), + // `try_initialize` is only called once per os thread local variable, + // except in corner cases where thread_local dtors reference other + // thread_local's, or it is being recursively initialized. + unsafe fn try_initialize(&'static self, init: fn() -> T) -> Option<&'static T> { + let ptr = self.os.get() as *mut Value<T>; + if ptr as usize == 1 { + // destructor is running + return None + } + + let ptr = if ptr.is_null() { + // If the lookup returned null, we haven't initialized our own + // local copy, so do that now. + let ptr: Box<Value<T>> = box Value { + inner: LazyKeyInner::new(), + key: self, + }; + let ptr = Box::into_raw(ptr); + self.os.set(ptr as *mut u8); + ptr + } else { + // recursive initialization + ptr }; - let ptr = Box::into_raw(ptr); - self.os.set(ptr as *mut u8); - Some(&(*ptr).value) + + Some((*ptr).inner.initialize(init)) } } @@ -530,7 +611,7 @@ mod tests { thread::spawn(|| { assert!(FOO.try_with(|_| ()).is_ok()); - }).join().ok().unwrap(); + }).join().ok().expect("thread panicked"); } #[test] @@ -584,7 +665,7 @@ mod tests { thread::spawn(move|| { drop(S1); - }).join().ok().unwrap(); + }).join().ok().expect("thread panicked"); } #[test] @@ -600,7 +681,7 @@ mod tests { thread::spawn(move|| unsafe { K1.with(|s| *s.get() = Some(S1)); - }).join().ok().unwrap(); + }).join().ok().expect("thread panicked"); } // Note that this test will deadlock if TLS destructors aren't run (this diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 08f0aa2f0d2..764041d2f42 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -157,12 +157,12 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::any::Any; -use crate::boxed::FnBox; use crate::cell::UnsafeCell; use crate::ffi::{CStr, CString}; use crate::fmt; use crate::io; use crate::mem; +use crate::num::NonZeroU64; use crate::panic; use crate::panicking; use crate::str; @@ -269,7 +269,7 @@ impl Builder { /// /// let builder = thread::Builder::new() /// .name("foo".into()) - /// .stack_size(10); + /// .stack_size(32 * 1024); /// /// let handler = builder.spawn(|| { /// // thread code @@ -443,6 +443,7 @@ impl Builder { /// [`Builder::spawn`]: ../../std/thread/struct.Builder.html#method.spawn /// [`io::Result`]: ../../std/io/type.Result.html /// [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html + /// [`JoinHandle::join`]: ../../std/thread/struct.JoinHandle.html#method.join #[unstable(feature = "thread_spawn_unchecked", issue = "55132")] pub unsafe fn spawn_unchecked<'a, F, T>(self, f: F) -> io::Result<JoinHandle<T>> where F: FnOnce() -> T, F: Send + 'a, T: Send + 'a @@ -487,7 +488,9 @@ impl Builder { // returning. native: Some(imp::Thread::new( stack_size, - mem::transmute::<Box<dyn FnBox() + 'a>, Box<dyn FnBox() + 'static>>(Box::new(main)) + mem::transmute::<Box<dyn FnOnce() + 'a>, Box<dyn FnOnce() + 'static>>(Box::new( + main, + )), )?), thread: my_thread, packet: Packet(my_packet), @@ -1036,7 +1039,7 @@ pub fn park_timeout(dur: Duration) { /// [`Thread`]: ../../std/thread/struct.Thread.html #[stable(feature = "thread_id", since = "1.19.0")] #[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] -pub struct ThreadId(u64); +pub struct ThreadId(NonZeroU64); impl ThreadId { // Generate a new unique thread ID. @@ -1044,7 +1047,7 @@ impl ThreadId { // We never call `GUARD.init()`, so it is UB to attempt to // acquire this mutex reentrantly! static GUARD: mutex::Mutex = mutex::Mutex::new(); - static mut COUNTER: u64 = 0; + static mut COUNTER: u64 = 1; unsafe { let _guard = GUARD.lock(); @@ -1058,7 +1061,7 @@ impl ThreadId { let id = COUNTER; COUNTER += 1; - ThreadId(id) + ThreadId(NonZeroU64::new(id).unwrap()) } } } @@ -1255,8 +1258,11 @@ impl Thread { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for Thread { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self.name(), f) + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Thread") + .field("id", &self.id()) + .field("name", &self.name()) + .finish() } } @@ -1465,7 +1471,7 @@ impl<T> IntoInner<imp::Thread> for JoinHandle<T> { #[stable(feature = "std_debug", since = "1.16.0")] impl<T> fmt::Debug for JoinHandle<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("JoinHandle { .. }") } } @@ -1484,9 +1490,10 @@ fn _assert_sync_and_send() { mod tests { use super::Builder; use crate::any::Any; + use crate::mem; use crate::sync::mpsc::{channel, Sender}; use crate::result; - use crate::thread; + use crate::thread::{self, ThreadId}; use crate::time::Duration; use crate::u32; @@ -1497,7 +1504,7 @@ mod tests { fn test_unnamed_thread() { thread::spawn(move|| { assert!(thread::current().name().is_none()); - }).join().ok().unwrap(); + }).join().ok().expect("thread panicked"); } #[test] @@ -1691,6 +1698,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn test_park_timeout_unpark_not_called() { for _ in 0..10 { thread::park_timeout(Duration::from_millis(10)); @@ -1698,6 +1706,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn test_park_timeout_unpark_called_other_thread() { for _ in 0..10 { let th = thread::current(); @@ -1712,11 +1721,17 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn sleep_ms_smoke() { thread::sleep(Duration::from_millis(2)); } #[test] + fn test_size_of_option_thread_id() { + assert_eq!(mem::size_of::<Option<ThreadId>>(), mem::size_of::<ThreadId>()); + } + + #[test] fn test_thread_id_equal() { assert!(thread::current().id() == thread::current().id()); } @@ -1727,6 +1742,6 @@ mod tests { assert!(thread::current().id() != spawned_id); } - // NOTE: the corresponding test for stderr is in run-pass/thread-stderr, due + // NOTE: the corresponding test for stderr is in ui/thread-stderr, due // to the test harness apparently interfering with stderr configuration. } |
