]>
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 | 12 | |
e9174d1e | 13 | #![unstable(feature = "thread_local_internals", issue = "0")] |
1a4d82fc JJ |
14 | |
15 | use cell::UnsafeCell; | |
32a655c1 | 16 | use fmt; |
9cc50fc6 | 17 | use mem; |
1a4d82fc JJ |
18 | |
19 | /// A thread local storage key which owns its contents. | |
20 | /// | |
21 | /// This key uses the fastest possible implementation available to it for the | |
7cac9316 XL |
22 | /// target platform. It is instantiated with the [`thread_local!`] macro and the |
23 | /// primary method is the [`with`] method. | |
1a4d82fc | 24 | /// |
7cac9316 | 25 | /// The [`with`] method yields a reference to the contained value which cannot be |
bd371182 | 26 | /// sent across threads or escape the given closure. |
1a4d82fc JJ |
27 | /// |
28 | /// # Initialization and Destruction | |
29 | /// | |
7cac9316 XL |
30 | /// Initialization is dynamically performed on the first call to [`with`] |
31 | /// within a thread, and values that implement [`Drop`] get destructed when a | |
8bb4bdeb | 32 | /// thread exits. Some caveats apply, which are explained below. |
1a4d82fc | 33 | /// |
ea8adc8c XL |
34 | /// A `LocalKey`'s initializer cannot recursively depend on itself, and using |
35 | /// a `LocalKey` in this way will cause the initializer to infinitely recurse | |
36 | /// on the first call to `with`. | |
37 | /// | |
c34b1796 | 38 | /// # Examples |
1a4d82fc JJ |
39 | /// |
40 | /// ``` | |
41 | /// use std::cell::RefCell; | |
85aaf69f | 42 | /// use std::thread; |
1a4d82fc | 43 | /// |
c34b1796 | 44 | /// thread_local!(static FOO: RefCell<u32> = RefCell::new(1)); |
1a4d82fc JJ |
45 | /// |
46 | /// FOO.with(|f| { | |
47 | /// assert_eq!(*f.borrow(), 1); | |
48 | /// *f.borrow_mut() = 2; | |
49 | /// }); | |
50 | /// | |
51 | /// // each thread starts out with the initial value of 1 | |
85aaf69f | 52 | /// thread::spawn(move|| { |
1a4d82fc JJ |
53 | /// FOO.with(|f| { |
54 | /// assert_eq!(*f.borrow(), 1); | |
55 | /// *f.borrow_mut() = 3; | |
56 | /// }); | |
57 | /// }); | |
58 | /// | |
59 | /// // we retain our original value of 2 despite the child thread | |
60 | /// FOO.with(|f| { | |
61 | /// assert_eq!(*f.borrow(), 2); | |
62 | /// }); | |
63 | /// ``` | |
7453a54e SL |
64 | /// |
65 | /// # Platform-specific behavior | |
66 | /// | |
67 | /// Note that a "best effort" is made to ensure that destructors for types | |
a7813a04 | 68 | /// stored in thread local storage are run, but not all platforms can guarantee |
7453a54e SL |
69 | /// that destructors will be run for all types in thread local storage. For |
70 | /// example, there are a number of known caveats where destructors are not run: | |
71 | /// | |
72 | /// 1. On Unix systems when pthread-based TLS is being used, destructors will | |
73 | /// not be run for TLS values on the main thread when it exits. Note that the | |
74 | /// application will exit immediately after the main thread exits as well. | |
75 | /// 2. On all platforms it's possible for TLS to re-initialize other TLS slots | |
76 | /// during destruction. Some platforms ensure that this cannot happen | |
77 | /// infinitely by preventing re-initialization of any slot that has been | |
78 | /// destroyed, but not all platforms have this guard. Those platforms that do | |
79 | /// not guard typically have a synthetic limit after which point no more | |
80 | /// destructors are run. | |
cc61c64b | 81 | /// 3. On macOS, initializing TLS during destruction of other TLS slots can |
7453a54e SL |
82 | /// sometimes cancel *all* destructors for the current thread, whether or not |
83 | /// the slots have already had their destructors run or not. | |
7cac9316 XL |
84 | /// |
85 | /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with | |
86 | /// [`thread_local!`]: ../../std/macro.thread_local.html | |
87 | /// [`Drop`]: ../../std/ops/trait.Drop.html | |
85aaf69f | 88 | #[stable(feature = "rust1", since = "1.0.0")] |
9cc50fc6 SL |
89 | pub struct LocalKey<T: 'static> { |
90 | // This outer `LocalKey<T>` type is what's going to be stored in statics, | |
91 | // but actual data inside will sometimes be tagged with #[thread_local]. | |
92 | // It's not valid for a true static to reference a #[thread_local] static, | |
93 | // so we get around that by exposing an accessor through a layer of function | |
94 | // indirection (this thunk). | |
1a4d82fc | 95 | // |
9cc50fc6 SL |
96 | // Note that the thunk is itself unsafe because the returned lifetime of the |
97 | // slot where data lives, `'static`, is not actually valid. The lifetime | |
3b2f2976 | 98 | // here is actually slightly shorter than the currently running thread! |
9cc50fc6 SL |
99 | // |
100 | // Although this is an extra layer of indirection, it should in theory be | |
101 | // trivially devirtualizable by LLVM because the value of `inner` never | |
102 | // changes and the constant should be readonly within a crate. This mainly | |
103 | // only runs into problems when TLS statics are exported across crates. | |
3b2f2976 | 104 | inner: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>, |
1a4d82fc JJ |
105 | |
106 | // initialization routine to invoke to create a value | |
62682a34 | 107 | init: fn() -> T, |
1a4d82fc JJ |
108 | } |
109 | ||
8bb4bdeb | 110 | #[stable(feature = "std_debug", since = "1.16.0")] |
32a655c1 SL |
111 | impl<T: 'static> fmt::Debug for LocalKey<T> { |
112 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
113 | f.pad("LocalKey { .. }") | |
114 | } | |
115 | } | |
116 | ||
7cac9316 | 117 | /// Declare a new thread local storage key of type [`std::thread::LocalKey`]. |
62682a34 | 118 | /// |
3157f602 XL |
119 | /// # Syntax |
120 | /// | |
121 | /// The macro wraps any number of static declarations and makes them thread local. | |
041b39d2 | 122 | /// Publicity and attributes for each static are allowed. Example: |
3157f602 XL |
123 | /// |
124 | /// ``` | |
125 | /// use std::cell::RefCell; | |
126 | /// thread_local! { | |
127 | /// pub static FOO: RefCell<u32> = RefCell::new(1); | |
128 | /// | |
129 | /// #[allow(unused)] | |
130 | /// static BAR: RefCell<f32> = RefCell::new(1.0); | |
131 | /// } | |
132 | /// # fn main() {} | |
133 | /// ``` | |
134 | /// | |
7cac9316 | 135 | /// See [LocalKey documentation][`std::thread::LocalKey`] for more |
62682a34 | 136 | /// information. |
7cac9316 XL |
137 | /// |
138 | /// [`std::thread::LocalKey`]: ../std/thread/struct.LocalKey.html | |
1a4d82fc | 139 | #[macro_export] |
62682a34 | 140 | #[stable(feature = "rust1", since = "1.0.0")] |
c34b1796 | 141 | #[allow_internal_unstable] |
041b39d2 XL |
142 | macro_rules! thread_local { |
143 | // empty (base case for the recursion) | |
144 | () => {}; | |
145 | ||
146 | // process multiple declarations | |
147 | ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( | |
148 | __thread_local_inner!($(#[$attr])* $vis $name, $t, $init); | |
149 | thread_local!($($rest)*); | |
150 | ); | |
151 | ||
152 | // handle a single declaration | |
153 | ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => ( | |
154 | __thread_local_inner!($(#[$attr])* $vis $name, $t, $init); | |
155 | ); | |
156 | } | |
157 | ||
041b39d2 XL |
158 | #[doc(hidden)] |
159 | #[unstable(feature = "thread_local_internals", | |
160 | reason = "should not be necessary", | |
161 | issue = "0")] | |
162 | #[macro_export] | |
163 | #[allow_internal_unstable] | |
ea8adc8c | 164 | #[allow_internal_unsafe] |
041b39d2 | 165 | macro_rules! __thread_local_inner { |
ea8adc8c XL |
166 | (@key $(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => { |
167 | { | |
168 | #[inline] | |
041b39d2 XL |
169 | fn __init() -> $t { $init } |
170 | ||
3b2f2976 | 171 | unsafe fn __getit() -> $crate::option::Option< |
041b39d2 XL |
172 | &'static $crate::cell::UnsafeCell< |
173 | $crate::option::Option<$t>>> | |
174 | { | |
175 | #[thread_local] | |
176 | #[cfg(target_thread_local)] | |
177 | static __KEY: $crate::thread::__FastLocalKeyInner<$t> = | |
178 | $crate::thread::__FastLocalKeyInner::new(); | |
179 | ||
180 | #[cfg(not(target_thread_local))] | |
181 | static __KEY: $crate::thread::__OsLocalKeyInner<$t> = | |
182 | $crate::thread::__OsLocalKeyInner::new(); | |
183 | ||
184 | __KEY.get() | |
185 | } | |
186 | ||
3b2f2976 XL |
187 | unsafe { |
188 | $crate::thread::LocalKey::new(__getit, __init) | |
189 | } | |
ea8adc8c XL |
190 | } |
191 | }; | |
192 | ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => { | |
ea8adc8c XL |
193 | $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = |
194 | __thread_local_inner!(@key $(#[$attr])* $vis $name, $t, $init); | |
041b39d2 XL |
195 | } |
196 | } | |
197 | ||
1a4d82fc | 198 | /// Indicator of the state of a thread local storage key. |
62682a34 | 199 | #[unstable(feature = "thread_local_state", |
e9174d1e SL |
200 | reason = "state querying was recently added", |
201 | issue = "27716")] | |
32a655c1 | 202 | #[derive(Debug, Eq, PartialEq, Copy, Clone)] |
c34b1796 | 203 | pub enum LocalKeyState { |
1a4d82fc | 204 | /// All keys are in this state whenever a thread starts. Keys will |
7cac9316 | 205 | /// transition to the `Valid` state once the first call to [`with`] happens |
1a4d82fc JJ |
206 | /// and the initialization expression succeeds. |
207 | /// | |
208 | /// Keys in the `Uninitialized` state will yield a reference to the closure | |
7cac9316 XL |
209 | /// passed to [`with`] so long as the initialization routine does not panic. |
210 | /// | |
211 | /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with | |
1a4d82fc JJ |
212 | Uninitialized, |
213 | ||
214 | /// Once a key has been accessed successfully, it will enter the `Valid` | |
215 | /// state. Keys in the `Valid` state will remain so until the thread exits, | |
216 | /// at which point the destructor will be run and the key will enter the | |
217 | /// `Destroyed` state. | |
218 | /// | |
219 | /// Keys in the `Valid` state will be guaranteed to yield a reference to the | |
7cac9316 XL |
220 | /// closure passed to [`with`]. |
221 | /// | |
222 | /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with | |
1a4d82fc JJ |
223 | Valid, |
224 | ||
225 | /// When a thread exits, the destructors for keys will be run (if | |
226 | /// necessary). While a destructor is running, and possibly after a | |
227 | /// destructor has run, a key is in the `Destroyed` state. | |
228 | /// | |
229 | /// Keys in the `Destroyed` states will trigger a panic when accessed via | |
7cac9316 XL |
230 | /// [`with`]. |
231 | /// | |
232 | /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with | |
1a4d82fc JJ |
233 | Destroyed, |
234 | } | |
235 | ||
041b39d2 XL |
236 | /// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with). |
237 | #[unstable(feature = "thread_local_state", | |
238 | reason = "state querying was recently added", | |
239 | issue = "27716")] | |
240 | pub struct AccessError { | |
241 | _private: (), | |
242 | } | |
243 | ||
244 | #[unstable(feature = "thread_local_state", | |
245 | reason = "state querying was recently added", | |
246 | issue = "27716")] | |
247 | impl fmt::Debug for AccessError { | |
248 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
249 | f.debug_struct("AccessError").finish() | |
250 | } | |
251 | } | |
252 | ||
253 | #[unstable(feature = "thread_local_state", | |
254 | reason = "state querying was recently added", | |
255 | issue = "27716")] | |
256 | impl fmt::Display for AccessError { | |
257 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
258 | fmt::Display::fmt("already destroyed", f) | |
259 | } | |
260 | } | |
261 | ||
c34b1796 | 262 | impl<T: 'static> LocalKey<T> { |
62682a34 SL |
263 | #[doc(hidden)] |
264 | #[unstable(feature = "thread_local_internals", | |
e9174d1e SL |
265 | reason = "recently added to create a key", |
266 | issue = "0")] | |
3b2f2976 XL |
267 | pub const unsafe fn new(inner: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>, |
268 | init: fn() -> T) -> LocalKey<T> { | |
62682a34 | 269 | LocalKey { |
3b2f2976 XL |
270 | inner, |
271 | init, | |
62682a34 SL |
272 | } |
273 | } | |
274 | ||
9346a6ac | 275 | /// Acquires a reference to the value in this TLS key. |
1a4d82fc JJ |
276 | /// |
277 | /// This will lazily initialize the value if this thread has not referenced | |
278 | /// this key yet. | |
279 | /// | |
280 | /// # Panics | |
281 | /// | |
282 | /// This function will `panic!()` if the key currently has its | |
283 | /// destructor running, and it **may** panic if the destructor has | |
284 | /// previously been run for this thread. | |
85aaf69f | 285 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
286 | pub fn with<F, R>(&'static self, f: F) -> R |
287 | where F: FnOnce(&T) -> R { | |
041b39d2 XL |
288 | self.try_with(f).expect("cannot access a TLS value during or \ |
289 | after it is destroyed") | |
1a4d82fc JJ |
290 | } |
291 | ||
292 | unsafe fn init(&self, slot: &UnsafeCell<Option<T>>) -> &T { | |
293 | // Execute the initialization up front, *then* move it into our slot, | |
294 | // just in case initialization fails. | |
295 | let value = (self.init)(); | |
296 | let ptr = slot.get(); | |
9cc50fc6 SL |
297 | |
298 | // note that this can in theory just be `*ptr = Some(value)`, but due to | |
299 | // the compiler will currently codegen that pattern with something like: | |
300 | // | |
301 | // ptr::drop_in_place(ptr) | |
302 | // ptr::write(ptr, Some(value)) | |
303 | // | |
304 | // Due to this pattern it's possible for the destructor of the value in | |
305 | // `ptr` (e.g. if this is being recursively initialized) to re-access | |
306 | // TLS, in which case there will be a `&` and `&mut` pointer to the same | |
307 | // value (an aliasing violation). To avoid setting the "I'm running a | |
308 | // destructor" flag we just use `mem::replace` which should sequence the | |
309 | // operations a little differently and make this safe to call. | |
310 | mem::replace(&mut *ptr, Some(value)); | |
311 | ||
1a4d82fc JJ |
312 | (*ptr).as_ref().unwrap() |
313 | } | |
314 | ||
315 | /// Query the current state of this key. | |
316 | /// | |
317 | /// A key is initially in the `Uninitialized` state whenever a thread | |
7cac9316 | 318 | /// starts. It will remain in this state up until the first call to [`with`] |
1a4d82fc JJ |
319 | /// within a thread has run the initialization expression successfully. |
320 | /// | |
321 | /// Once the initialization expression succeeds, the key transitions to the | |
7cac9316 | 322 | /// `Valid` state which will guarantee that future calls to [`with`] will |
abe05a73 XL |
323 | /// succeed within the thread. Some keys might skip the `Uninitialized` |
324 | /// state altogether and start in the `Valid` state as an optimization | |
325 | /// (e.g. keys initialized with a constant expression), but no guarantees | |
326 | /// are made. | |
1a4d82fc JJ |
327 | /// |
328 | /// When a thread exits, each key will be destroyed in turn, and as keys are | |
329 | /// destroyed they will enter the `Destroyed` state just before the | |
330 | /// destructor starts to run. Keys may remain in the `Destroyed` state after | |
331 | /// destruction has completed. Keys without destructors (e.g. with types | |
7cac9316 | 332 | /// that are [`Copy`]), may never enter the `Destroyed` state. |
1a4d82fc | 333 | /// |
a7813a04 | 334 | /// Keys in the `Uninitialized` state can be accessed so long as the |
1a4d82fc JJ |
335 | /// initialization does not panic. Keys in the `Valid` state are guaranteed |
336 | /// to be able to be accessed. Keys in the `Destroyed` state will panic on | |
7cac9316 XL |
337 | /// any call to [`with`]. |
338 | /// | |
339 | /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with | |
340 | /// [`Copy`]: ../../std/marker/trait.Copy.html | |
62682a34 | 341 | #[unstable(feature = "thread_local_state", |
e9174d1e SL |
342 | reason = "state querying was recently added", |
343 | issue = "27716")] | |
c34b1796 | 344 | pub fn state(&'static self) -> LocalKeyState { |
1a4d82fc | 345 | unsafe { |
9cc50fc6 | 346 | match (self.inner)() { |
1a4d82fc JJ |
347 | Some(cell) => { |
348 | match *cell.get() { | |
c34b1796 AL |
349 | Some(..) => LocalKeyState::Valid, |
350 | None => LocalKeyState::Uninitialized, | |
1a4d82fc JJ |
351 | } |
352 | } | |
c34b1796 | 353 | None => LocalKeyState::Destroyed, |
1a4d82fc JJ |
354 | } |
355 | } | |
356 | } | |
041b39d2 XL |
357 | |
358 | /// Acquires a reference to the value in this TLS key. | |
359 | /// | |
360 | /// This will lazily initialize the value if this thread has not referenced | |
361 | /// this key yet. If the key has been destroyed (which may happen if this is called | |
362 | /// in a destructor), this function will return a ThreadLocalError. | |
363 | /// | |
364 | /// # Panics | |
365 | /// | |
366 | /// This function will still `panic!()` if the key is uninitialized and the | |
367 | /// key's initializer panics. | |
368 | #[unstable(feature = "thread_local_state", | |
369 | reason = "state querying was recently added", | |
370 | issue = "27716")] | |
371 | pub fn try_with<F, R>(&'static self, f: F) -> Result<R, AccessError> | |
372 | where F: FnOnce(&T) -> R { | |
373 | unsafe { | |
374 | let slot = (self.inner)().ok_or(AccessError { | |
375 | _private: (), | |
376 | })?; | |
377 | Ok(f(match *slot.get() { | |
378 | Some(ref inner) => inner, | |
379 | None => self.init(slot), | |
380 | })) | |
381 | } | |
382 | } | |
383 | } | |
384 | ||
385 | #[doc(hidden)] | |
386 | #[cfg(target_thread_local)] | |
387 | pub mod fast { | |
388 | use cell::{Cell, UnsafeCell}; | |
389 | use fmt; | |
390 | use mem; | |
391 | use ptr; | |
392 | use sys::fast_thread_local::{register_dtor, requires_move_before_drop}; | |
393 | ||
394 | pub struct Key<T> { | |
395 | inner: UnsafeCell<Option<T>>, | |
396 | ||
397 | // Metadata to keep track of the state of the destructor. Remember that | |
398 | // these variables are thread-local, not global. | |
399 | dtor_registered: Cell<bool>, | |
400 | dtor_running: Cell<bool>, | |
401 | } | |
402 | ||
403 | impl<T> fmt::Debug for Key<T> { | |
404 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
405 | f.pad("Key { .. }") | |
406 | } | |
407 | } | |
408 | ||
041b39d2 XL |
409 | impl<T> Key<T> { |
410 | pub const fn new() -> Key<T> { | |
411 | Key { | |
412 | inner: UnsafeCell::new(None), | |
413 | dtor_registered: Cell::new(false), | |
414 | dtor_running: Cell::new(false) | |
415 | } | |
416 | } | |
417 | ||
3b2f2976 XL |
418 | pub unsafe fn get(&self) -> Option<&'static UnsafeCell<Option<T>>> { |
419 | if mem::needs_drop::<T>() && self.dtor_running.get() { | |
420 | return None | |
041b39d2 | 421 | } |
3b2f2976 XL |
422 | self.register_dtor(); |
423 | Some(&*(&self.inner as *const _)) | |
041b39d2 XL |
424 | } |
425 | ||
426 | unsafe fn register_dtor(&self) { | |
427 | if !mem::needs_drop::<T>() || self.dtor_registered.get() { | |
428 | return | |
429 | } | |
430 | ||
431 | register_dtor(self as *const _ as *mut u8, | |
432 | destroy_value::<T>); | |
433 | self.dtor_registered.set(true); | |
434 | } | |
435 | } | |
436 | ||
437 | unsafe extern fn destroy_value<T>(ptr: *mut u8) { | |
438 | let ptr = ptr as *mut Key<T>; | |
439 | // Right before we run the user destructor be sure to flag the | |
440 | // destructor as running for this thread so calls to `get` will return | |
441 | // `None`. | |
442 | (*ptr).dtor_running.set(true); | |
443 | ||
444 | // Some implementations may require us to move the value before we drop | |
445 | // it as it could get re-initialized in-place during destruction. | |
446 | // | |
447 | // Hence, we use `ptr::read` on those platforms (to move to a "safe" | |
448 | // location) instead of drop_in_place. | |
449 | if requires_move_before_drop() { | |
450 | ptr::read((*ptr).inner.get()); | |
451 | } else { | |
452 | ptr::drop_in_place((*ptr).inner.get()); | |
453 | } | |
454 | } | |
1a4d82fc JJ |
455 | } |
456 | ||
d9579d0f | 457 | #[doc(hidden)] |
9cc50fc6 | 458 | pub mod os { |
62682a34 | 459 | use cell::{Cell, UnsafeCell}; |
32a655c1 | 460 | use fmt; |
62682a34 | 461 | use marker; |
85aaf69f | 462 | use ptr; |
1a4d82fc JJ |
463 | use sys_common::thread_local::StaticKey as OsStaticKey; |
464 | ||
1a4d82fc | 465 | pub struct Key<T> { |
1a4d82fc | 466 | // OS-TLS key that we'll use to key off. |
62682a34 SL |
467 | os: OsStaticKey, |
468 | marker: marker::PhantomData<Cell<T>>, | |
1a4d82fc JJ |
469 | } |
470 | ||
32a655c1 SL |
471 | impl<T> fmt::Debug for Key<T> { |
472 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
473 | f.pad("Key { .. }") | |
474 | } | |
475 | } | |
476 | ||
1a4d82fc JJ |
477 | unsafe impl<T> ::marker::Sync for Key<T> { } |
478 | ||
479 | struct Value<T: 'static> { | |
480 | key: &'static Key<T>, | |
62682a34 | 481 | value: UnsafeCell<Option<T>>, |
1a4d82fc JJ |
482 | } |
483 | ||
62682a34 SL |
484 | impl<T: 'static> Key<T> { |
485 | pub const fn new() -> Key<T> { | |
486 | Key { | |
487 | os: OsStaticKey::new(Some(destroy_value::<T>)), | |
488 | marker: marker::PhantomData | |
489 | } | |
1a4d82fc JJ |
490 | } |
491 | ||
3b2f2976 XL |
492 | pub unsafe fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> { |
493 | let ptr = self.os.get() as *mut Value<T>; | |
494 | if !ptr.is_null() { | |
495 | if ptr as usize == 1 { | |
496 | return None | |
1a4d82fc | 497 | } |
3b2f2976 | 498 | return Some(&(*ptr).value); |
7453a54e | 499 | } |
3b2f2976 XL |
500 | |
501 | // If the lookup returned null, we haven't initialized our own | |
502 | // local copy, so do that now. | |
503 | let ptr: Box<Value<T>> = box Value { | |
504 | key: self, | |
505 | value: UnsafeCell::new(None), | |
506 | }; | |
507 | let ptr = Box::into_raw(ptr); | |
508 | self.os.set(ptr as *mut u8); | |
509 | Some(&(*ptr).value) | |
1a4d82fc JJ |
510 | } |
511 | } | |
512 | ||
041b39d2 | 513 | unsafe extern fn destroy_value<T: 'static>(ptr: *mut u8) { |
1a4d82fc JJ |
514 | // The OS TLS ensures that this key contains a NULL value when this |
515 | // destructor starts to run. We set it back to a sentinel value of 1 to | |
516 | // ensure that any future calls to `get` for this thread will return | |
517 | // `None`. | |
518 | // | |
519 | // Note that to prevent an infinite loop we reset it back to null right | |
520 | // before we return from the destructor ourselves. | |
d9579d0f | 521 | let ptr = Box::from_raw(ptr as *mut Value<T>); |
1a4d82fc JJ |
522 | let key = ptr.key; |
523 | key.os.set(1 as *mut u8); | |
524 | drop(ptr); | |
85aaf69f | 525 | key.os.set(ptr::null_mut()); |
1a4d82fc JJ |
526 | } |
527 | } | |
528 | ||
c30ab7b3 | 529 | #[cfg(all(test, not(target_os = "emscripten")))] |
1a4d82fc | 530 | mod tests { |
1a4d82fc | 531 | use sync::mpsc::{channel, Sender}; |
62682a34 | 532 | use cell::{Cell, UnsafeCell}; |
c34b1796 | 533 | use super::LocalKeyState; |
85aaf69f | 534 | use thread; |
1a4d82fc JJ |
535 | |
536 | struct Foo(Sender<()>); | |
537 | ||
538 | impl Drop for Foo { | |
539 | fn drop(&mut self) { | |
540 | let Foo(ref s) = *self; | |
541 | s.send(()).unwrap(); | |
542 | } | |
543 | } | |
544 | ||
545 | #[test] | |
546 | fn smoke_no_dtor() { | |
62682a34 | 547 | thread_local!(static FOO: Cell<i32> = Cell::new(1)); |
1a4d82fc | 548 | |
62682a34 SL |
549 | FOO.with(|f| { |
550 | assert_eq!(f.get(), 1); | |
551 | f.set(2); | |
1a4d82fc JJ |
552 | }); |
553 | let (tx, rx) = channel(); | |
85aaf69f | 554 | let _t = thread::spawn(move|| { |
62682a34 SL |
555 | FOO.with(|f| { |
556 | assert_eq!(f.get(), 1); | |
1a4d82fc JJ |
557 | }); |
558 | tx.send(()).unwrap(); | |
559 | }); | |
560 | rx.recv().unwrap(); | |
561 | ||
62682a34 SL |
562 | FOO.with(|f| { |
563 | assert_eq!(f.get(), 2); | |
1a4d82fc JJ |
564 | }); |
565 | } | |
566 | ||
567 | #[test] | |
568 | fn states() { | |
569 | struct Foo; | |
570 | impl Drop for Foo { | |
571 | fn drop(&mut self) { | |
c34b1796 | 572 | assert!(FOO.state() == LocalKeyState::Destroyed); |
1a4d82fc JJ |
573 | } |
574 | } | |
575 | fn foo() -> Foo { | |
c34b1796 | 576 | assert!(FOO.state() == LocalKeyState::Uninitialized); |
1a4d82fc JJ |
577 | Foo |
578 | } | |
579 | thread_local!(static FOO: Foo = foo()); | |
580 | ||
85aaf69f | 581 | thread::spawn(|| { |
c34b1796 | 582 | assert!(FOO.state() == LocalKeyState::Uninitialized); |
1a4d82fc | 583 | FOO.with(|_| { |
c34b1796 | 584 | assert!(FOO.state() == LocalKeyState::Valid); |
1a4d82fc | 585 | }); |
c34b1796 | 586 | assert!(FOO.state() == LocalKeyState::Valid); |
1a4d82fc JJ |
587 | }).join().ok().unwrap(); |
588 | } | |
589 | ||
590 | #[test] | |
591 | fn smoke_dtor() { | |
62682a34 | 592 | thread_local!(static FOO: UnsafeCell<Option<Foo>> = UnsafeCell::new(None)); |
1a4d82fc JJ |
593 | |
594 | let (tx, rx) = channel(); | |
85aaf69f | 595 | let _t = thread::spawn(move|| unsafe { |
1a4d82fc JJ |
596 | let mut tx = Some(tx); |
597 | FOO.with(|f| { | |
598 | *f.get() = Some(Foo(tx.take().unwrap())); | |
599 | }); | |
600 | }); | |
601 | rx.recv().unwrap(); | |
602 | } | |
603 | ||
604 | #[test] | |
605 | fn circular() { | |
606 | struct S1; | |
607 | struct S2; | |
62682a34 SL |
608 | thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None)); |
609 | thread_local!(static K2: UnsafeCell<Option<S2>> = UnsafeCell::new(None)); | |
c34b1796 | 610 | static mut HITS: u32 = 0; |
1a4d82fc JJ |
611 | |
612 | impl Drop for S1 { | |
613 | fn drop(&mut self) { | |
614 | unsafe { | |
615 | HITS += 1; | |
c34b1796 | 616 | if K2.state() == LocalKeyState::Destroyed { |
1a4d82fc JJ |
617 | assert_eq!(HITS, 3); |
618 | } else { | |
619 | if HITS == 1 { | |
620 | K2.with(|s| *s.get() = Some(S2)); | |
621 | } else { | |
622 | assert_eq!(HITS, 3); | |
623 | } | |
624 | } | |
625 | } | |
626 | } | |
627 | } | |
628 | impl Drop for S2 { | |
629 | fn drop(&mut self) { | |
630 | unsafe { | |
631 | HITS += 1; | |
c34b1796 | 632 | assert!(K1.state() != LocalKeyState::Destroyed); |
1a4d82fc JJ |
633 | assert_eq!(HITS, 2); |
634 | K1.with(|s| *s.get() = Some(S1)); | |
635 | } | |
636 | } | |
637 | } | |
638 | ||
85aaf69f | 639 | thread::spawn(move|| { |
1a4d82fc JJ |
640 | drop(S1); |
641 | }).join().ok().unwrap(); | |
642 | } | |
643 | ||
644 | #[test] | |
645 | fn self_referential() { | |
646 | struct S1; | |
62682a34 | 647 | thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None)); |
1a4d82fc JJ |
648 | |
649 | impl Drop for S1 { | |
650 | fn drop(&mut self) { | |
c34b1796 | 651 | assert!(K1.state() == LocalKeyState::Destroyed); |
1a4d82fc JJ |
652 | } |
653 | } | |
654 | ||
85aaf69f | 655 | thread::spawn(move|| unsafe { |
1a4d82fc JJ |
656 | K1.with(|s| *s.get() = Some(S1)); |
657 | }).join().ok().unwrap(); | |
658 | } | |
659 | ||
7453a54e | 660 | // Note that this test will deadlock if TLS destructors aren't run (this |
cc61c64b | 661 | // requires the destructor to be run to pass the test). macOS has a known bug |
7453a54e | 662 | // where dtors-in-dtors may cancel other destructors, so we just ignore this |
cc61c64b | 663 | // test on macOS. |
1a4d82fc | 664 | #[test] |
7453a54e | 665 | #[cfg_attr(target_os = "macos", ignore)] |
1a4d82fc JJ |
666 | fn dtors_in_dtors_in_dtors() { |
667 | struct S1(Sender<()>); | |
62682a34 SL |
668 | thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None)); |
669 | thread_local!(static K2: UnsafeCell<Option<Foo>> = UnsafeCell::new(None)); | |
1a4d82fc JJ |
670 | |
671 | impl Drop for S1 { | |
672 | fn drop(&mut self) { | |
673 | let S1(ref tx) = *self; | |
674 | unsafe { | |
c34b1796 | 675 | if K2.state() != LocalKeyState::Destroyed { |
1a4d82fc JJ |
676 | K2.with(|s| *s.get() = Some(Foo(tx.clone()))); |
677 | } | |
678 | } | |
679 | } | |
680 | } | |
681 | ||
682 | let (tx, rx) = channel(); | |
85aaf69f | 683 | let _t = thread::spawn(move|| unsafe { |
1a4d82fc JJ |
684 | let mut tx = Some(tx); |
685 | K1.with(|s| *s.get() = Some(S1(tx.take().unwrap()))); | |
686 | }); | |
687 | rx.recv().unwrap(); | |
688 | } | |
689 | } | |
690 | ||
691 | #[cfg(test)] | |
692 | mod dynamic_tests { | |
1a4d82fc JJ |
693 | use cell::RefCell; |
694 | use collections::HashMap; | |
695 | ||
696 | #[test] | |
697 | fn smoke() { | |
c34b1796 AL |
698 | fn square(i: i32) -> i32 { i * i } |
699 | thread_local!(static FOO: i32 = square(3)); | |
1a4d82fc JJ |
700 | |
701 | FOO.with(|f| { | |
702 | assert_eq!(*f, 9); | |
703 | }); | |
704 | } | |
705 | ||
706 | #[test] | |
707 | fn hashmap() { | |
c34b1796 | 708 | fn map() -> RefCell<HashMap<i32, i32>> { |
1a4d82fc JJ |
709 | let mut m = HashMap::new(); |
710 | m.insert(1, 2); | |
711 | RefCell::new(m) | |
712 | } | |
c34b1796 | 713 | thread_local!(static FOO: RefCell<HashMap<i32, i32>> = map()); |
1a4d82fc JJ |
714 | |
715 | FOO.with(|map| { | |
c34b1796 | 716 | assert_eq!(map.borrow()[&1], 2); |
1a4d82fc JJ |
717 | }); |
718 | } | |
719 | ||
720 | #[test] | |
721 | fn refcell_vec() { | |
c34b1796 | 722 | thread_local!(static FOO: RefCell<Vec<u32>> = RefCell::new(vec![1, 2, 3])); |
1a4d82fc JJ |
723 | |
724 | FOO.with(|vec| { | |
725 | assert_eq!(vec.borrow().len(), 3); | |
726 | vec.borrow_mut().push(4); | |
727 | assert_eq!(vec.borrow()[3], 4); | |
728 | }); | |
729 | } | |
730 | } |