File tree Expand file tree Collapse file tree 2 files changed +67
-1
lines changed Expand file tree Collapse file tree 2 files changed +67
-1
lines changed Original file line number Diff line number Diff line change 1313#![ unstable( feature = "thread_local_internals" , issue = "0" ) ]
1414
1515use cell:: UnsafeCell ;
16+ use mem;
1617
1718// Sure wish we had macro hygiene, no?
1819#[ doc( hidden) ]
@@ -226,7 +227,21 @@ impl<T: 'static> LocalKey<T> {
226227 // just in case initialization fails.
227228 let value = ( self . init ) ( ) ;
228229 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+
230245 ( * ptr) . as_ref ( ) . unwrap ( )
231246 }
232247
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