File tree 2 files changed +67
-1
lines changed
2 files changed +67
-1
lines changed Original file line number Diff line number Diff line change 13
13
#![ unstable( feature = "thread_local_internals" , issue = "0" ) ]
14
14
15
15
use cell:: UnsafeCell ;
16
+ use mem;
16
17
17
18
// Sure wish we had macro hygiene, no?
18
19
#[ doc( hidden) ]
@@ -226,7 +227,21 @@ impl<T: 'static> LocalKey<T> {
226
227
// just in case initialization fails.
227
228
let value = ( self . init ) ( ) ;
228
229
let ptr = slot. get ( ) ;
229
- * ptr = Some ( value) ;
230
+
231
+ // note that this can in theory just be `*ptr = Some(value)`, but due to
232
+ // the compiler will currently codegen that pattern with something like:
233
+ //
234
+ // ptr::drop_in_place(ptr)
235
+ // ptr::write(ptr, Some(value))
236
+ //
237
+ // Due to this pattern it's possible for the destructor of the value in
238
+ // `ptr` (e.g. if this is being recursively initialized) to re-access
239
+ // TLS, in which case there will be a `&` and `&mut` pointer to the same
240
+ // value (an aliasing violation). To avoid setting the "I'm running a
241
+ // destructor" flag we just use `mem::replace` which should sequence the
242
+ // operations a little differently and make this safe to call.
243
+ mem:: replace ( & mut * ptr, Some ( value) ) ;
244
+
230
245
( * ptr) . as_ref ( ) . unwrap ( )
231
246
}
232
247
Original file line number Diff line number Diff line change
1
+ // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2
+ // file at the top-level directory of this distribution and at
3
+ // http://rust-lang.org/COPYRIGHT.
4
+ //
5
+ // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6
+ // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7
+ // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8
+ // option. This file may not be copied, modified, or distributed
9
+ // except according to those terms.
10
+
11
+ #![ feature( thread_local_state) ]
12
+
13
+ use std:: thread:: { self , LocalKeyState } ;
14
+ use std:: sync:: atomic:: { AtomicUsize , Ordering , ATOMIC_USIZE_INIT } ;
15
+
16
+ struct Foo { cnt : usize }
17
+
18
+ thread_local ! ( static FOO : Foo = Foo :: init( ) ) ;
19
+
20
+ static CNT : AtomicUsize = ATOMIC_USIZE_INIT ;
21
+
22
+ impl Foo {
23
+ fn init ( ) -> Foo {
24
+ let cnt = CNT . fetch_add ( 1 , Ordering :: SeqCst ) ;
25
+ if cnt == 0 {
26
+ FOO . with ( |_| { } ) ;
27
+ }
28
+ Foo { cnt : cnt }
29
+ }
30
+ }
31
+
32
+ impl Drop for Foo {
33
+ fn drop ( & mut self ) {
34
+ if self . cnt == 1 {
35
+ FOO . with ( |foo| assert_eq ! ( foo. cnt, 0 ) ) ;
36
+ } else {
37
+ assert_eq ! ( self . cnt, 0 ) ;
38
+ match FOO . state ( ) {
39
+ LocalKeyState :: Valid => panic ! ( "should not be in valid state" ) ,
40
+ LocalKeyState :: Uninitialized |
41
+ LocalKeyState :: Destroyed => { }
42
+ }
43
+ }
44
+ }
45
+ }
46
+
47
+ fn main ( ) {
48
+ thread:: spawn ( || {
49
+ FOO . with ( |_| { } ) ;
50
+ } ) . join ( ) . unwrap ( ) ;
51
+ }
You can’t perform that action at this time.
0 commit comments