]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2014-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 | //! Thread local storage | |
c34b1796 AL |
12 | |
13 | #![unstable(feature = "thread_local_internals")] | |
1a4d82fc JJ |
14 | |
15 | use prelude::v1::*; | |
16 | ||
17 | use cell::UnsafeCell; | |
18 | ||
1a4d82fc JJ |
19 | // Sure wish we had macro hygiene, no? |
20 | #[doc(hidden)] | |
c34b1796 | 21 | #[unstable(feature = "thread_local_internals")] |
1a4d82fc JJ |
22 | pub mod __impl { |
23 | pub use super::imp::Key as KeyInner; | |
24 | pub use super::imp::destroy_value; | |
25 | pub use sys_common::thread_local::INIT_INNER as OS_INIT_INNER; | |
26 | pub use sys_common::thread_local::StaticKey as OsStaticKey; | |
27 | } | |
28 | ||
29 | /// A thread local storage key which owns its contents. | |
30 | /// | |
31 | /// This key uses the fastest possible implementation available to it for the | |
32 | /// target platform. It is instantiated with the `thread_local!` macro and the | |
33 | /// primary method is the `with` method. | |
34 | /// | |
35 | /// The `with` method yields a reference to the contained value which cannot be | |
bd371182 | 36 | /// sent across threads or escape the given closure. |
1a4d82fc JJ |
37 | /// |
38 | /// # Initialization and Destruction | |
39 | /// | |
40 | /// Initialization is dynamically performed on the first call to `with()` | |
41 | /// within a thread, and values support destructors which will be run when a | |
42 | /// thread exits. | |
43 | /// | |
c34b1796 | 44 | /// # Examples |
1a4d82fc JJ |
45 | /// |
46 | /// ``` | |
47 | /// use std::cell::RefCell; | |
85aaf69f | 48 | /// use std::thread; |
1a4d82fc | 49 | /// |
c34b1796 | 50 | /// thread_local!(static FOO: RefCell<u32> = RefCell::new(1)); |
1a4d82fc JJ |
51 | /// |
52 | /// FOO.with(|f| { | |
53 | /// assert_eq!(*f.borrow(), 1); | |
54 | /// *f.borrow_mut() = 2; | |
55 | /// }); | |
56 | /// | |
57 | /// // each thread starts out with the initial value of 1 | |
85aaf69f | 58 | /// thread::spawn(move|| { |
1a4d82fc JJ |
59 | /// FOO.with(|f| { |
60 | /// assert_eq!(*f.borrow(), 1); | |
61 | /// *f.borrow_mut() = 3; | |
62 | /// }); | |
63 | /// }); | |
64 | /// | |
65 | /// // we retain our original value of 2 despite the child thread | |
66 | /// FOO.with(|f| { | |
67 | /// assert_eq!(*f.borrow(), 2); | |
68 | /// }); | |
69 | /// ``` | |
85aaf69f | 70 | #[stable(feature = "rust1", since = "1.0.0")] |
c34b1796 | 71 | pub struct LocalKey<T> { |
1a4d82fc JJ |
72 | // The key itself may be tagged with #[thread_local], and this `Key` is |
73 | // stored as a `static`, and it's not valid for a static to reference the | |
74 | // address of another thread_local static. For this reason we kinda wonkily | |
75 | // work around this by generating a shim function which will give us the | |
76 | // address of the inner TLS key at runtime. | |
77 | // | |
78 | // This is trivially devirtualizable by LLVM because we never store anything | |
79 | // to this field and rustc can declare the `static` as constant as well. | |
80 | #[doc(hidden)] | |
c34b1796 | 81 | #[unstable(feature = "thread_local_internals")] |
1a4d82fc JJ |
82 | pub inner: fn() -> &'static __impl::KeyInner<UnsafeCell<Option<T>>>, |
83 | ||
84 | // initialization routine to invoke to create a value | |
85 | #[doc(hidden)] | |
c34b1796 | 86 | #[unstable(feature = "thread_local_internals")] |
1a4d82fc JJ |
87 | pub init: fn() -> T, |
88 | } | |
89 | ||
c34b1796 | 90 | /// Declare a new thread local storage key of type `std::thread::LocalKey`. |
1a4d82fc | 91 | #[macro_export] |
85aaf69f | 92 | #[stable(feature = "rust1", since = "1.0.0")] |
c34b1796 | 93 | #[allow_internal_unstable] |
1a4d82fc JJ |
94 | macro_rules! thread_local { |
95 | (static $name:ident: $t:ty = $init:expr) => ( | |
c34b1796 | 96 | static $name: ::std::thread::LocalKey<$t> = { |
1a4d82fc | 97 | use std::cell::UnsafeCell as __UnsafeCell; |
9346a6ac | 98 | use std::thread::__local::KeyInner as __KeyInner; |
1a4d82fc JJ |
99 | use std::option::Option as __Option; |
100 | use std::option::Option::None as __None; | |
101 | ||
102 | __thread_local_inner!(static __KEY: __UnsafeCell<__Option<$t>> = { | |
103 | __UnsafeCell { value: __None } | |
104 | }); | |
105 | fn __init() -> $t { $init } | |
106 | fn __getit() -> &'static __KeyInner<__UnsafeCell<__Option<$t>>> { | |
107 | &__KEY | |
108 | } | |
c34b1796 | 109 | ::std::thread::LocalKey { inner: __getit, init: __init } |
1a4d82fc JJ |
110 | }; |
111 | ); | |
112 | (pub static $name:ident: $t:ty = $init:expr) => ( | |
c34b1796 | 113 | pub static $name: ::std::thread::LocalKey<$t> = { |
1a4d82fc | 114 | use std::cell::UnsafeCell as __UnsafeCell; |
9346a6ac | 115 | use std::thread::__local::KeyInner as __KeyInner; |
1a4d82fc JJ |
116 | use std::option::Option as __Option; |
117 | use std::option::Option::None as __None; | |
118 | ||
119 | __thread_local_inner!(static __KEY: __UnsafeCell<__Option<$t>> = { | |
120 | __UnsafeCell { value: __None } | |
121 | }); | |
122 | fn __init() -> $t { $init } | |
123 | fn __getit() -> &'static __KeyInner<__UnsafeCell<__Option<$t>>> { | |
124 | &__KEY | |
125 | } | |
c34b1796 | 126 | ::std::thread::LocalKey { inner: __getit, init: __init } |
1a4d82fc JJ |
127 | }; |
128 | ); | |
129 | } | |
130 | ||
131 | // Macro pain #4586: | |
132 | // | |
133 | // When cross compiling, rustc will load plugins and macros from the *host* | |
134 | // platform before search for macros from the target platform. This is primarily | |
135 | // done to detect, for example, plugins. Ideally the macro below would be | |
136 | // defined once per module below, but unfortunately this means we have the | |
137 | // following situation: | |
138 | // | |
139 | // 1. We compile libstd for x86_64-unknown-linux-gnu, this thread_local!() macro | |
140 | // will inject #[thread_local] statics. | |
141 | // 2. We then try to compile a program for arm-linux-androideabi | |
142 | // 3. The compiler has a host of linux and a target of android, so it loads | |
143 | // macros from the *linux* libstd. | |
144 | // 4. The macro generates a #[thread_local] field, but the android libstd does | |
145 | // not use #[thread_local] | |
146 | // 5. Compile error about structs with wrong fields. | |
147 | // | |
148 | // To get around this, we're forced to inject the #[cfg] logic into the macro | |
149 | // itself. Woohoo. | |
150 | ||
151 | #[macro_export] | |
152 | #[doc(hidden)] | |
c34b1796 | 153 | #[allow_internal_unstable] |
1a4d82fc JJ |
154 | macro_rules! __thread_local_inner { |
155 | (static $name:ident: $t:ty = $init:expr) => ( | |
156 | #[cfg_attr(all(any(target_os = "macos", target_os = "linux"), | |
157 | not(target_arch = "aarch64")), | |
158 | thread_local)] | |
9346a6ac | 159 | static $name: ::std::thread::__local::KeyInner<$t> = |
1a4d82fc JJ |
160 | __thread_local_inner!($init, $t); |
161 | ); | |
162 | (pub static $name:ident: $t:ty = $init:expr) => ( | |
163 | #[cfg_attr(all(any(target_os = "macos", target_os = "linux"), | |
164 | not(target_arch = "aarch64")), | |
165 | thread_local)] | |
9346a6ac | 166 | pub static $name: ::std::thread::__local::KeyInner<$t> = |
1a4d82fc JJ |
167 | __thread_local_inner!($init, $t); |
168 | ); | |
169 | ($init:expr, $t:ty) => ({ | |
170 | #[cfg(all(any(target_os = "macos", target_os = "linux"), not(target_arch = "aarch64")))] | |
9346a6ac AL |
171 | const _INIT: ::std::thread::__local::KeyInner<$t> = { |
172 | ::std::thread::__local::KeyInner { | |
1a4d82fc JJ |
173 | inner: ::std::cell::UnsafeCell { value: $init }, |
174 | dtor_registered: ::std::cell::UnsafeCell { value: false }, | |
175 | dtor_running: ::std::cell::UnsafeCell { value: false }, | |
176 | } | |
177 | }; | |
178 | ||
c34b1796 | 179 | #[allow(trivial_casts)] |
1a4d82fc | 180 | #[cfg(any(not(any(target_os = "macos", target_os = "linux")), target_arch = "aarch64"))] |
9346a6ac AL |
181 | const _INIT: ::std::thread::__local::KeyInner<$t> = { |
182 | ::std::thread::__local::KeyInner { | |
1a4d82fc | 183 | inner: ::std::cell::UnsafeCell { value: $init }, |
9346a6ac AL |
184 | os: ::std::thread::__local::OsStaticKey { |
185 | inner: ::std::thread::__local::OS_INIT_INNER, | |
c34b1796 | 186 | dtor: ::std::option::Option::Some( |
9346a6ac | 187 | ::std::thread::__local::destroy_value::<$t> |
c34b1796 | 188 | ), |
1a4d82fc JJ |
189 | }, |
190 | } | |
191 | }; | |
192 | ||
193 | _INIT | |
194 | }); | |
195 | } | |
196 | ||
197 | /// Indicator of the state of a thread local storage key. | |
85aaf69f SL |
198 | #[unstable(feature = "std_misc", |
199 | reason = "state querying was recently added")] | |
c34b1796 AL |
200 | #[derive(Eq, PartialEq, Copy, Clone)] |
201 | pub enum LocalKeyState { | |
1a4d82fc JJ |
202 | /// All keys are in this state whenever a thread starts. Keys will |
203 | /// transition to the `Valid` state once the first call to `with` happens | |
204 | /// and the initialization expression succeeds. | |
205 | /// | |
206 | /// Keys in the `Uninitialized` state will yield a reference to the closure | |
207 | /// passed to `with` so long as the initialization routine does not panic. | |
208 | Uninitialized, | |
209 | ||
210 | /// Once a key has been accessed successfully, it will enter the `Valid` | |
211 | /// state. Keys in the `Valid` state will remain so until the thread exits, | |
212 | /// at which point the destructor will be run and the key will enter the | |
213 | /// `Destroyed` state. | |
214 | /// | |
215 | /// Keys in the `Valid` state will be guaranteed to yield a reference to the | |
216 | /// closure passed to `with`. | |
217 | Valid, | |
218 | ||
219 | /// When a thread exits, the destructors for keys will be run (if | |
220 | /// necessary). While a destructor is running, and possibly after a | |
221 | /// destructor has run, a key is in the `Destroyed` state. | |
222 | /// | |
223 | /// Keys in the `Destroyed` states will trigger a panic when accessed via | |
224 | /// `with`. | |
225 | Destroyed, | |
226 | } | |
227 | ||
c34b1796 | 228 | impl<T: 'static> LocalKey<T> { |
9346a6ac | 229 | /// Acquires a reference to the value in this TLS key. |
1a4d82fc JJ |
230 | /// |
231 | /// This will lazily initialize the value if this thread has not referenced | |
232 | /// this key yet. | |
233 | /// | |
234 | /// # Panics | |
235 | /// | |
236 | /// This function will `panic!()` if the key currently has its | |
237 | /// destructor running, and it **may** panic if the destructor has | |
238 | /// previously been run for this thread. | |
85aaf69f | 239 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
240 | pub fn with<F, R>(&'static self, f: F) -> R |
241 | where F: FnOnce(&T) -> R { | |
242 | let slot = (self.inner)(); | |
243 | unsafe { | |
244 | let slot = slot.get().expect("cannot access a TLS value during or \ | |
245 | after it is destroyed"); | |
246 | f(match *slot.get() { | |
247 | Some(ref inner) => inner, | |
248 | None => self.init(slot), | |
249 | }) | |
250 | } | |
251 | } | |
252 | ||
253 | unsafe fn init(&self, slot: &UnsafeCell<Option<T>>) -> &T { | |
254 | // Execute the initialization up front, *then* move it into our slot, | |
255 | // just in case initialization fails. | |
256 | let value = (self.init)(); | |
257 | let ptr = slot.get(); | |
258 | *ptr = Some(value); | |
259 | (*ptr).as_ref().unwrap() | |
260 | } | |
261 | ||
262 | /// Query the current state of this key. | |
263 | /// | |
264 | /// A key is initially in the `Uninitialized` state whenever a thread | |
265 | /// starts. It will remain in this state up until the first call to `with` | |
266 | /// within a thread has run the initialization expression successfully. | |
267 | /// | |
268 | /// Once the initialization expression succeeds, the key transitions to the | |
269 | /// `Valid` state which will guarantee that future calls to `with` will | |
270 | /// succeed within the thread. | |
271 | /// | |
272 | /// When a thread exits, each key will be destroyed in turn, and as keys are | |
273 | /// destroyed they will enter the `Destroyed` state just before the | |
274 | /// destructor starts to run. Keys may remain in the `Destroyed` state after | |
275 | /// destruction has completed. Keys without destructors (e.g. with types | |
276 | /// that are `Copy`), may never enter the `Destroyed` state. | |
277 | /// | |
278 | /// Keys in the `Uninitialized` can be accessed so long as the | |
279 | /// initialization does not panic. Keys in the `Valid` state are guaranteed | |
280 | /// to be able to be accessed. Keys in the `Destroyed` state will panic on | |
281 | /// any call to `with`. | |
85aaf69f SL |
282 | #[unstable(feature = "std_misc", |
283 | reason = "state querying was recently added")] | |
c34b1796 | 284 | pub fn state(&'static self) -> LocalKeyState { |
1a4d82fc JJ |
285 | unsafe { |
286 | match (self.inner)().get() { | |
287 | Some(cell) => { | |
288 | match *cell.get() { | |
c34b1796 AL |
289 | Some(..) => LocalKeyState::Valid, |
290 | None => LocalKeyState::Uninitialized, | |
1a4d82fc JJ |
291 | } |
292 | } | |
c34b1796 | 293 | None => LocalKeyState::Destroyed, |
1a4d82fc JJ |
294 | } |
295 | } | |
296 | } | |
1a4d82fc JJ |
297 | } |
298 | ||
299 | #[cfg(all(any(target_os = "macos", target_os = "linux"), not(target_arch = "aarch64")))] | |
300 | mod imp { | |
301 | use prelude::v1::*; | |
302 | ||
303 | use cell::UnsafeCell; | |
304 | use intrinsics; | |
305 | use ptr; | |
306 | ||
307 | #[doc(hidden)] | |
c34b1796 | 308 | #[unstable(feature = "thread_local_internals")] |
1a4d82fc JJ |
309 | pub struct Key<T> { |
310 | // Place the inner bits in an `UnsafeCell` to currently get around the | |
311 | // "only Sync statics" restriction. This allows any type to be placed in | |
312 | // the cell. | |
313 | // | |
314 | // Note that all access requires `T: 'static` so it can't be a type with | |
315 | // any borrowed pointers still. | |
c34b1796 | 316 | #[unstable(feature = "thread_local_internals")] |
1a4d82fc JJ |
317 | pub inner: UnsafeCell<T>, |
318 | ||
319 | // Metadata to keep track of the state of the destructor. Remember that | |
320 | // these variables are thread-local, not global. | |
c34b1796 | 321 | #[unstable(feature = "thread_local_internals")] |
1a4d82fc | 322 | pub dtor_registered: UnsafeCell<bool>, // should be Cell |
c34b1796 | 323 | #[unstable(feature = "thread_local_internals")] |
1a4d82fc JJ |
324 | pub dtor_running: UnsafeCell<bool>, // should be Cell |
325 | } | |
326 | ||
327 | unsafe impl<T> ::marker::Sync for Key<T> { } | |
328 | ||
329 | #[doc(hidden)] | |
330 | impl<T> Key<T> { | |
331 | pub unsafe fn get(&'static self) -> Option<&'static T> { | |
332 | if intrinsics::needs_drop::<T>() && *self.dtor_running.get() { | |
333 | return None | |
334 | } | |
335 | self.register_dtor(); | |
336 | Some(&*self.inner.get()) | |
337 | } | |
338 | ||
339 | unsafe fn register_dtor(&self) { | |
340 | if !intrinsics::needs_drop::<T>() || *self.dtor_registered.get() { | |
341 | return | |
342 | } | |
343 | ||
344 | register_dtor(self as *const _ as *mut u8, | |
345 | destroy_value::<T>); | |
346 | *self.dtor_registered.get() = true; | |
347 | } | |
348 | } | |
349 | ||
350 | // Since what appears to be glibc 2.18 this symbol has been shipped which | |
351 | // GCC and clang both use to invoke destructors in thread_local globals, so | |
352 | // let's do the same! | |
353 | // | |
354 | // Note, however, that we run on lots older linuxes, as well as cross | |
355 | // compiling from a newer linux to an older linux, so we also have a | |
356 | // fallback implementation to use as well. | |
357 | // | |
358 | // Due to rust-lang/rust#18804, make sure this is not generic! | |
359 | #[cfg(target_os = "linux")] | |
360 | unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { | |
c34b1796 | 361 | use boxed; |
1a4d82fc JJ |
362 | use mem; |
363 | use libc; | |
364 | use sys_common::thread_local as os; | |
365 | ||
366 | extern { | |
367 | static __dso_handle: *mut u8; | |
368 | #[linkage = "extern_weak"] | |
369 | static __cxa_thread_atexit_impl: *const (); | |
370 | } | |
371 | if !__cxa_thread_atexit_impl.is_null() { | |
372 | type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8), | |
373 | arg: *mut u8, | |
374 | dso_handle: *mut u8) -> libc::c_int; | |
375 | mem::transmute::<*const (), F>(__cxa_thread_atexit_impl) | |
9346a6ac | 376 | (dtor, t, &__dso_handle as *const _ as *mut _); |
1a4d82fc JJ |
377 | return |
378 | } | |
379 | ||
380 | // The fallback implementation uses a vanilla OS-based TLS key to track | |
381 | // the list of destructors that need to be run for this thread. The key | |
382 | // then has its own destructor which runs all the other destructors. | |
383 | // | |
384 | // The destructor for DTORS is a little special in that it has a `while` | |
385 | // loop to continuously drain the list of registered destructors. It | |
386 | // *should* be the case that this loop always terminates because we | |
387 | // provide the guarantee that a TLS key cannot be set after it is | |
388 | // flagged for destruction. | |
389 | static DTORS: os::StaticKey = os::StaticKey { | |
390 | inner: os::INIT_INNER, | |
391 | dtor: Some(run_dtors as unsafe extern "C" fn(*mut u8)), | |
392 | }; | |
393 | type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>; | |
394 | if DTORS.get().is_null() { | |
395 | let v: Box<List> = box Vec::new(); | |
c34b1796 | 396 | DTORS.set(boxed::into_raw(v) as *mut u8); |
1a4d82fc JJ |
397 | } |
398 | let list: &mut List = &mut *(DTORS.get() as *mut List); | |
399 | list.push((t, dtor)); | |
400 | ||
401 | unsafe extern fn run_dtors(mut ptr: *mut u8) { | |
402 | while !ptr.is_null() { | |
c34b1796 | 403 | let list: Box<List> = Box::from_raw(ptr as *mut List); |
85aaf69f | 404 | for &(ptr, dtor) in &*list { |
1a4d82fc JJ |
405 | dtor(ptr); |
406 | } | |
407 | ptr = DTORS.get(); | |
85aaf69f | 408 | DTORS.set(ptr::null_mut()); |
1a4d82fc JJ |
409 | } |
410 | } | |
411 | } | |
412 | ||
413 | // OSX's analog of the above linux function is this _tlv_atexit function. | |
414 | // The disassembly of thread_local globals in C++ (at least produced by | |
415 | // clang) will have this show up in the output. | |
416 | #[cfg(target_os = "macos")] | |
417 | unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { | |
418 | extern { | |
419 | fn _tlv_atexit(dtor: unsafe extern fn(*mut u8), | |
420 | arg: *mut u8); | |
421 | } | |
422 | _tlv_atexit(dtor, t); | |
423 | } | |
424 | ||
425 | #[doc(hidden)] | |
c34b1796 | 426 | #[unstable(feature = "thread_local_internals")] |
1a4d82fc JJ |
427 | pub unsafe extern fn destroy_value<T>(ptr: *mut u8) { |
428 | let ptr = ptr as *mut Key<T>; | |
429 | // Right before we run the user destructor be sure to flag the | |
430 | // destructor as running for this thread so calls to `get` will return | |
431 | // `None`. | |
432 | *(*ptr).dtor_running.get() = true; | |
85aaf69f | 433 | ptr::read((*ptr).inner.get()); |
1a4d82fc JJ |
434 | } |
435 | } | |
436 | ||
437 | #[cfg(any(not(any(target_os = "macos", target_os = "linux")), target_arch = "aarch64"))] | |
438 | mod imp { | |
439 | use prelude::v1::*; | |
440 | ||
c34b1796 | 441 | use alloc::boxed; |
1a4d82fc JJ |
442 | use cell::UnsafeCell; |
443 | use mem; | |
85aaf69f | 444 | use ptr; |
1a4d82fc JJ |
445 | use sys_common::thread_local::StaticKey as OsStaticKey; |
446 | ||
447 | #[doc(hidden)] | |
c34b1796 | 448 | #[unstable(feature = "thread_local_internals")] |
1a4d82fc JJ |
449 | pub struct Key<T> { |
450 | // Statically allocated initialization expression, using an `UnsafeCell` | |
451 | // for the same reasons as above. | |
c34b1796 | 452 | #[unstable(feature = "thread_local_internals")] |
1a4d82fc JJ |
453 | pub inner: UnsafeCell<T>, |
454 | ||
455 | // OS-TLS key that we'll use to key off. | |
c34b1796 | 456 | #[unstable(feature = "thread_local_internals")] |
1a4d82fc JJ |
457 | pub os: OsStaticKey, |
458 | } | |
459 | ||
460 | unsafe impl<T> ::marker::Sync for Key<T> { } | |
461 | ||
462 | struct Value<T: 'static> { | |
463 | key: &'static Key<T>, | |
464 | value: T, | |
465 | } | |
466 | ||
467 | #[doc(hidden)] | |
468 | impl<T> Key<T> { | |
469 | pub unsafe fn get(&'static self) -> Option<&'static T> { | |
470 | self.ptr().map(|p| &*p) | |
471 | } | |
472 | ||
473 | unsafe fn ptr(&'static self) -> Option<*mut T> { | |
474 | let ptr = self.os.get() as *mut Value<T>; | |
475 | if !ptr.is_null() { | |
c34b1796 | 476 | if ptr as usize == 1 { |
1a4d82fc JJ |
477 | return None |
478 | } | |
479 | return Some(&mut (*ptr).value as *mut T); | |
480 | } | |
481 | ||
482 | // If the lookup returned null, we haven't initialized our own local | |
483 | // copy, so do that now. | |
484 | // | |
485 | // Also note that this transmute_copy should be ok because the value | |
486 | // `inner` is already validated to be a valid `static` value, so we | |
487 | // should be able to freely copy the bits. | |
488 | let ptr: Box<Value<T>> = box Value { | |
489 | key: self, | |
490 | value: mem::transmute_copy(&self.inner), | |
491 | }; | |
c34b1796 | 492 | let ptr: *mut Value<T> = boxed::into_raw(ptr); |
1a4d82fc JJ |
493 | self.os.set(ptr as *mut u8); |
494 | Some(&mut (*ptr).value as *mut T) | |
495 | } | |
496 | } | |
497 | ||
498 | #[doc(hidden)] | |
c34b1796 | 499 | #[unstable(feature = "thread_local_internals")] |
1a4d82fc JJ |
500 | pub unsafe extern fn destroy_value<T: 'static>(ptr: *mut u8) { |
501 | // The OS TLS ensures that this key contains a NULL value when this | |
502 | // destructor starts to run. We set it back to a sentinel value of 1 to | |
503 | // ensure that any future calls to `get` for this thread will return | |
504 | // `None`. | |
505 | // | |
506 | // Note that to prevent an infinite loop we reset it back to null right | |
507 | // before we return from the destructor ourselves. | |
c34b1796 | 508 | let ptr: Box<Value<T>> = Box::from_raw(ptr as *mut Value<T>); |
1a4d82fc JJ |
509 | let key = ptr.key; |
510 | key.os.set(1 as *mut u8); | |
511 | drop(ptr); | |
85aaf69f | 512 | key.os.set(ptr::null_mut()); |
1a4d82fc JJ |
513 | } |
514 | } | |
515 | ||
516 | #[cfg(test)] | |
517 | mod tests { | |
518 | use prelude::v1::*; | |
519 | ||
520 | use sync::mpsc::{channel, Sender}; | |
521 | use cell::UnsafeCell; | |
c34b1796 | 522 | use super::LocalKeyState; |
85aaf69f | 523 | use thread; |
1a4d82fc JJ |
524 | |
525 | struct Foo(Sender<()>); | |
526 | ||
527 | impl Drop for Foo { | |
528 | fn drop(&mut self) { | |
529 | let Foo(ref s) = *self; | |
530 | s.send(()).unwrap(); | |
531 | } | |
532 | } | |
533 | ||
534 | #[test] | |
535 | fn smoke_no_dtor() { | |
c34b1796 | 536 | thread_local!(static FOO: UnsafeCell<i32> = UnsafeCell { value: 1 }); |
1a4d82fc JJ |
537 | |
538 | FOO.with(|f| unsafe { | |
539 | assert_eq!(*f.get(), 1); | |
540 | *f.get() = 2; | |
541 | }); | |
542 | let (tx, rx) = channel(); | |
85aaf69f | 543 | let _t = thread::spawn(move|| { |
1a4d82fc JJ |
544 | FOO.with(|f| unsafe { |
545 | assert_eq!(*f.get(), 1); | |
546 | }); | |
547 | tx.send(()).unwrap(); | |
548 | }); | |
549 | rx.recv().unwrap(); | |
550 | ||
551 | FOO.with(|f| unsafe { | |
552 | assert_eq!(*f.get(), 2); | |
553 | }); | |
554 | } | |
555 | ||
556 | #[test] | |
557 | fn states() { | |
558 | struct Foo; | |
559 | impl Drop for Foo { | |
560 | fn drop(&mut self) { | |
c34b1796 | 561 | assert!(FOO.state() == LocalKeyState::Destroyed); |
1a4d82fc JJ |
562 | } |
563 | } | |
564 | fn foo() -> Foo { | |
c34b1796 | 565 | assert!(FOO.state() == LocalKeyState::Uninitialized); |
1a4d82fc JJ |
566 | Foo |
567 | } | |
568 | thread_local!(static FOO: Foo = foo()); | |
569 | ||
85aaf69f | 570 | thread::spawn(|| { |
c34b1796 | 571 | assert!(FOO.state() == LocalKeyState::Uninitialized); |
1a4d82fc | 572 | FOO.with(|_| { |
c34b1796 | 573 | assert!(FOO.state() == LocalKeyState::Valid); |
1a4d82fc | 574 | }); |
c34b1796 | 575 | assert!(FOO.state() == LocalKeyState::Valid); |
1a4d82fc JJ |
576 | }).join().ok().unwrap(); |
577 | } | |
578 | ||
579 | #[test] | |
580 | fn smoke_dtor() { | |
581 | thread_local!(static FOO: UnsafeCell<Option<Foo>> = UnsafeCell { | |
582 | value: None | |
583 | }); | |
584 | ||
585 | let (tx, rx) = channel(); | |
85aaf69f | 586 | let _t = thread::spawn(move|| unsafe { |
1a4d82fc JJ |
587 | let mut tx = Some(tx); |
588 | FOO.with(|f| { | |
589 | *f.get() = Some(Foo(tx.take().unwrap())); | |
590 | }); | |
591 | }); | |
592 | rx.recv().unwrap(); | |
593 | } | |
594 | ||
595 | #[test] | |
596 | fn circular() { | |
597 | struct S1; | |
598 | struct S2; | |
599 | thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell { | |
600 | value: None | |
601 | }); | |
602 | thread_local!(static K2: UnsafeCell<Option<S2>> = UnsafeCell { | |
603 | value: None | |
604 | }); | |
c34b1796 | 605 | static mut HITS: u32 = 0; |
1a4d82fc JJ |
606 | |
607 | impl Drop for S1 { | |
608 | fn drop(&mut self) { | |
609 | unsafe { | |
610 | HITS += 1; | |
c34b1796 | 611 | if K2.state() == LocalKeyState::Destroyed { |
1a4d82fc JJ |
612 | assert_eq!(HITS, 3); |
613 | } else { | |
614 | if HITS == 1 { | |
615 | K2.with(|s| *s.get() = Some(S2)); | |
616 | } else { | |
617 | assert_eq!(HITS, 3); | |
618 | } | |
619 | } | |
620 | } | |
621 | } | |
622 | } | |
623 | impl Drop for S2 { | |
624 | fn drop(&mut self) { | |
625 | unsafe { | |
626 | HITS += 1; | |
c34b1796 | 627 | assert!(K1.state() != LocalKeyState::Destroyed); |
1a4d82fc JJ |
628 | assert_eq!(HITS, 2); |
629 | K1.with(|s| *s.get() = Some(S1)); | |
630 | } | |
631 | } | |
632 | } | |
633 | ||
85aaf69f | 634 | thread::spawn(move|| { |
1a4d82fc JJ |
635 | drop(S1); |
636 | }).join().ok().unwrap(); | |
637 | } | |
638 | ||
639 | #[test] | |
640 | fn self_referential() { | |
641 | struct S1; | |
642 | thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell { | |
643 | value: None | |
644 | }); | |
645 | ||
646 | impl Drop for S1 { | |
647 | fn drop(&mut self) { | |
c34b1796 | 648 | assert!(K1.state() == LocalKeyState::Destroyed); |
1a4d82fc JJ |
649 | } |
650 | } | |
651 | ||
85aaf69f | 652 | thread::spawn(move|| unsafe { |
1a4d82fc JJ |
653 | K1.with(|s| *s.get() = Some(S1)); |
654 | }).join().ok().unwrap(); | |
655 | } | |
656 | ||
657 | #[test] | |
658 | fn dtors_in_dtors_in_dtors() { | |
659 | struct S1(Sender<()>); | |
660 | thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell { | |
661 | value: None | |
662 | }); | |
663 | thread_local!(static K2: UnsafeCell<Option<Foo>> = UnsafeCell { | |
664 | value: None | |
665 | }); | |
666 | ||
667 | impl Drop for S1 { | |
668 | fn drop(&mut self) { | |
669 | let S1(ref tx) = *self; | |
670 | unsafe { | |
c34b1796 | 671 | if K2.state() != LocalKeyState::Destroyed { |
1a4d82fc JJ |
672 | K2.with(|s| *s.get() = Some(Foo(tx.clone()))); |
673 | } | |
674 | } | |
675 | } | |
676 | } | |
677 | ||
678 | let (tx, rx) = channel(); | |
85aaf69f | 679 | let _t = thread::spawn(move|| unsafe { |
1a4d82fc JJ |
680 | let mut tx = Some(tx); |
681 | K1.with(|s| *s.get() = Some(S1(tx.take().unwrap()))); | |
682 | }); | |
683 | rx.recv().unwrap(); | |
684 | } | |
685 | } | |
686 | ||
687 | #[cfg(test)] | |
688 | mod dynamic_tests { | |
689 | use prelude::v1::*; | |
690 | ||
691 | use cell::RefCell; | |
692 | use collections::HashMap; | |
693 | ||
694 | #[test] | |
695 | fn smoke() { | |
c34b1796 AL |
696 | fn square(i: i32) -> i32 { i * i } |
697 | thread_local!(static FOO: i32 = square(3)); | |
1a4d82fc JJ |
698 | |
699 | FOO.with(|f| { | |
700 | assert_eq!(*f, 9); | |
701 | }); | |
702 | } | |
703 | ||
704 | #[test] | |
705 | fn hashmap() { | |
c34b1796 | 706 | fn map() -> RefCell<HashMap<i32, i32>> { |
1a4d82fc JJ |
707 | let mut m = HashMap::new(); |
708 | m.insert(1, 2); | |
709 | RefCell::new(m) | |
710 | } | |
c34b1796 | 711 | thread_local!(static FOO: RefCell<HashMap<i32, i32>> = map()); |
1a4d82fc JJ |
712 | |
713 | FOO.with(|map| { | |
c34b1796 | 714 | assert_eq!(map.borrow()[&1], 2); |
1a4d82fc JJ |
715 | }); |
716 | } | |
717 | ||
718 | #[test] | |
719 | fn refcell_vec() { | |
c34b1796 | 720 | thread_local!(static FOO: RefCell<Vec<u32>> = RefCell::new(vec![1, 2, 3])); |
1a4d82fc JJ |
721 | |
722 | FOO.with(|vec| { | |
723 | assert_eq!(vec.borrow().len(), 3); | |
724 | vec.borrow_mut().push(4); | |
725 | assert_eq!(vec.borrow()[3], 4); | |
726 | }); | |
727 | } | |
728 | } |