Skip to content

Commit be11aa6

Browse files
committed
std: Second pass stabilization for thread_local
This commit performs a second pass over the `std::thread_local` module. Most of the functionality remains explicitly unstable, but the specific actions taken were: * `thread_local` is now stable * `thread_local!` is now stable * `thread_local::Key` is now stable * `thread_local::Key::with` is now stable * `thread_local::Key::destroyed` is deprecated in favor of a more general `state` function * `thread_local::Key::state` was added to query the three states that a key can be in: uninitialized, valid, or destroyed. This function, and the corresponding `State` enum, are both marked unstable as we may wish to expand it later on. * `thread_local::scoped` is entirely unstable. There hasn't been a whole lot of usage of this module in the standard distribution, so it remains unstable at this time. Note that while the structure `Key` is marked stable, it is currently forced to expose all of its implementation details due to the use of construction-via-macro. The use of construction-via-macro is currently required in order to place the `#[thread_local]` attribute on static in a platform-specific manner. These stability attributes were assigned assuming that it will be acceptable to tweak the implementation of `Key` in the future.
1 parent 10d99a9 commit be11aa6

File tree

3 files changed

+145
-44
lines changed

3 files changed

+145
-44
lines changed

src/libstd/sys/common/thread_info.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010

1111
use core::prelude::*;
1212

13-
use thread::Thread;
1413
use cell::RefCell;
1514
use string::String;
15+
use thread::Thread;
16+
use thread_local::State;
1617

