From c99f4c4c5b9f968b82037cf643b6662b140d9b1f Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 27 Feb 2018 17:00:01 +0100 Subject: [PATCH 1/4] Stabilize LocalKey::try_with --- src/libstd/io/stdio.rs | 5 ++++- src/libstd/thread/local.rs | 28 ++++++++++++++------------- src/libstd/thread/mod.rs | 5 ++++- src/test/run-pass/tls-init-on-init.rs | 1 + 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 831688bb73d1c..f01ca18851d77 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -17,7 +17,9 @@ use io::{self, Initializer, BufReader, LineWriter}; use sync::{Arc, Mutex, MutexGuard}; use sys::stdio; use sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; -use thread::{LocalKey, LocalKeyState}; +use thread::LocalKey; +#[allow(deprecated)] +use thread::LocalKeyState; /// Stdout used by print! and println! macros thread_local! { @@ -668,6 +670,7 @@ pub fn set_print(sink: Option>) -> Option> { /// thread, it will just fall back to the global stream. /// /// However, if the actual I/O causes an error, this function does panic. +#[allow(deprecated)] fn print_to(args: fmt::Arguments, local_s: &'static LocalKey>>>, global_s: fn() -> T, diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index fcbca38a98f0b..b6dbcf8914cbf 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -199,6 +199,7 @@ macro_rules! __thread_local_inner { #[unstable(feature = "thread_local_state", reason = "state querying was recently added", issue = "27716")] +#[rustc_deprecated(since = "1.26.0", reason = "use `LocalKey::try_with` instead")] #[derive(Debug, Eq, PartialEq, Copy, Clone)] pub enum LocalKeyState { /// All keys are in this state whenever a thread starts. Keys will @@ -234,25 +235,19 @@ pub enum LocalKeyState { } /// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with). -#[unstable(feature = "thread_local_state", - reason = "state querying was recently added", - issue = "27716")] +#[stable(feature = "thread_local_try_with", since = "1.26.0")] pub struct AccessError { _private: (), } -#[unstable(feature = "thread_local_state", - reason = "state querying was recently added", - issue = "27716")] +#[stable(feature = "thread_local_try_with", since = "1.26.0")] impl fmt::Debug for AccessError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("AccessError").finish() } } -#[unstable(feature = "thread_local_state", - reason = "state querying was recently added", - issue = "27716")] +#[stable(feature = "thread_local_try_with", since = "1.26.0")] impl fmt::Display for AccessError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt("already destroyed", f) @@ -341,6 +336,8 @@ impl LocalKey { #[unstable(feature = "thread_local_state", reason = "state querying was recently added", issue = "27716")] + #[rustc_deprecated(since = "1.26.0", reason = "use `LocalKey::try_with` instead")] + #[allow(deprecated)] pub fn state(&'static self) -> LocalKeyState { unsafe { match (self.inner)() { @@ -365,11 +362,11 @@ impl LocalKey { /// /// This function will still `panic!()` if the key is uninitialized and the /// key's initializer panics. - #[unstable(feature = "thread_local_state", - reason = "state querying was recently added", - issue = "27716")] + #[stable(feature = "thread_local_try_with", since = "1.26.0")] pub fn try_with(&'static self, f: F) -> Result - where F: FnOnce(&T) -> R { + where + F: FnOnce(&T) -> R, + { unsafe { let slot = (self.inner)().ok_or(AccessError { _private: (), @@ -530,6 +527,7 @@ pub mod os { mod tests { use sync::mpsc::{channel, Sender}; use cell::{Cell, UnsafeCell}; + #[allow(deprecated)] use super::LocalKeyState; use thread; @@ -565,6 +563,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn states() { struct Foo; impl Drop for Foo { @@ -602,6 +601,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn circular() { struct S1; struct S2; @@ -642,6 +642,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn self_referential() { struct S1; thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); @@ -663,6 +664,7 @@ mod tests { // test on macOS. #[test] #[cfg_attr(target_os = "macos", ignore)] + #[allow(deprecated)] fn dtors_in_dtors_in_dtors() { struct S1(Sender<()>); thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index ff121e2d7ee4e..01898679bdcfc 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -191,7 +191,10 @@ use time::Duration; #[macro_use] mod local; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::local::{LocalKey, LocalKeyState, AccessError}; +pub use self::local::{LocalKey, AccessError}; +#[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated)] +pub use self::local::LocalKeyState; // The types used by the thread_local! macro to access TLS keys. Note that there // are two types, the "OS" type and the "fast" type. The OS thread local key diff --git a/src/test/run-pass/tls-init-on-init.rs b/src/test/run-pass/tls-init-on-init.rs index b44c535d3a487..b5b9fb561ae7e 100644 --- a/src/test/run-pass/tls-init-on-init.rs +++ b/src/test/run-pass/tls-init-on-init.rs @@ -11,6 +11,7 @@ // ignore-emscripten no threads support #![feature(thread_local_state)] +#![allow(deprecated)] use std::thread::{self, LocalKeyState}; use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; From 27fae2b24af48041ceea6e04c7f217d3db372164 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 28 Feb 2018 18:59:12 +0100 Subject: [PATCH 2/4] Remove thread_local_state --- src/libstd/io/stdio.rs | 39 +++++++------ src/libstd/thread/local.rs | 111 +++---------------------------------- src/libstd/thread/mod.rs | 3 - 3 files changed, 26 insertions(+), 127 deletions(-) diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index f01ca18851d77..b8fb83ad46597 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -18,8 +18,6 @@ use sync::{Arc, Mutex, MutexGuard}; use sys::stdio; use sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; use thread::LocalKey; -#[allow(deprecated)] -use thread::LocalKeyState; /// Stdout used by print! and println! macros thread_local! { @@ -670,25 +668,26 @@ pub fn set_print(sink: Option>) -> Option> { /// thread, it will just fall back to the global stream. /// /// However, if the actual I/O causes an error, this function does panic. -#[allow(deprecated)] -fn print_to(args: fmt::Arguments, - local_s: &'static LocalKey>>>, - global_s: fn() -> T, - label: &str) where T: Write { - let result = match local_s.state() { - LocalKeyState::Uninitialized | - LocalKeyState::Destroyed => global_s().write_fmt(args), - LocalKeyState::Valid => { - local_s.with(|s| { - if let Ok(mut borrowed) = s.try_borrow_mut() { - if let Some(w) = borrowed.as_mut() { - return w.write_fmt(args); - } - } - global_s().write_fmt(args) - }) +fn print_to( + args: fmt::Arguments, + local_s: &'static LocalKey>>>, + global_s: fn() -> T, + label: &str, +) +where + T: Write, +{ + let result = local_s.try_with(|s| { + if let Ok(mut borrowed) = s.try_borrow_mut() { + if let Some(w) = borrowed.as_mut() { + return w.write_fmt(args); + } } - }; + global_s().write_fmt(args) + }).unwrap_or_else(|_| { + global_s().write_fmt(args) + }); + if let Err(e) = result { panic!("failed printing to {}: {}", label, e); } diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index b6dbcf8914cbf..25fedcb277265 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -195,45 +195,6 @@ macro_rules! __thread_local_inner { } } -/// Indicator of the state of a thread local storage key. -#[unstable(feature = "thread_local_state", - reason = "state querying was recently added", - issue = "27716")] -#[rustc_deprecated(since = "1.26.0", reason = "use `LocalKey::try_with` instead")] -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub enum LocalKeyState { - /// All keys are in this state whenever a thread starts. Keys will - /// transition to the `Valid` state once the first call to [`with`] happens - /// and the initialization expression succeeds. - /// - /// Keys in the `Uninitialized` state will yield a reference to the closure - /// passed to [`with`] so long as the initialization routine does not panic. - /// - /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with - Uninitialized, - - /// Once a key has been accessed successfully, it will enter the `Valid` - /// state. Keys in the `Valid` state will remain so until the thread exits, - /// at which point the destructor will be run and the key will enter the - /// `Destroyed` state. - /// - /// Keys in the `Valid` state will be guaranteed to yield a reference to the - /// closure passed to [`with`]. - /// - /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with - Valid, - - /// When a thread exits, the destructors for keys will be run (if - /// necessary). While a destructor is running, and possibly after a - /// destructor has run, a key is in the `Destroyed` state. - /// - /// Keys in the `Destroyed` states will trigger a panic when accessed via - /// [`with`]. - /// - /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with - Destroyed, -} - /// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with). #[stable(feature = "thread_local_try_with", since = "1.26.0")] pub struct AccessError { @@ -307,51 +268,6 @@ impl LocalKey { (*ptr).as_ref().unwrap() } - /// Query the current state of this key. - /// - /// A key is initially in the `Uninitialized` state whenever a thread - /// starts. It will remain in this state up until the first call to [`with`] - /// within a thread has run the initialization expression successfully. - /// - /// Once the initialization expression succeeds, the key transitions to the - /// `Valid` state which will guarantee that future calls to [`with`] will - /// succeed within the thread. Some keys might skip the `Uninitialized` - /// state altogether and start in the `Valid` state as an optimization - /// (e.g. keys initialized with a constant expression), but no guarantees - /// are made. - /// - /// When a thread exits, each key will be destroyed in turn, and as keys are - /// destroyed they will enter the `Destroyed` state just before the - /// destructor starts to run. Keys may remain in the `Destroyed` state after - /// destruction has completed. Keys without destructors (e.g. with types - /// that are [`Copy`]), may never enter the `Destroyed` state. - /// - /// Keys in the `Uninitialized` state can be accessed so long as the - /// initialization does not panic. Keys in the `Valid` state are guaranteed - /// to be able to be accessed. Keys in the `Destroyed` state will panic on - /// any call to [`with`]. - /// - /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with - /// [`Copy`]: ../../std/marker/trait.Copy.html - #[unstable(feature = "thread_local_state", - reason = "state querying was recently added", - issue = "27716")] - #[rustc_deprecated(since = "1.26.0", reason = "use `LocalKey::try_with` instead")] - #[allow(deprecated)] - pub fn state(&'static self) -> LocalKeyState { - unsafe { - match (self.inner)() { - Some(cell) => { - match *cell.get() { - Some(..) => LocalKeyState::Valid, - None => LocalKeyState::Uninitialized, - } - } - None => LocalKeyState::Destroyed, - } - } - } - /// Acquires a reference to the value in this TLS key. /// /// This will lazily initialize the value if this thread has not referenced @@ -527,8 +443,6 @@ pub mod os { mod tests { use sync::mpsc::{channel, Sender}; use cell::{Cell, UnsafeCell}; - #[allow(deprecated)] - use super::LocalKeyState; use thread; struct Foo(Sender<()>); @@ -563,26 +477,21 @@ mod tests { } #[test] - #[allow(deprecated)] fn states() { struct Foo; impl Drop for Foo { fn drop(&mut self) { - assert!(FOO.state() == LocalKeyState::Destroyed); + assert!(FOO.try_with(|_| ()).is_err()); } } fn foo() -> Foo { - assert!(FOO.state() == LocalKeyState::Uninitialized); + assert!(FOO.try_with(|_| ()).is_err()); Foo } thread_local!(static FOO: Foo = foo()); thread::spawn(|| { - assert!(FOO.state() == LocalKeyState::Uninitialized); - FOO.with(|_| { - assert!(FOO.state() == LocalKeyState::Valid); - }); - assert!(FOO.state() == LocalKeyState::Valid); + assert!(FOO.try_with(|_| ()).is_ok()); }).join().ok().unwrap(); } @@ -601,7 +510,6 @@ mod tests { } #[test] - #[allow(deprecated)] fn circular() { struct S1; struct S2; @@ -612,8 +520,7 @@ mod tests { impl Drop for S1 { fn drop(&mut self) { unsafe { - HITS += 1; - if K2.state() == LocalKeyState::Destroyed { + if K2.try_with(|_| ()).is_err() { assert_eq!(HITS, 3); } else { if HITS == 1 { @@ -629,7 +536,7 @@ mod tests { fn drop(&mut self) { unsafe { HITS += 1; - assert!(K1.state() != LocalKeyState::Destroyed); + assert!(K1.try_with(|_| ()).is_ok()); assert_eq!(HITS, 2); K1.with(|s| *s.get() = Some(S1)); } @@ -642,14 +549,13 @@ mod tests { } #[test] - #[allow(deprecated)] fn self_referential() { struct S1; thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); impl Drop for S1 { fn drop(&mut self) { - assert!(K1.state() == LocalKeyState::Destroyed); + assert!(K1.try_with(|_| ()).is_err()); } } @@ -664,7 +570,6 @@ mod tests { // test on macOS. #[test] #[cfg_attr(target_os = "macos", ignore)] - #[allow(deprecated)] fn dtors_in_dtors_in_dtors() { struct S1(Sender<()>); thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); @@ -674,9 +579,7 @@ mod tests { fn drop(&mut self) { let S1(ref tx) = *self; unsafe { - if K2.state() != LocalKeyState::Destroyed { - K2.with(|s| *s.get() = Some(Foo(tx.clone()))); - } + let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone()))); } } } diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 01898679bdcfc..71aee673cfe3e 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -192,9 +192,6 @@ use time::Duration; #[stable(feature = "rust1", since = "1.0.0")] pub use self::local::{LocalKey, AccessError}; -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated)] -pub use self::local::LocalKeyState; // The types used by the thread_local! macro to access TLS keys. Note that there // are two types, the "OS" type and the "fast" type. The OS thread local key From 082dd6d7af37ac76d99147ae7242080d4f5c74aa Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 28 Feb 2018 20:50:26 +0100 Subject: [PATCH 3/4] Fix a few run-pass tests --- src/test/run-pass/tls-init-on-init.rs | 11 ++++------- src/test/run-pass/tls-try-with.rs | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/test/run-pass/tls-init-on-init.rs b/src/test/run-pass/tls-init-on-init.rs index b5b9fb561ae7e..48a0d4a99ecc9 100644 --- a/src/test/run-pass/tls-init-on-init.rs +++ b/src/test/run-pass/tls-init-on-init.rs @@ -10,10 +10,9 @@ // ignore-emscripten no threads support -#![feature(thread_local_state)] -#![allow(deprecated)] +#![feature(thread_local_try_with)] -use std::thread::{self, LocalKeyState}; +use std::thread; use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; struct Foo { cnt: usize } @@ -38,10 +37,8 @@ impl Drop for Foo { FOO.with(|foo| assert_eq!(foo.cnt, 0)); } else { assert_eq!(self.cnt, 0); - match FOO.state() { - LocalKeyState::Valid => panic!("should not be in valid state"), - LocalKeyState::Uninitialized | - LocalKeyState::Destroyed => {} + if FOO.try_with(|_| ()).is_ok() { + panic!("should not be in valid state"); } } } diff --git a/src/test/run-pass/tls-try-with.rs b/src/test/run-pass/tls-try-with.rs index c072ec0679d73..552f4c5e829e1 100644 --- a/src/test/run-pass/tls-try-with.rs +++ b/src/test/run-pass/tls-try-with.rs @@ -10,7 +10,7 @@ // ignore-emscripten no threads support -#![feature(thread_local_state)] +#![feature(thread_local_try_with)] use std::thread; From cb56b2d1522e83c5bb0613abcf78b686e994df9e Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 1 Mar 2018 00:07:27 +0100 Subject: [PATCH 4/4] Fix a bug introduced in previous commit --- src/libstd/io/stdio.rs | 4 ++-- src/libstd/thread/local.rs | 9 +++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index b8fb83ad46597..1f73054e3beed 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -663,8 +663,8 @@ pub fn set_print(sink: Option>) -> Option> { /// /// This function is used to print error messages, so it takes extra /// care to avoid causing a panic when `local_stream` is unusable. -/// For instance, if the TLS key for the local stream is uninitialized -/// or already destroyed, or if the local stream is locked by another +/// For instance, if the TLS key for the local stream is +/// already destroyed, or if the local stream is locked by another /// thread, it will just fall back to the global stream. /// /// However, if the actual I/O causes an error, this function does panic. diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 25fedcb277265..99479bc56eff3 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -272,7 +272,7 @@ impl LocalKey { /// /// This will lazily initialize the value if this thread has not referenced /// this key yet. If the key has been destroyed (which may happen if this is called - /// in a destructor), this function will return a ThreadLocalError. + /// in a destructor), this function will return a `ThreadLocalError`. /// /// # Panics /// @@ -484,11 +484,7 @@ mod tests { assert!(FOO.try_with(|_| ()).is_err()); } } - fn foo() -> Foo { - assert!(FOO.try_with(|_| ()).is_err()); - Foo - } - thread_local!(static FOO: Foo = foo()); + thread_local!(static FOO: Foo = Foo); thread::spawn(|| { assert!(FOO.try_with(|_| ()).is_ok()); @@ -520,6 +516,7 @@ mod tests { impl Drop for S1 { fn drop(&mut self) { unsafe { + HITS += 1; if K2.try_with(|_| ()).is_err() { assert_eq!(HITS, 3); } else {