35
35
//! `Cell` or `RefCell` types.
36
36
37
37
#![ macro_escape]
38
- #![ experimental ]
38
+ #![ stable ]
39
39
40
40
use prelude:: * ;
41
41
42
42
use cell:: UnsafeCell ;
43
43
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
-
50
44
pub mod scoped;
51
45
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
+
52
55
/// A thread local storage key which owns its contents.
53
56
///
54
57
/// This key uses the fastest possible implementation available to it for the
@@ -90,6 +93,7 @@ pub mod scoped;
90
93
/// assert_eq!(*f.borrow(), 2);
91
94
/// });
92
95
/// ```
96
+ #[ stable]
93
97
pub struct Key < T > {
94
98
// The key itself may be tagged with #[thread_local], and this `Key` is
95
99
// stored as a `static`, and it's not valid for a static to reference the
@@ -100,7 +104,7 @@ pub struct Key<T> {
100
104
// This is trivially devirtualizable by LLVM because we never store anything
101
105
// to this field and rustc can declare the `static` as constant as well.
102
106
#[ doc( hidden) ]
103
- pub inner : fn ( ) -> & ' static KeyInner < UnsafeCell < Option < T > > > ,
107
+ pub inner : fn ( ) -> & ' static __impl :: KeyInner < UnsafeCell < Option < T > > > ,
104
108
105
109
// initialization routine to invoke to create a value
106
110
#[ doc( hidden) ]
@@ -109,12 +113,12 @@ pub struct Key<T> {
109
113
110
114
/// Declare a new thread local storage key of type `std::thread_local::Key`.
111
115
#[ macro_export]
112
- #[ doc ( hidden ) ]
116
+ #[ stable ]
113
117
macro_rules! thread_local {
114
118
( static $name: ident: $t: ty = $init: expr) => (
115
119
static $name: :: std:: thread_local:: Key <$t> = {
116
120
use std:: cell:: UnsafeCell as __UnsafeCell;
117
- use std:: thread_local:: KeyInner as __KeyInner;
121
+ use std:: thread_local:: __impl :: KeyInner as __KeyInner;
118
122
use std:: option:: Option as __Option;
119
123
use std:: option:: Option :: None as __None;
120
124
@@ -131,7 +135,7 @@ macro_rules! thread_local {
131
135
( pub static $name: ident: $t: ty = $init: expr) => (
132
136
pub static $name: :: std:: thread_local:: Key <$t> = {
133
137
use std:: cell:: UnsafeCell as __UnsafeCell;
134
- use std:: thread_local:: KeyInner as __KeyInner;
138
+ use std:: thread_local:: __impl :: KeyInner as __KeyInner;
135
139
use std:: option:: Option as __Option;
136
140
use std:: option:: Option :: None as __None;
137
141
@@ -168,46 +172,77 @@ macro_rules! thread_local {
168
172
// itself. Woohoo.
169
173
170
174
#[ macro_export]
175
+ #[ doc( hidden) ]
171
176
macro_rules! __thread_local_inner {
172
177
( static $name: ident: $t: ty = $init: expr) => (
173
178
#[ 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> =
175
180
__thread_local_inner!( $init, $t) ;
176
181
) ;
177
182
( pub static $name: ident: $t: ty = $init: expr) => (
178
183
#[ 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> =
180
185
__thread_local_inner!( $init, $t) ;
181
186
) ;
182
187
( $init: expr, $t: ty) => ( {
183
188
#[ 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 {
186
191
inner: :: std:: cell:: UnsafeCell { value: $init } ,
187
192
dtor_registered: :: std:: cell:: UnsafeCell { value: false } ,
188
193
dtor_running: :: std:: cell:: UnsafeCell { value: false } ,
189
194
}
190
195
} ;
191
196
192
197
#[ 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> = {
194
199
unsafe extern fn __destroy( ptr: * mut u8 ) {
195
- :: std:: thread_local:: destroy_value:: <$t>( ptr) ;
200
+ :: std:: thread_local:: __impl :: destroy_value:: <$t>( ptr) ;
196
201
}
197
202
198
- :: std:: thread_local:: KeyInner {
203
+ :: std:: thread_local:: __impl :: KeyInner {
199
204
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 ,
202
207
dtor: :: std:: option:: Option :: Some ( __destroy as unsafe extern fn ( * mut u8 ) ) ,
203
208
} ,
204
209
}
205
210
} ;
206
211
207
- INIT
212
+ _INIT
208
213
} ) ;
209
214
}
210
215
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
+
211
246
impl < T : ' static > Key < T > {
212
247
/// Acquire a reference to the value in this TLS key.
213
248
///
@@ -219,6 +254,7 @@ impl<T: 'static> Key<T> {
219
254
/// This function will `panic!()` if the key currently has its
220
255
/// destructor running, and it **may** panic if the destructor has
221
256
/// previously been run for this thread.
257
+ #[ stable]
222
258
pub fn with < F , R > ( & ' static self , f : F ) -> R
223
259
where F : FnOnce ( & T ) -> R {
224
260
let slot = ( self . inner ) ( ) ;
@@ -233,17 +269,52 @@ impl<T: 'static> Key<T> {
233
269
}
234
270
235
271
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 ( )
238
278
}
239
279
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.
242
289
///
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
+ }
246
313
}
314
+
315
+ /// Deprecated
316
+ #[ deprecated = "function renamed to state() and returns more info" ]
317
+ pub fn destroyed ( & ' static self ) -> bool { self . state ( ) == State :: Destroyed }
247
318
}
248
319
249
320
#[ cfg( any( target_os = "macos" , target_os = "linux" ) ) ]
@@ -456,6 +527,7 @@ mod tests {
456
527
use prelude:: * ;
457
528
458
529
use cell:: UnsafeCell ;
530
+ use super :: State ;
459
531
use thread:: Thread ;
460
532
461
533
struct Foo ( Sender < ( ) > ) ;
@@ -489,6 +561,29 @@ mod tests {
489
561
} ) ;
490
562
}
491
563
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
+
492
587
#[ test]
493
588
fn smoke_dtor ( ) {
494
589
thread_local ! ( static FOO : UnsafeCell <Option <Foo >> = UnsafeCell {
@@ -521,7 +616,7 @@ mod tests {
521
616
fn drop ( & mut self ) {
522
617
unsafe {
523
618
HITS += 1 ;
524
- if K2 . destroyed ( ) {
619
+ if K2 . state ( ) == State :: Destroyed {
525
620
assert_eq ! ( HITS , 3 ) ;
526
621
} else {
527
622
if HITS == 1 {
@@ -537,7 +632,7 @@ mod tests {
537
632
fn drop ( & mut self ) {
538
633
unsafe {
539
634
HITS += 1 ;
540
- assert ! ( ! K1 . destroyed ( ) ) ;
635
+ assert ! ( K1 . state ( ) != State :: Destroyed ) ;
541
636
assert_eq ! ( HITS , 2 ) ;
542
637
K1 . with ( |s| * s. get ( ) = Some ( S1 ) ) ;
543
638
}
@@ -558,7 +653,7 @@ mod tests {
558
653
559
654
impl Drop for S1 {
560
655
fn drop ( & mut self ) {
561
- assert ! ( K1 . destroyed ( ) ) ;
656
+ assert ! ( K1 . state ( ) == State :: Destroyed ) ;
562
657
}
563
658
}
564
659
@@ -581,7 +676,7 @@ mod tests {
581
676
fn drop ( & mut self ) {
582
677
let S1 ( ref tx) = * self ;
583
678
unsafe {
584
- if ! K2 . destroyed ( ) {
679
+ if K2 . state ( ) != State :: Destroyed {
585
680
K2 . with ( |s| * s. get ( ) = Some ( Foo ( tx. clone ( ) ) ) ) ;
586
681
}
587
682
}
0 commit comments