1718
struct ThreadInfo {
1819
// This field holds the known bounds of the stack in (lo, hi)
@@ -27,7 +28,7 @@ thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = RefCell::new(N
2728

2829
impl ThreadInfo {
2930
fn with<R>(f: |&mut ThreadInfo| -> R) -> R {
30-
if THREAD_INFO.destroyed() {
31+
if THREAD_INFO.state() == State::Destroyed {
3132
panic!("Use of std::thread::Thread::current() is not possible after \
3233
the thread's local data has been destroyed");
3334
}

src/libstd/thread_local/mod.rs

Lines changed: 127 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,23 @@
3535
//! `Cell` or `RefCell` types.
3636
3737
#![macro_escape]
38-
#![experimental]
38+
#![stable]
3939

4040
use prelude::*;
4141

4242
use cell::UnsafeCell;
4343

44-
// Sure wish we had macro hygiene, no?
45-
#[doc(hidden)] pub use self::imp::Key as KeyInner;
46-
#[doc(hidden)] pub use self::imp::destroy_value;
47-
#[doc(hidden)] pub use sys_common::thread_local::INIT_INNER as OS_INIT_INNER;
48-
#[doc(hidden)] pub use sys_common::thread_local::StaticKey as OsStaticKey;
49-
5044
pub mod scoped;
5145

46+
// Sure wish we had macro hygiene, no?
47+
#[doc(hidden)]
48+
pub mod __impl {
49+
pub use super::imp::Key as KeyInner;
50+
pub use super::imp::destroy_value;
51+
pub use sys_common::thread_local::INIT_INNER as OS_INIT_INNER;
52+
pub use sys_common::thread_local::StaticKey as OsStaticKey;
53+
}
54+
5255
/// A thread local storage key which owns its contents.
5356
///
5457
/// This key uses the fastest possible implementation available to it for the
@@ -90,6 +93,7 @@ pub mod scoped;
9093
/// assert_eq!(*f.borrow(), 2);
9194
/// });
9295
/// ```
96+
#[stable]
9397
pub struct Key<T> {
9498
// The key itself may be tagged with #[thread_local], and this `Key` is
9599
// stored as a `static`, and it's not valid for a static to reference the
@@ -100,7 +104,7 @@ pub struct Key<T> {
100104
// This is trivially devirtualizable by LLVM because we never store anything
101105
// to this field and rustc can declare the `static` as constant as well.
102106
#[doc(hidden)]
103-
pub inner: fn() -> &'static KeyInner<UnsafeCell<Option<T>>>,
107+
pub inner: fn() -> &'static __impl::KeyInner<UnsafeCell<Option<T>>>,
104108

105109
// initialization routine to invoke to create a value
106110
#[doc(hidden)]
@@ -109,12 +113,12 @@ pub struct Key<T> {
109113

110114
/// Declare a new thread local storage key of type `std::thread_local::Key`.
111115
#[macro_export]
112-
#[doc(hidden)]
116+
#[stable]
113117
macro_rules! thread_local {
114118
(static $name:ident: $t:ty = $init:expr) => (
115119
static $name: ::std::thread_local::Key<$t> = {
116120
use std::cell::UnsafeCell as __UnsafeCell;
117-
use std::thread_local::KeyInner as __KeyInner;
121+
use std::thread_local::__impl::KeyInner as __KeyInner;
118122
use std::option::Option as __Option;
119123
use std::option::Option::None as __None;
120124

@@ -131,7 +135,7 @@ macro_rules! thread_local {
131135
(pub static $name:ident: $t:ty = $init:expr) => (
132136
pub static $name: ::std::thread_local::Key<$t> = {
133137
use std::cell::UnsafeCell as __UnsafeCell;
134-
use std::thread_local::KeyInner as __KeyInner;
138+
use std::thread_local::__impl::KeyInner as __KeyInner;
135139
use std::option::Option as __Option;
136140
use std::option::Option::None as __None;
137141

@@ -168,46 +172,77 @@ macro_rules! thread_local {
168172
// itself. Woohoo.
169173

170174
#[macro_export]
175+
#[doc(hidden)]
171176
macro_rules! __thread_local_inner {
172177
(static $name:ident: $t:ty = $init:expr) => (
173178
#[cfg_attr(any(target_os = "macos", target_os = "linux"), thread_local)]
174-
static $name: ::std::thread_local::KeyInner<$t> =
179+
static $name: ::std::thread_local::__impl::KeyInner<$t> =
175180
__thread_local_inner!($init, $t);
176181
);
177182
(pub static $name:ident: $t:ty = $init:expr) => (
178183
#[cfg_attr(any(target_os = "macos", target_os = "linux"), thread_local)]
179-
pub static $name: ::std::thread_local::KeyInner<$t> =
184+
pub static $name: ::std::thread_local::__impl::KeyInner<$t> =
180185
__thread_local_inner!($init, $t);
181186
);
182187
($init:expr, $t:ty) => ({
183188
#[cfg(any(target_os = "macos", target_os = "linux"))]
184-
const INIT: ::std::thread_local::KeyInner<$t> = {
185-
::std::thread_local::KeyInner {
189+
const _INIT: ::std::thread_local::__impl::KeyInner<$t> = {
190+
::std::thread_local::__impl::KeyInner {
186191
inner: ::std::cell::UnsafeCell { value: $init },
187192
dtor_registered: ::std::cell::UnsafeCell { value: false },
188193
dtor_running: ::std::cell::UnsafeCell { value: false },
189194
}
190195
};
191196

192197
#[cfg(all(not(any(target_os = "macos", target_os = "linux"))))]
193-
const INIT: ::std::thread_local::KeyInner<$t> = {
198+
const _INIT: ::std::thread_local::__impl::KeyInner<$t> = {
194199
unsafe extern fn __destroy(ptr: *mut u8) {
195-
::std::thread_local::destroy_value::<$t>(ptr);
200+
::std::thread_local::__impl::destroy_value::<$t>(ptr);
196201
}
197202

198-
::std::thread_local::KeyInner {
203+
::std::thread_local::__impl::KeyInner {
199204
inner: ::std::cell::UnsafeCell { value: $init },
200-
os: ::std::thread_local::OsStaticKey {
201-
inner: ::std::thread_local::OS_INIT_INNER,
205+
os: ::std::thread_local::__impl::OsStaticKey {
206+
inner: ::std::thread_local::__impl::OS_INIT_INNER,
202207
dtor: ::std::option::Option::Some(__destroy as unsafe extern fn(*mut u8)),
203208
},
204209
}
205210
};
206211

207-
INIT
212+
_INIT
208213
});
209214
}
210215

216+
/// Indicator of the state of a thread local storage key.
217+
#[unstable = "state querying was recently added"]
218+
#[deriving(Eq, PartialEq, Copy)]
219+
pub enum State {
220+
/// All keys are in this state whenever a thread starts. Keys will
221+
/// transition to the `Valid` state once the first call to `with` happens
222+
/// and the initialization expression succeeds.
223+
///
224+
/// Keys in the `Uninitialized` state will yield a reference to the closure
225+
/// passed to `with` so long as the initialization routine does not panic.
226+
Uninitialized,
227+
228+
/// Once a key has been accessed successfully, it will enter the `Valid`
229+
/// state. Keys in the `Valid` state will remain so until the thread exits,
230+
/// at which point the destructor will be run and the key will enter the
231+
/// `Destroyed` state.
232+
///
233+
/// Keys in the `Valid` state will be guaranteed to yield a reference to the
234+
/// closure passed to `with`.
235+
Valid,
236+
237+
/// When a thread exits, the destructors for keys will be run (if
238+
/// necessary). While a destructor is running, and possibly after a
239+
/// destructor has run, a key is in the `Destroyed` state.
240+
///
241+
/// Keys in the `Destroyed` states will trigger a panic when accessed via
242+
/// `with`.
243+
Destroyed,
244+
}
245+
211246
impl<T: 'static> Key<T> {
212247
/// Acquire a reference to the value in this TLS key.
213248
///
@@ -219,6 +254,7 @@ impl<T: 'static> Key<T> {
219254
/// This function will `panic!()` if the key currently has its
220255
/// destructor running, and it **may** panic if the destructor has
221256
/// previously been run for this thread.
257+
#[stable]
222258
pub fn with<F, R>(&'static self, f: F) -> R
223259
where F: FnOnce(&T) -> R {
224260
let slot = (self.inner)();
@@ -233,17 +269,52 @@ impl<T: 'static> Key<T> {
233269
}
234270

235271
unsafe fn init(&self, slot: &UnsafeCell<Option<T>>) -> &T {
236-
*slot.get() = Some((self.init)());
237-
(*slot.get()).as_ref().unwrap()
272+
// Execute the initialization up front, *then* move it into our slot,
273+
// just in case initialization fails.
274+
let value = (self.init)();
275+
let ptr = slot.get();
276+
*ptr = Some(value);
277+
(*ptr).as_ref().unwrap()
238278
}
239279

240-
/// Test this TLS key to determine whether its value has been destroyed for
241-
/// the current thread or not.
280+
/// Query the current state of this key.
281+
///
282+
/// A key is initially in the `Uninitialized` state whenever a thread
283+
/// starts. It will remain in this state up until the first call to `with`
284+
/// within a thread has run the initialization expression successfully.
285+
///
286+
/// Once the initialization expression succeeds, the key transitions to the
287+
/// `Valid` state which will guarantee that future calls to `with` will
288+
/// succeed within the thread.
242289
///
243-
/// This will not initialize the key if it is not already initialized.
244-
pub fn destroyed(&'static self) -> bool {
245-
unsafe { (self.inner)().get().is_none() }
290+
/// When a thread exits, each key will be destroyed in turn, and as keys are
291+
/// destroyed they will enter the `Destroyed` state just before the
292+
/// destructor starts to run. Keys may remain in the `Destroyed` state after
293+
/// destruction has completed. Keys without destructors (e.g. with types
294+
/// that are `Copy`), may never enter the `Destroyed` state.
295+
///
296+
/// Keys in the `Uninitialized` can be accessed so long as the
297+
/// initialization does not panic. Keys in the `Valid` state are guaranteed
298+
/// to be able to be accessed. Keys in the `Destroyed` state will panic on
299+
/// any call to `with`.
300+
#[unstable = "state querying was recently added"]
301+
pub fn state(&'static self) -> State {
302+
unsafe {
303+
match (self.inner)().get() {
304+
Some(cell) => {
305+
match *cell.get() {
306+
Some(..) => State::Valid,
307+
None => State::Uninitialized,
308+
}
309+
}
310+
None => State::Destroyed,
311+
}
312+
}
246313
}
314+
315+
/// Deprecated
316+
#[deprecated = "function renamed to state() and returns more info"]
317+
pub fn destroyed(&'static self) -> bool { self.state() == State::Destroyed }
247318
}
248319

249320
#[cfg(any(target_os = "macos", target_os = "linux"))]
@@ -456,6 +527,7 @@ mod tests {
456527
use prelude::*;
457528

458529
use cell::UnsafeCell;
530+
use super::State;
459531
use thread::Thread;
460532

461533
struct Foo(Sender<()>);
@@ -489,6 +561,29 @@ mod tests {
489561
});
490562
}
491563

564+
#[test]
565+
fn states() {
566+
struct Foo;
567+
impl Drop for Foo {
568+
fn drop(&mut self) {
569+
assert!(FOO.state() == State::Destroyed);
570+
}
571+
}
572+
fn foo() -> Foo {
573+
assert!(FOO.state() == State::Uninitialized);
574+
Foo
575+
}
576+
thread_local!(static FOO: Foo = foo());
577+
578+
Thread::spawn(|| {
579+
assert!(FOO.state() == State::Uninitialized);
580+
FOO.with(|_| {
581+
assert!(FOO.state() == State::Valid);
582+
});
583+
assert!(FOO.state() == State::Valid);
584+
}).join().ok().unwrap();
585+
}
586+
492587
#[test]
493588
fn smoke_dtor() {
494589
thread_local!(static FOO: UnsafeCell<Option<Foo>> = UnsafeCell {
@@ -521,7 +616,7 @@ mod tests {
521616
fn drop(&mut self) {
522617
unsafe {
523618
HITS += 1;
524-
if K2.destroyed() {
619+
if K2.state() == State::Destroyed {
525620
assert_eq!(HITS, 3);
526621
} else {
527622
if HITS == 1 {
@@ -537,7 +632,7 @@ mod tests {
537632
fn drop(&mut self) {
538633
unsafe {
539634
HITS += 1;
540-
assert!(!K1.destroyed());
635+
assert!(K1.state() != State::Destroyed);
541636
assert_eq!(HITS, 2);
542637
K1.with(|s| *s.get() = Some(S1));
543638
}
@@ -558,7 +653,7 @@ mod tests {
558653

559654
impl Drop for S1 {
560655
fn drop(&mut self) {
561-
assert!(K1.destroyed());
656+
assert!(K1.state() == State::Destroyed);
562657
}
563658
}
564659

@@ -581,7 +676,7 @@ mod tests {
581676
fn drop(&mut self) {
582677
let S1(ref tx) = *self;
583678
unsafe {
584-
if !K2.destroyed() {
679+
if K2.state() != State::Destroyed {
585680
K2.with(|s| *s.get() = Some(Foo(tx.clone())));
586681
}
587682
}

src/libstd/thread_local/scoped.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,17 @@
3939
//! ```
4040
4141
#![macro_escape]
42+
#![unstable = "scoped TLS has yet to have wide enough use to fully consider \
43+
stabilizing its interface"]
4244

4345
use prelude::*;
4446

4547
// macro hygiene sure would be nice, wouldn't it?
46-
#[doc(hidden)] pub use self::imp::KeyInner;
47-
#[doc(hidden)] pub use sys_common::thread_local::INIT as OS_INIT;
48+
#[doc(hidden)]
49+
pub mod __impl {
50+
pub use super::imp::KeyInner;
51+
pub use sys_common::thread_local::INIT as OS_INIT;
52+
}
4853

4954
/// Type representing a thread local storage key corresponding to a reference
5055
/// to the type parameter `T`.
@@ -53,7 +58,7 @@ use prelude::*;
5358
/// type `T` scoped to a particular lifetime. Keys provides two methods, `set`
5459
/// and `with`, both of which currently use closures to control the scope of
5560
/// their contents.
56-
pub struct Key<T> { #[doc(hidden)] pub inner: KeyInner<T> }
61+
pub struct Key<T> { #[doc(hidden)] pub inner: __impl::KeyInner<T> }
5762

5863
/// Declare a new scoped thread local storage key.
5964
///
@@ -88,21 +93,21 @@ macro_rules! __scoped_thread_local_inner {
8893
use std::thread_local::scoped::Key as __Key;
8994

9095
#[cfg(not(any(windows, target_os = "android", target_os = "ios")))]
91-
const INIT: __Key<$t> = __Key {
92-
inner: ::std::thread_local::scoped::KeyInner {
96+
const _INIT: __Key<$t> = __Key {
97+
inner: ::std::thread_local::scoped::__impl::KeyInner {
9398
inner: ::std::cell::UnsafeCell { value: 0 as *mut _ },
9499
}
95100
};
96101

97102
#[cfg(any(windows, target_os = "android", target_os = "ios"))]
98-
const INIT: __Key<$t> = __Key {
99-
inner: ::std::thread_local::scoped::KeyInner {
100-
inner: ::std::thread_local::scoped::OS_INIT,
103+
const _INIT: __Key<$t> = __Key {
104+
inner: ::std::thread_local::scoped::__impl::KeyInner {
105+
inner: ::std::thread_local::scoped::__impl::OS_INIT,
101106
marker: ::std::kinds::marker::InvariantType,
102107
}
103108
};
104109

105-
INIT
110+
_INIT
106111
})
107112
}
108113

@@ -139,7 +144,7 @@ impl<T> Key<T> {
139144
F: FnOnce() -> R,
140145
{
141146
struct Reset<'a, T: 'a> {
142-
key: &'a KeyInner<T>,
147+
key: &'a __impl::KeyInner<T>,
143148
val: *mut T,
144149
}
145150
#[unsafe_destructor]

0 commit comments

Comments
 (0)