Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 1 | //! Thread local storage |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 2 | |
Alex Crichton | 5f62562 | 2015-08-13 17:12:38 | [diff] [blame] | 3 | #![unstable(feature = "thread_local_internals", issue = "0")] |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 4 | |
Stjepan Glavina | d6c3196 | 2019-06-03 16:20:38 | [diff] [blame] | 5 | use crate::error::Error; |
Taiki Endo | 93b6d9e | 2019-02-10 19:23:21 | [diff] [blame] | 6 | use crate::fmt; |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 7 | |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 8 | /// A thread local storage key which owns its contents. |
| 9 | /// |
| 10 | /// This key uses the fastest possible implementation available to it for the |
Felix Raimundo | f92bd3d | 2017-05-14 18:06:54 | [diff] [blame] | 11 | /// target platform. It is instantiated with the [`thread_local!`] macro and the |
| 12 | /// primary method is the [`with`] method. |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 13 | /// |
Felix Raimundo | f92bd3d | 2017-05-14 18:06:54 | [diff] [blame] | 14 | /// The [`with`] method yields a reference to the contained value which cannot be |
Barosl Lee | ff332b6 | 2015-05-08 15:12:29 | [diff] [blame] | 15 | /// sent across threads or escape the given closure. |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 16 | /// |
| 17 | /// # Initialization and Destruction |
| 18 | /// |
Felix Raimundo | f92bd3d | 2017-05-14 18:06:54 | [diff] [blame] | 19 | /// Initialization is dynamically performed on the first call to [`with`] |
| 20 | /// within a thread, and values that implement [`Drop`] get destructed when a |
Stjepan Glavina | 1fbbe79 | 2017-02-15 22:26:29 | [diff] [blame] | 21 | /// thread exits. Some caveats apply, which are explained below. |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 22 | /// |
Joshua Liebow-Feeser | ddb072b | 2017-09-07 19:57:08 | [diff] [blame] | 23 | /// A `LocalKey`'s initializer cannot recursively depend on itself, and using |
| 24 | /// a `LocalKey` in this way will cause the initializer to infinitely recurse |
| 25 | /// on the first call to `with`. |
| 26 | /// |
Steve Klabnik | 64ab111 | 2015-03-12 01:11:40 | [diff] [blame] | 27 | /// # Examples |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 28 | /// |
| 29 | /// ``` |
| 30 | /// use std::cell::RefCell; |
Aaron Turon | d0de2b4 | 2015-02-17 23:10:25 | [diff] [blame] | 31 | /// use std::thread; |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 32 | /// |
Vojtech Kral | e5e76e9 | 2015-02-23 16:24:50 | [diff] [blame] | 33 | /// thread_local!(static FOO: RefCell<u32> = RefCell::new(1)); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 34 | /// |
| 35 | /// FOO.with(|f| { |
| 36 | /// assert_eq!(*f.borrow(), 1); |
| 37 | /// *f.borrow_mut() = 2; |
| 38 | /// }); |
| 39 | /// |
| 40 | /// // each thread starts out with the initial value of 1 |
benaryorg | 2293d22 | 2019-03-03 14:21:52 | [diff] [blame] | 41 | /// let t = thread::spawn(move|| { |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 42 | /// FOO.with(|f| { |
| 43 | /// assert_eq!(*f.borrow(), 1); |
| 44 | /// *f.borrow_mut() = 3; |
| 45 | /// }); |
Aaron Turon | caca9b2 | 2015-01-06 05:59:45 | [diff] [blame] | 46 | /// }); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 47 | /// |
benaryorg | 2293d22 | 2019-03-03 14:21:52 | [diff] [blame] | 48 | /// // wait for the thread to complete and bail out on panic |
| 49 | /// t.join().unwrap(); |
| 50 | /// |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 51 | /// // we retain our original value of 2 despite the child thread |
| 52 | /// FOO.with(|f| { |
| 53 | /// assert_eq!(*f.borrow(), 2); |
| 54 | /// }); |
| 55 | /// ``` |
Alex Crichton | b960de0 | 2016-01-29 21:46:47 | [diff] [blame] | 56 | /// |
| 57 | /// # Platform-specific behavior |
| 58 | /// |
| 59 | /// Note that a "best effort" is made to ensure that destructors for types |
Ryman | 2b71219 | 2016-04-25 20:01:19 | [diff] [blame] | 60 | /// stored in thread local storage are run, but not all platforms can guarantee |
Alex Crichton | b960de0 | 2016-01-29 21:46:47 | [diff] [blame] | 61 | /// that destructors will be run for all types in thread local storage. For |
| 62 | /// example, there are a number of known caveats where destructors are not run: |
| 63 | /// |
| 64 | /// 1. On Unix systems when pthread-based TLS is being used, destructors will |
| 65 | /// not be run for TLS values on the main thread when it exits. Note that the |
| 66 | /// application will exit immediately after the main thread exits as well. |
| 67 | /// 2. On all platforms it's possible for TLS to re-initialize other TLS slots |
| 68 | /// during destruction. Some platforms ensure that this cannot happen |
| 69 | /// infinitely by preventing re-initialization of any slot that has been |
| 70 | /// destroyed, but not all platforms have this guard. Those platforms that do |
| 71 | /// not guard typically have a synthetic limit after which point no more |
| 72 | /// destructors are run. |
Felix Raimundo | f92bd3d | 2017-05-14 18:06:54 | [diff] [blame] | 73 | /// |
| 74 | /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with |
| 75 | /// [`thread_local!`]: ../../std/macro.thread_local.html |
| 76 | /// [`Drop`]: ../../std/ops/trait.Drop.html |
Brian Anderson | b44ee37 | 2015-01-24 05:48:20 | [diff] [blame] | 77 | #[stable(feature = "rust1", since = "1.0.0")] |
Alex Crichton | cd74364e | 2015-12-11 20:42:29 | [diff] [blame] | 78 | pub struct LocalKey<T: 'static> { |
| 79 | // This outer `LocalKey<T>` type is what's going to be stored in statics, |
| 80 | // but actual data inside will sometimes be tagged with #[thread_local]. |
| 81 | // It's not valid for a true static to reference a #[thread_local] static, |
| 82 | // so we get around that by exposing an accessor through a layer of function |
| 83 | // indirection (this thunk). |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 84 | // |
Alex Crichton | cd74364e | 2015-12-11 20:42:29 | [diff] [blame] | 85 | // Note that the thunk is itself unsafe because the returned lifetime of the |
| 86 | // slot where data lives, `'static`, is not actually valid. The lifetime |
Eduard-Mihai Burtescu | 92892d3 | 2017-08-08 15:22:51 | [diff] [blame] | 87 | // here is actually slightly shorter than the currently running thread! |
Alex Crichton | cd74364e | 2015-12-11 20:42:29 | [diff] [blame] | 88 | // |
| 89 | // Although this is an extra layer of indirection, it should in theory be |
| 90 | // trivially devirtualizable by LLVM because the value of `inner` never |
| 91 | // changes and the constant should be readonly within a crate. This mainly |
| 92 | // only runs into problems when TLS statics are exported across crates. |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 93 | inner: unsafe fn() -> Option<&'static T>, |
Patrick Walton | ddb2466 | 2014-11-14 17:18:10 | [diff] [blame] | 94 | } |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 95 | |
Oliver Middleton | 9128f61 | 2017-01-29 13:31:47 | [diff] [blame] | 96 | #[stable(feature = "std_debug", since = "1.16.0")] |
Corey Farwell | 86fc63e | 2016-11-25 18:21:49 | [diff] [blame] | 97 | impl<T: 'static> fmt::Debug for LocalKey<T> { |
Mazdak Farrokhzad | 379c380 | 2019-03-01 08:34:11 | [diff] [blame] | 98 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
Corey Farwell | 86fc63e | 2016-11-25 18:21:49 | [diff] [blame] | 99 | f.pad("LocalKey { .. }") |
| 100 | } |
| 101 | } |
| 102 | |
Felix Raimundo | f92bd3d | 2017-05-14 18:06:54 | [diff] [blame] | 103 | /// Declare a new thread local storage key of type [`std::thread::LocalKey`]. |
Eduard Burtescu | 377b090 | 2015-05-27 08:18:36 | [diff] [blame] | 104 | /// |
Alex Burka | fc28ee2 | 2016-06-04 19:19:22 | [diff] [blame] | 105 | /// # Syntax |
| 106 | /// |
| 107 | /// The macro wraps any number of static declarations and makes them thread local. |
Alex Burka | b6a2d7e | 2017-04-01 03:06:34 | [diff] [blame] | 108 | /// Publicity and attributes for each static are allowed. Example: |
Alex Burka | fc28ee2 | 2016-06-04 19:19:22 | [diff] [blame] | 109 | /// |
| 110 | /// ``` |
| 111 | /// use std::cell::RefCell; |
| 112 | /// thread_local! { |
| 113 | /// pub static FOO: RefCell<u32> = RefCell::new(1); |
| 114 | /// |
| 115 | /// #[allow(unused)] |
| 116 | /// static BAR: RefCell<f32> = RefCell::new(1.0); |
| 117 | /// } |
| 118 | /// # fn main() {} |
| 119 | /// ``` |
| 120 | /// |
Felix Raimundo | f92bd3d | 2017-05-14 18:06:54 | [diff] [blame] | 121 | /// See [LocalKey documentation][`std::thread::LocalKey`] for more |
Alex Crichton | 1b5f9cb | 2015-05-28 06:24:27 | [diff] [blame] | 122 | /// information. |
Felix Raimundo | f92bd3d | 2017-05-14 18:06:54 | [diff] [blame] | 123 | /// |
| 124 | /// [`std::thread::LocalKey`]: ../std/thread/struct.LocalKey.html |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 125 | #[macro_export] |
Eduard Burtescu | 377b090 | 2015-05-27 08:18:36 | [diff] [blame] | 126 | #[stable(feature = "rust1", since = "1.0.0")] |
Mark Rousskov | 2870015 | 2019-02-27 23:58:12 | [diff] [blame] | 127 | #[allow_internal_unstable(thread_local_internals)] |
Eduard Burtescu | 377b090 | 2015-05-27 08:18:36 | [diff] [blame] | 128 | macro_rules! thread_local { |
Alex Burka | b6a2d7e | 2017-04-01 03:06:34 | [diff] [blame] | 129 | // empty (base case for the recursion) |
Alex Burka | fc28ee2 | 2016-06-04 19:19:22 | [diff] [blame] | 130 | () => {}; |
| 131 | |
Alex Burka | 8cce5bc | 2017-04-19 02:29:40 | [diff] [blame] | 132 | // process multiple declarations |
| 133 | ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( |
Alex Crichton | ff5226c | 2018-11-01 21:17:39 | [diff] [blame] | 134 | $crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, $init); |
| 135 | $crate::thread_local!($($rest)*); |
Alex Burka | fc28ee2 | 2016-06-04 19:19:22 | [diff] [blame] | 136 | ); |
| 137 | |
Alex Burka | 8cce5bc | 2017-04-19 02:29:40 | [diff] [blame] | 138 | // handle a single declaration |
| 139 | ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => ( |
Alex Crichton | ff5226c | 2018-11-01 21:17:39 | [diff] [blame] | 140 | $crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, $init); |
Alex Crichton | 1b5f9cb | 2015-05-28 06:24:27 | [diff] [blame] | 141 | ); |
| 142 | } |
| 143 | |
| 144 | #[doc(hidden)] |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 145 | #[unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "0")] |
Alex Crichton | 1b5f9cb | 2015-05-28 06:24:27 | [diff] [blame] | 146 | #[macro_export] |
Mark Rousskov | 2870015 | 2019-02-27 23:58:12 | [diff] [blame] | 147 | #[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)] |
Alex Crichton | 2972687 | 2017-08-25 15:39:02 | [diff] [blame] | 148 | #[allow_internal_unsafe] |
Alex Crichton | 1b5f9cb | 2015-05-28 06:24:27 | [diff] [blame] | 149 | macro_rules! __thread_local_inner { |
3442853561 | 936349c | 2019-11-06 08:39:48 | [diff] [blame] | 150 | (@key $t:ty, $init:expr) => { |
Eduard-Mihai Burtescu | 4e2be14 | 2017-08-13 13:42:10 | [diff] [blame] | 151 | { |
| 152 | #[inline] |
Alex Burka | b6a2d7e | 2017-04-01 03:06:34 | [diff] [blame] | 153 | fn __init() -> $t { $init } |
Alex Crichton | cd74364e | 2015-12-11 20:42:29 | [diff] [blame] | 154 | |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 155 | unsafe fn __getit() -> $crate::option::Option<&'static $t> { |
Alex Crichton | cbe9f33 | 2018-10-10 06:10:25 | [diff] [blame] | 156 | #[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))] |
Alex Crichton | c3a5d6b | 2018-03-29 21:59:13 | [diff] [blame] | 157 | static __KEY: $crate::thread::__StaticLocalKeyInner<$t> = |
| 158 | $crate::thread::__StaticLocalKeyInner::new(); |
| 159 | |
Alex Burka | b6a2d7e | 2017-04-01 03:06:34 | [diff] [blame] | 160 | #[thread_local] |
Alex Crichton | cbe9f33 | 2018-10-10 06:10:25 | [diff] [blame] | 161 | #[cfg(all( |
| 162 | target_thread_local, |
| 163 | not(all(target_arch = "wasm32", not(target_feature = "atomics"))), |
| 164 | ))] |
Alex Burka | b6a2d7e | 2017-04-01 03:06:34 | [diff] [blame] | 165 | static __KEY: $crate::thread::__FastLocalKeyInner<$t> = |
| 166 | $crate::thread::__FastLocalKeyInner::new(); |
Alex Crichton | cd74364e | 2015-12-11 20:42:29 | [diff] [blame] | 167 | |
Alex Crichton | cbe9f33 | 2018-10-10 06:10:25 | [diff] [blame] | 168 | #[cfg(all( |
| 169 | not(target_thread_local), |
| 170 | not(all(target_arch = "wasm32", not(target_feature = "atomics"))), |
| 171 | ))] |
Alex Burka | b6a2d7e | 2017-04-01 03:06:34 | [diff] [blame] | 172 | static __KEY: $crate::thread::__OsLocalKeyInner<$t> = |
| 173 | $crate::thread::__OsLocalKeyInner::new(); |
Alex Crichton | cd74364e | 2015-12-11 20:42:29 | [diff] [blame] | 174 | |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 175 | __KEY.get(__init) |
Alex Burka | b6a2d7e | 2017-04-01 03:06:34 | [diff] [blame] | 176 | } |
Alex Crichton | cd74364e | 2015-12-11 20:42:29 | [diff] [blame] | 177 | |
Eduard-Mihai Burtescu | 92892d3 | 2017-08-08 15:22:51 | [diff] [blame] | 178 | unsafe { |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 179 | $crate::thread::LocalKey::new(__getit) |
Eduard-Mihai Burtescu | 92892d3 | 2017-08-08 15:22:51 | [diff] [blame] | 180 | } |
Eduard-Mihai Burtescu | 4e2be14 | 2017-08-13 13:42:10 | [diff] [blame] | 181 | } |
| 182 | }; |
| 183 | ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => { |
Eduard-Mihai Burtescu | 4e2be14 | 2017-08-13 13:42:10 | [diff] [blame] | 184 | $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = |
3442853561 | 936349c | 2019-11-06 08:39:48 | [diff] [blame] | 185 | $crate::__thread_local_inner!(@key $t, $init); |
Alex Burka | b6a2d7e | 2017-04-01 03:06:34 | [diff] [blame] | 186 | } |
Alex Crichton | 1b5f9cb | 2015-05-28 06:24:27 | [diff] [blame] | 187 | } |
| 188 | |
Lee Bousfield | 32ae12b | 2017-07-10 23:26:11 | [diff] [blame] | 189 | /// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with). |
Stjepan Glavina | c99f4c4 | 2018-02-27 16:00:01 | [diff] [blame] | 190 | #[stable(feature = "thread_local_try_with", since = "1.26.0")] |
Stjepan Glavina | d6c3196 | 2019-06-03 16:20:38 | [diff] [blame] | 191 | #[derive(Clone, Copy, Eq, PartialEq)] |
Lee Bousfield | 32ae12b | 2017-07-10 23:26:11 | [diff] [blame] | 192 | pub struct AccessError { |
| 193 | _private: (), |
| 194 | } |
| 195 | |
Stjepan Glavina | c99f4c4 | 2018-02-27 16:00:01 | [diff] [blame] | 196 | #[stable(feature = "thread_local_try_with", since = "1.26.0")] |
Lee Bousfield | 32ae12b | 2017-07-10 23:26:11 | [diff] [blame] | 197 | impl fmt::Debug for AccessError { |
Mazdak Farrokhzad | 379c380 | 2019-03-01 08:34:11 | [diff] [blame] | 198 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
Lee Bousfield | 32ae12b | 2017-07-10 23:26:11 | [diff] [blame] | 199 | f.debug_struct("AccessError").finish() |
| 200 | } |
| 201 | } |
| 202 | |
Stjepan Glavina | c99f4c4 | 2018-02-27 16:00:01 | [diff] [blame] | 203 | #[stable(feature = "thread_local_try_with", since = "1.26.0")] |
Lee Bousfield | 32ae12b | 2017-07-10 23:26:11 | [diff] [blame] | 204 | impl fmt::Display for AccessError { |
Mazdak Farrokhzad | 379c380 | 2019-03-01 08:34:11 | [diff] [blame] | 205 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
Lee Bousfield | 32ae12b | 2017-07-10 23:26:11 | [diff] [blame] | 206 | fmt::Display::fmt("already destroyed", f) |
| 207 | } |
| 208 | } |
| 209 | |
Stjepan Glavina | d2c9c12 | 2019-08-04 13:11:08 | [diff] [blame] | 210 | #[stable(feature = "thread_local_try_with", since = "1.26.0")] |
Stjepan Glavina | d6c3196 | 2019-06-03 16:20:38 | [diff] [blame] | 211 | impl Error for AccessError {} |
| 212 | |
Aaron Turon | 6bd3ab0 | 2015-03-20 07:46:13 | [diff] [blame] | 213 | impl<T: 'static> LocalKey<T> { |
Eduard Burtescu | 377b090 | 2015-05-27 08:18:36 | [diff] [blame] | 214 | #[doc(hidden)] |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 215 | #[unstable( |
| 216 | feature = "thread_local_internals", |
| 217 | reason = "recently added to create a key", |
| 218 | issue = "0" |
| 219 | )] |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 220 | pub const unsafe fn new(inner: unsafe fn() -> Option<&'static T>) -> LocalKey<T> { |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 221 | LocalKey { inner } |
Eduard Burtescu | 377b090 | 2015-05-27 08:18:36 | [diff] [blame] | 222 | } |
| 223 | |
Andrew Paseltiner | 6fa16d6 | 2015-04-13 14:21:32 | [diff] [blame] | 224 | /// Acquires a reference to the value in this TLS key. |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 225 | /// |
| 226 | /// This will lazily initialize the value if this thread has not referenced |
| 227 | /// this key yet. |
| 228 | /// |
| 229 | /// # Panics |
| 230 | /// |
| 231 | /// This function will `panic!()` if the key currently has its |
| 232 | /// destructor running, and it **may** panic if the destructor has |
| 233 | /// previously been run for this thread. |
Brian Anderson | b44ee37 | 2015-01-24 05:48:20 | [diff] [blame] | 234 | #[stable(feature = "rust1", since = "1.0.0")] |
Alex Crichton | 4ffd9f4 | 2014-12-10 15:49:45 | [diff] [blame] | 235 | pub fn with<F, R>(&'static self, f: F) -> R |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 236 | where |
| 237 | F: FnOnce(&T) -> R, |
| 238 | { |
| 239 | self.try_with(f).expect( |
| 240 | "cannot access a Thread Local Storage value \ |
| 241 | during or after destruction", |
| 242 | ) |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 243 | } |
| 244 | |
Lee Bousfield | 32ae12b | 2017-07-10 23:26:11 | [diff] [blame] | 245 | /// Acquires a reference to the value in this TLS key. |
| 246 | /// |
| 247 | /// This will lazily initialize the value if this thread has not referenced |
| 248 | /// this key yet. If the key has been destroyed (which may happen if this is called |
Michal 'vorner' Vaner | 771748d | 2018-06-26 20:58:25 | [diff] [blame] | 249 | /// in a destructor), this function will return an [`AccessError`](struct.AccessError.html). |
Lee Bousfield | 32ae12b | 2017-07-10 23:26:11 | [diff] [blame] | 250 | /// |
| 251 | /// # Panics |
| 252 | /// |
| 253 | /// This function will still `panic!()` if the key is uninitialized and the |
| 254 | /// key's initializer panics. |
Stjepan Glavina | c99f4c4 | 2018-02-27 16:00:01 | [diff] [blame] | 255 | #[stable(feature = "thread_local_try_with", since = "1.26.0")] |
Lee Bousfield | 32ae12b | 2017-07-10 23:26:11 | [diff] [blame] | 256 | pub fn try_with<F, R>(&'static self, f: F) -> Result<R, AccessError> |
Stjepan Glavina | c99f4c4 | 2018-02-27 16:00:01 | [diff] [blame] | 257 | where |
| 258 | F: FnOnce(&T) -> R, |
| 259 | { |
Lee Bousfield | 32ae12b | 2017-07-10 23:26:11 | [diff] [blame] | 260 | unsafe { |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 261 | let thread_local = (self.inner)().ok_or(AccessError { _private: () })?; |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 262 | Ok(f(thread_local)) |
| 263 | } |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | mod lazy { |
| 268 | use crate::cell::UnsafeCell; |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 269 | use crate::hint; |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 270 | use crate::mem; |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 271 | |
| 272 | pub struct LazyKeyInner<T> { |
| 273 | inner: UnsafeCell<Option<T>>, |
| 274 | } |
| 275 | |
| 276 | impl<T> LazyKeyInner<T> { |
| 277 | pub const fn new() -> LazyKeyInner<T> { |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 278 | LazyKeyInner { inner: UnsafeCell::new(None) } |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 279 | } |
| 280 | |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 281 | pub unsafe fn get(&self) -> Option<&'static T> { |
| 282 | (*self.inner.get()).as_ref() |
| 283 | } |
| 284 | |
| 285 | pub unsafe fn initialize<F: FnOnce() -> T>(&self, init: F) -> &'static T { |
| 286 | // Execute the initialization up front, *then* move it into our slot, |
| 287 | // just in case initialization fails. |
| 288 | let value = init(); |
| 289 | let ptr = self.inner.get(); |
| 290 | |
| 291 | // note that this can in theory just be `*ptr = Some(value)`, but due to |
| 292 | // the compiler will currently codegen that pattern with something like: |
| 293 | // |
| 294 | // ptr::drop_in_place(ptr) |
| 295 | // ptr::write(ptr, Some(value)) |
| 296 | // |
| 297 | // Due to this pattern it's possible for the destructor of the value in |
| 298 | // `ptr` (e.g., if this is being recursively initialized) to re-access |
| 299 | // TLS, in which case there will be a `&` and `&mut` pointer to the same |
| 300 | // value (an aliasing violation). To avoid setting the "I'm running a |
| 301 | // destructor" flag we just use `mem::replace` which should sequence the |
| 302 | // operations a little differently and make this safe to call. |
| 303 | mem::replace(&mut *ptr, Some(value)); |
| 304 | |
| 305 | // After storing `Some` we want to get a reference to the contents of |
| 306 | // what we just stored. While we could use `unwrap` here and it should |
| 307 | // always work it empirically doesn't seem to always get optimized away, |
| 308 | // which means that using something like `try_with` can pull in |
| 309 | // panicking code and cause a large size bloat. |
| 310 | match *ptr { |
| 311 | Some(ref x) => x, |
| 312 | None => hint::unreachable_unchecked(), |
| 313 | } |
| 314 | } |
| 315 | |
tyler | 060d8bb | 2019-05-04 00:01:53 | [diff] [blame] | 316 | #[allow(unused)] |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 317 | pub unsafe fn take(&mut self) -> Option<T> { |
| 318 | (*self.inner.get()).take() |
Lee Bousfield | 32ae12b | 2017-07-10 23:26:11 | [diff] [blame] | 319 | } |
| 320 | } |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 321 | } |
| 322 | |
Alex Crichton | c3a5d6b | 2018-03-29 21:59:13 | [diff] [blame] | 323 | /// On some platforms like wasm32 there's no threads, so no need to generate |
| 324 | /// thread locals and we can instead just use plain statics! |
| 325 | #[doc(hidden)] |
Alex Crichton | cbe9f33 | 2018-10-10 06:10:25 | [diff] [blame] | 326 | #[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))] |
Alex Crichton | c3a5d6b | 2018-03-29 21:59:13 | [diff] [blame] | 327 | pub mod statik { |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 328 | use super::lazy::LazyKeyInner; |
Taiki Endo | 93b6d9e | 2019-02-10 19:23:21 | [diff] [blame] | 329 | use crate::fmt; |
Alex Crichton | c3a5d6b | 2018-03-29 21:59:13 | [diff] [blame] | 330 | |
| 331 | pub struct Key<T> { |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 332 | inner: LazyKeyInner<T>, |
Alex Crichton | c3a5d6b | 2018-03-29 21:59:13 | [diff] [blame] | 333 | } |
| 334 | |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 335 | unsafe impl<T> Sync for Key<T> {} |
Alex Crichton | c3a5d6b | 2018-03-29 21:59:13 | [diff] [blame] | 336 | |
| 337 | impl<T> fmt::Debug for Key<T> { |
Mazdak Farrokhzad | 379c380 | 2019-03-01 08:34:11 | [diff] [blame] | 338 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
Alex Crichton | c3a5d6b | 2018-03-29 21:59:13 | [diff] [blame] | 339 | f.pad("Key { .. }") |
| 340 | } |
| 341 | } |
| 342 | |
| 343 | impl<T> Key<T> { |
| 344 | pub const fn new() -> Key<T> { |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 345 | Key { inner: LazyKeyInner::new() } |
Alex Crichton | c3a5d6b | 2018-03-29 21:59:13 | [diff] [blame] | 346 | } |
| 347 | |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 348 | pub unsafe fn get(&self, init: fn() -> T) -> Option<&'static T> { |
| 349 | let value = match self.inner.get() { |
| 350 | Some(ref value) => value, |
| 351 | None => self.inner.initialize(init), |
| 352 | }; |
| 353 | Some(value) |
Alex Crichton | c3a5d6b | 2018-03-29 21:59:13 | [diff] [blame] | 354 | } |
| 355 | } |
| 356 | } |
| 357 | |
Alex Crichton | 0e154aa | 2015-04-15 19:26:27 | [diff] [blame] | 358 | #[doc(hidden)] |
Alex Crichton | 06540cb | 2016-02-17 07:07:09 | [diff] [blame] | 359 | #[cfg(target_thread_local)] |
| 360 | pub mod fast { |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 361 | use super::lazy::LazyKeyInner; |
| 362 | use crate::cell::Cell; |
Taiki Endo | 93b6d9e | 2019-02-10 19:23:21 | [diff] [blame] | 363 | use crate::fmt; |
| 364 | use crate::mem; |
tyler | 7acfb99 | 2019-05-01 01:24:38 | [diff] [blame] | 365 | use crate::sys::fast_thread_local::register_dtor; |
Alex Crichton | 06540cb | 2016-02-17 07:07:09 | [diff] [blame] | 366 | |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 367 | #[derive(Copy, Clone)] |
| 368 | enum DtorState { |
| 369 | Unregistered, |
| 370 | Registered, |
| 371 | RunningOrHasRun, |
| 372 | } |
| 373 | |
tyler | 1a7f774 | 2019-05-11 00:29:43 | [diff] [blame] | 374 | // This data structure has been carefully constructed so that the fast path |
| 375 | // only contains one branch on x86. That optimization is necessary to avoid |
| 376 | // duplicated tls lookups on OSX. |
tyler | 2b3642b | 2019-05-11 17:42:44 | [diff] [blame] | 377 | // |
tyler | 1a7f774 | 2019-05-11 00:29:43 | [diff] [blame] | 378 | // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 |
Alex Crichton | 06540cb | 2016-02-17 07:07:09 | [diff] [blame] | 379 | pub struct Key<T> { |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 380 | // If `LazyKeyInner::get` returns `None`, that indicates either: |
| 381 | // * The value has never been initialized |
| 382 | // * The value is being recursively initialized |
| 383 | // * The value has already been destroyed or is being destroyed |
| 384 | // To determine which kind of `None`, check `dtor_state`. |
| 385 | // |
| 386 | // This is very optimizer friendly for the fast path - initialized but |
| 387 | // not yet dropped. |
| 388 | inner: LazyKeyInner<T>, |
Alex Crichton | 06540cb | 2016-02-17 07:07:09 | [diff] [blame] | 389 | |
| 390 | // Metadata to keep track of the state of the destructor. Remember that |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 391 | // this variable is thread-local, not global. |
| 392 | dtor_state: Cell<DtorState>, |
Alex Crichton | 06540cb | 2016-02-17 07:07:09 | [diff] [blame] | 393 | } |
| 394 | |
| 395 | impl<T> fmt::Debug for Key<T> { |
Mazdak Farrokhzad | 379c380 | 2019-03-01 08:34:11 | [diff] [blame] | 396 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
Alex Crichton | 06540cb | 2016-02-17 07:07:09 | [diff] [blame] | 397 | f.pad("Key { .. }") |
| 398 | } |
| 399 | } |
| 400 | |
Alex Crichton | 06540cb | 2016-02-17 07:07:09 | [diff] [blame] | 401 | impl<T> Key<T> { |
| 402 | pub const fn new() -> Key<T> { |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 403 | Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) } |
Alex Crichton | 06540cb | 2016-02-17 07:07:09 | [diff] [blame] | 404 | } |
| 405 | |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 406 | pub unsafe fn get<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> { |
| 407 | match self.inner.get() { |
| 408 | Some(val) => Some(val), |
tyler | 1a7f774 | 2019-05-11 00:29:43 | [diff] [blame] | 409 | None => self.try_initialize(init), |
Alex Crichton | 06540cb | 2016-02-17 07:07:09 | [diff] [blame] | 410 | } |
Alex Crichton | 06540cb | 2016-02-17 07:07:09 | [diff] [blame] | 411 | } |
| 412 | |
tyler | 1a7f774 | 2019-05-11 00:29:43 | [diff] [blame] | 413 | // `try_initialize` is only called once per fast thread local variable, |
tyler | 9289d03 | 2019-05-15 14:18:24 | [diff] [blame] | 414 | // except in corner cases where thread_local dtors reference other |
| 415 | // thread_local's, or it is being recursively initialized. |
| 416 | // |
| 417 | // Macos: Inlining this function can cause two `tlv_get_addr` calls to |
| 418 | // be performed for every call to `Key::get`. The #[cold] hint makes |
| 419 | // that less likely. |
| 420 | // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 421 | #[cold] |
tyler | 1a7f774 | 2019-05-11 00:29:43 | [diff] [blame] | 422 | unsafe fn try_initialize<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> { |
tyler | 9289d03 | 2019-05-15 14:18:24 | [diff] [blame] | 423 | if !mem::needs_drop::<T>() || self.try_register_dtor() { |
tyler | 1a7f774 | 2019-05-11 00:29:43 | [diff] [blame] | 424 | Some(self.inner.initialize(init)) |
tyler | 9289d03 | 2019-05-15 14:18:24 | [diff] [blame] | 425 | } else { |
| 426 | None |
tyler | 1a7f774 | 2019-05-11 00:29:43 | [diff] [blame] | 427 | } |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 428 | } |
| 429 | |
tyler | 9289d03 | 2019-05-15 14:18:24 | [diff] [blame] | 430 | // `try_register_dtor` is only called once per fast thread local |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 431 | // variable, except in corner cases where thread_local dtors reference |
| 432 | // other thread_local's, or it is being recursively initialized. |
tyler | 9289d03 | 2019-05-15 14:18:24 | [diff] [blame] | 433 | unsafe fn try_register_dtor(&self) -> bool { |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 434 | match self.dtor_state.get() { |
| 435 | DtorState::Unregistered => { |
| 436 | // dtor registration happens before initialization. |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 437 | register_dtor(self as *const _ as *mut u8, destroy_value::<T>); |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 438 | self.dtor_state.set(DtorState::Registered); |
tyler | 9289d03 | 2019-05-15 14:18:24 | [diff] [blame] | 439 | true |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 440 | } |
| 441 | DtorState::Registered => { |
| 442 | // recursively initialized |
tyler | 9289d03 | 2019-05-15 14:18:24 | [diff] [blame] | 443 | true |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 444 | } |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 445 | DtorState::RunningOrHasRun => false, |
Alex Crichton | 06540cb | 2016-02-17 07:07:09 | [diff] [blame] | 446 | } |
Alex Crichton | 06540cb | 2016-02-17 07:07:09 | [diff] [blame] | 447 | } |
| 448 | } |
| 449 | |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 450 | unsafe extern "C" fn destroy_value<T>(ptr: *mut u8) { |
Alex Crichton | 06540cb | 2016-02-17 07:07:09 | [diff] [blame] | 451 | let ptr = ptr as *mut Key<T>; |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 452 | |
| 453 | // Right before we run the user destructor be sure to set the |
| 454 | // `Option<T>` to `None`, and `dtor_state` to `RunningOrHasRun`. This |
| 455 | // causes future calls to `get` to run `try_initialize_drop` again, |
| 456 | // which will now fail, and return `None`. |
| 457 | let value = (*ptr).inner.take(); |
| 458 | (*ptr).dtor_state.set(DtorState::RunningOrHasRun); |
| 459 | drop(value); |
Alex Crichton | 06540cb | 2016-02-17 07:07:09 | [diff] [blame] | 460 | } |
| 461 | } |
| 462 | |
| 463 | #[doc(hidden)] |
Alex Crichton | cd74364e | 2015-12-11 20:42:29 | [diff] [blame] | 464 | pub mod os { |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 465 | use super::lazy::LazyKeyInner; |
| 466 | use crate::cell::Cell; |
Taiki Endo | 93b6d9e | 2019-02-10 19:23:21 | [diff] [blame] | 467 | use crate::fmt; |
| 468 | use crate::marker; |
| 469 | use crate::ptr; |
| 470 | use crate::sys_common::thread_local::StaticKey as OsStaticKey; |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 471 | |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 472 | pub struct Key<T> { |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 473 | // OS-TLS key that we'll use to key off. |
Eduard Burtescu | 377b090 | 2015-05-27 08:18:36 | [diff] [blame] | 474 | os: OsStaticKey, |
| 475 | marker: marker::PhantomData<Cell<T>>, |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 476 | } |
| 477 | |
Corey Farwell | 86fc63e | 2016-11-25 18:21:49 | [diff] [blame] | 478 | impl<T> fmt::Debug for Key<T> { |
Mazdak Farrokhzad | 379c380 | 2019-03-01 08:34:11 | [diff] [blame] | 479 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
Corey Farwell | 86fc63e | 2016-11-25 18:21:49 | [diff] [blame] | 480 | f.pad("Key { .. }") |
| 481 | } |
| 482 | } |
| 483 | |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 484 | unsafe impl<T> Sync for Key<T> {} |
Flavio Percoco | fb803a8 | 2014-12-06 16:39:25 | [diff] [blame] | 485 | |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 486 | struct Value<T: 'static> { |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 487 | inner: LazyKeyInner<T>, |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 488 | key: &'static Key<T>, |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 489 | } |
| 490 | |
Eduard Burtescu | 377b090 | 2015-05-27 08:18:36 | [diff] [blame] | 491 | impl<T: 'static> Key<T> { |
| 492 | pub const fn new() -> Key<T> { |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 493 | Key { os: OsStaticKey::new(Some(destroy_value::<T>)), marker: marker::PhantomData } |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 494 | } |
| 495 | |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 496 | pub unsafe fn get(&'static self, init: fn() -> T) -> Option<&'static T> { |
Eduard-Mihai Burtescu | 92892d3 | 2017-08-08 15:22:51 | [diff] [blame] | 497 | let ptr = self.os.get() as *mut Value<T>; |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 498 | if ptr as usize > 1 { |
Mateusz Mikuła | bedbf3b | 2019-09-05 12:08:06 | [diff] [blame] | 499 | if let Some(ref value) = (*ptr).inner.get() { |
| 500 | return Some(value); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 501 | } |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 502 | } |
| 503 | self.try_initialize(init) |
| 504 | } |
| 505 | |
| 506 | // `try_initialize` is only called once per os thread local variable, |
| 507 | // except in corner cases where thread_local dtors reference other |
| 508 | // thread_local's, or it is being recursively initialized. |
| 509 | unsafe fn try_initialize(&'static self, init: fn() -> T) -> Option<&'static T> { |
| 510 | let ptr = self.os.get() as *mut Value<T>; |
| 511 | if ptr as usize == 1 { |
| 512 | // destructor is running |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 513 | return None; |
Manish Goregaokar | 6c4f0bf | 2016-02-04 12:20:20 | [diff] [blame] | 514 | } |
Eduard-Mihai Burtescu | 92892d3 | 2017-08-08 15:22:51 | [diff] [blame] | 515 | |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 516 | let ptr = if ptr.is_null() { |
| 517 | // If the lookup returned null, we haven't initialized our own |
| 518 | // local copy, so do that now. |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 519 | let ptr: Box<Value<T>> = box Value { inner: LazyKeyInner::new(), key: self }; |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 520 | let ptr = Box::into_raw(ptr); |
| 521 | self.os.set(ptr as *mut u8); |
| 522 | ptr |
| 523 | } else { |
| 524 | // recursive initialization |
| 525 | ptr |
Eduard-Mihai Burtescu | 92892d3 | 2017-08-08 15:22:51 | [diff] [blame] | 526 | }; |
tyler | dfe51a7 | 2019-05-03 05:40:52 | [diff] [blame] | 527 | |
| 528 | Some((*ptr).inner.initialize(init)) |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 529 | } |
| 530 | } |
| 531 | |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 532 | unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) { |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 533 | // The OS TLS ensures that this key contains a NULL value when this |
| 534 | // destructor starts to run. We set it back to a sentinel value of 1 to |
| 535 | // ensure that any future calls to `get` for this thread will return |
| 536 | // `None`. |
| 537 | // |
| 538 | // Note that to prevent an infinite loop we reset it back to null right |
| 539 | // before we return from the destructor ourselves. |
Alex Crichton | 0e154aa | 2015-04-15 19:26:27 | [diff] [blame] | 540 | let ptr = Box::from_raw(ptr as *mut Value<T>); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 541 | let key = ptr.key; |
| 542 | key.os.set(1 as *mut u8); |
| 543 | drop(ptr); |
we | 2c2480d | 2015-01-19 05:27:09 | [diff] [blame] | 544 | key.os.set(ptr::null_mut()); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 545 | } |
| 546 | } |
| 547 | |
Brian Anderson | 096670c | 2016-09-22 20:04:48 | [diff] [blame] | 548 | #[cfg(all(test, not(target_os = "emscripten")))] |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 549 | mod tests { |
Taiki Endo | 93b6d9e | 2019-02-10 19:23:21 | [diff] [blame] | 550 | use crate::cell::{Cell, UnsafeCell}; |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 551 | use crate::sync::mpsc::{channel, Sender}; |
Taiki Endo | 93b6d9e | 2019-02-10 19:23:21 | [diff] [blame] | 552 | use crate::thread; |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 553 | |
| 554 | struct Foo(Sender<()>); |
| 555 | |
| 556 | impl Drop for Foo { |
| 557 | fn drop(&mut self) { |
| 558 | let Foo(ref s) = *self; |
Alex Crichton | bc83a00 | 2014-12-23 19:53:35 | [diff] [blame] | 559 | s.send(()).unwrap(); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 560 | } |
| 561 | } |
| 562 | |
| 563 | #[test] |
| 564 | fn smoke_no_dtor() { |
Eduard Burtescu | 377b090 | 2015-05-27 08:18:36 | [diff] [blame] | 565 | thread_local!(static FOO: Cell<i32> = Cell::new(1)); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 566 | |
Eduard Burtescu | 377b090 | 2015-05-27 08:18:36 | [diff] [blame] | 567 | FOO.with(|f| { |
| 568 | assert_eq!(f.get(), 1); |
| 569 | f.set(2); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 570 | }); |
| 571 | let (tx, rx) = channel(); |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 572 | let _t = thread::spawn(move || { |
Eduard Burtescu | 377b090 | 2015-05-27 08:18:36 | [diff] [blame] | 573 | FOO.with(|f| { |
| 574 | assert_eq!(f.get(), 1); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 575 | }); |
Alex Crichton | bc83a00 | 2014-12-23 19:53:35 | [diff] [blame] | 576 | tx.send(()).unwrap(); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 577 | }); |
Alex Crichton | bc83a00 | 2014-12-23 19:53:35 | [diff] [blame] | 578 | rx.recv().unwrap(); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 579 | |
Eduard Burtescu | 377b090 | 2015-05-27 08:18:36 | [diff] [blame] | 580 | FOO.with(|f| { |
| 581 | assert_eq!(f.get(), 2); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 582 | }); |
| 583 | } |
| 584 | |
| 585 | #[test] |
Alex Crichton | be11aa6 | 2014-12-30 23:20:47 | [diff] [blame] | 586 | fn states() { |
| 587 | struct Foo; |
| 588 | impl Drop for Foo { |
| 589 | fn drop(&mut self) { |
Stjepan Glavina | 27fae2b | 2018-02-28 17:59:12 | [diff] [blame] | 590 | assert!(FOO.try_with(|_| ()).is_err()); |
Alex Crichton | be11aa6 | 2014-12-30 23:20:47 | [diff] [blame] | 591 | } |
| 592 | } |
Stjepan Glavina | cb56b2d | 2018-02-28 23:07:27 | [diff] [blame] | 593 | thread_local!(static FOO: Foo = Foo); |
Alex Crichton | be11aa6 | 2014-12-30 23:20:47 | [diff] [blame] | 594 | |
Aaron Turon | d0de2b4 | 2015-02-17 23:10:25 | [diff] [blame] | 595 | thread::spawn(|| { |
Stjepan Glavina | 27fae2b | 2018-02-28 17:59:12 | [diff] [blame] | 596 | assert!(FOO.try_with(|_| ()).is_ok()); |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 597 | }) |
| 598 | .join() |
| 599 | .ok() |
| 600 | .expect("thread panicked"); |
Alex Crichton | be11aa6 | 2014-12-30 23:20:47 | [diff] [blame] | 601 | } |
| 602 | |
| 603 | #[test] |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 604 | fn smoke_dtor() { |
Eduard Burtescu | 377b090 | 2015-05-27 08:18:36 | [diff] [blame] | 605 | thread_local!(static FOO: UnsafeCell<Option<Foo>> = UnsafeCell::new(None)); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 606 | |
| 607 | let (tx, rx) = channel(); |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 608 | let _t = thread::spawn(move || unsafe { |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 609 | let mut tx = Some(tx); |
| 610 | FOO.with(|f| { |
| 611 | *f.get() = Some(Foo(tx.take().unwrap())); |
| 612 | }); |
| 613 | }); |
Alex Crichton | bc83a00 | 2014-12-23 19:53:35 | [diff] [blame] | 614 | rx.recv().unwrap(); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 615 | } |
| 616 | |
| 617 | #[test] |
| 618 | fn circular() { |
| 619 | struct S1; |
| 620 | struct S2; |
Eduard Burtescu | 377b090 | 2015-05-27 08:18:36 | [diff] [blame] | 621 | thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None)); |
| 622 | thread_local!(static K2: UnsafeCell<Option<S2>> = UnsafeCell::new(None)); |
Vojtech Kral | e5e76e9 | 2015-02-23 16:24:50 | [diff] [blame] | 623 | static mut HITS: u32 = 0; |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 624 | |
| 625 | impl Drop for S1 { |
| 626 | fn drop(&mut self) { |
| 627 | unsafe { |
Stjepan Glavina | cb56b2d | 2018-02-28 23:07:27 | [diff] [blame] | 628 | HITS += 1; |
Stjepan Glavina | 27fae2b | 2018-02-28 17:59:12 | [diff] [blame] | 629 | if K2.try_with(|_| ()).is_err() { |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 630 | assert_eq!(HITS, 3); |
| 631 | } else { |
| 632 | if HITS == 1 { |
| 633 | K2.with(|s| *s.get() = Some(S2)); |
| 634 | } else { |
| 635 | assert_eq!(HITS, 3); |
| 636 | } |
| 637 | } |
| 638 | } |
| 639 | } |
| 640 | } |
| 641 | impl Drop for S2 { |
| 642 | fn drop(&mut self) { |
| 643 | unsafe { |
| 644 | HITS += 1; |
Stjepan Glavina | 27fae2b | 2018-02-28 17:59:12 | [diff] [blame] | 645 | assert!(K1.try_with(|_| ()).is_ok()); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 646 | assert_eq!(HITS, 2); |
| 647 | K1.with(|s| *s.get() = Some(S1)); |
| 648 | } |
| 649 | } |
| 650 | } |
| 651 | |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 652 | thread::spawn(move || { |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 653 | drop(S1); |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 654 | }) |
| 655 | .join() |
| 656 | .ok() |
| 657 | .expect("thread panicked"); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 658 | } |
| 659 | |
| 660 | #[test] |
| 661 | fn self_referential() { |
| 662 | struct S1; |
Eduard Burtescu | 377b090 | 2015-05-27 08:18:36 | [diff] [blame] | 663 | thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None)); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 664 | |
| 665 | impl Drop for S1 { |
| 666 | fn drop(&mut self) { |
Stjepan Glavina | 27fae2b | 2018-02-28 17:59:12 | [diff] [blame] | 667 | assert!(K1.try_with(|_| ()).is_err()); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 668 | } |
| 669 | } |
| 670 | |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 671 | thread::spawn(move || unsafe { |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 672 | K1.with(|s| *s.get() = Some(S1)); |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 673 | }) |
| 674 | .join() |
| 675 | .ok() |
| 676 | .expect("thread panicked"); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 677 | } |
| 678 | |
Alex Crichton | b960de0 | 2016-01-29 21:46:47 | [diff] [blame] | 679 | // Note that this test will deadlock if TLS destructors aren't run (this |
tyler | 1a51bb8 | 2019-01-16 04:09:06 | [diff] [blame] | 680 | // requires the destructor to be run to pass the test). |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 681 | #[test] |
| 682 | fn dtors_in_dtors_in_dtors() { |
| 683 | struct S1(Sender<()>); |
Eduard Burtescu | 377b090 | 2015-05-27 08:18:36 | [diff] [blame] | 684 | thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None)); |
| 685 | thread_local!(static K2: UnsafeCell<Option<Foo>> = UnsafeCell::new(None)); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 686 | |
| 687 | impl Drop for S1 { |
| 688 | fn drop(&mut self) { |
| 689 | let S1(ref tx) = *self; |
| 690 | unsafe { |
Stjepan Glavina | 27fae2b | 2018-02-28 17:59:12 | [diff] [blame] | 691 | let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone()))); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 692 | } |
| 693 | } |
| 694 | } |
| 695 | |
| 696 | let (tx, rx) = channel(); |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 697 | let _t = thread::spawn(move || unsafe { |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 698 | let mut tx = Some(tx); |
| 699 | K1.with(|s| *s.get() = Some(S1(tx.take().unwrap()))); |
| 700 | }); |
Alex Crichton | bc83a00 | 2014-12-23 19:53:35 | [diff] [blame] | 701 | rx.recv().unwrap(); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 702 | } |
| 703 | } |
| 704 | |
| 705 | #[cfg(test)] |
| 706 | mod dynamic_tests { |
Taiki Endo | 93b6d9e | 2019-02-10 19:23:21 | [diff] [blame] | 707 | use crate::cell::RefCell; |
| 708 | use crate::collections::HashMap; |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 709 | |
| 710 | #[test] |
| 711 | fn smoke() { |
David Tolnay | 4436c9d | 2019-11-27 18:29:00 | [diff] [blame^] | 712 | fn square(i: i32) -> i32 { |
| 713 | i * i |
| 714 | } |
Vojtech Kral | e5e76e9 | 2015-02-23 16:24:50 | [diff] [blame] | 715 | thread_local!(static FOO: i32 = square(3)); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 716 | |
| 717 | FOO.with(|f| { |
| 718 | assert_eq!(*f, 9); |
| 719 | }); |
| 720 | } |
| 721 | |
| 722 | #[test] |
| 723 | fn hashmap() { |
Vojtech Kral | e5e76e9 | 2015-02-23 16:24:50 | [diff] [blame] | 724 | fn map() -> RefCell<HashMap<i32, i32>> { |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 725 | let mut m = HashMap::new(); |
| 726 | m.insert(1, 2); |
| 727 | RefCell::new(m) |
| 728 | } |
Vojtech Kral | e5e76e9 | 2015-02-23 16:24:50 | [diff] [blame] | 729 | thread_local!(static FOO: RefCell<HashMap<i32, i32>> = map()); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 730 | |
| 731 | FOO.with(|map| { |
Niko Matsakis | 8e58af4 | 2015-03-22 01:15:47 | [diff] [blame] | 732 | assert_eq!(map.borrow()[&1], 2); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 733 | }); |
| 734 | } |
| 735 | |
| 736 | #[test] |
| 737 | fn refcell_vec() { |
Vojtech Kral | e5e76e9 | 2015-02-23 16:24:50 | [diff] [blame] | 738 | thread_local!(static FOO: RefCell<Vec<u32>> = RefCell::new(vec![1, 2, 3])); |
Alex Crichton | a9c1152 | 2014-11-14 22:20:57 | [diff] [blame] | 739 | |
| 740 | FOO.with(|vec| { |
| 741 | assert_eq!(vec.borrow().len(), 3); |
| 742 | vec.borrow_mut().push(4); |
| 743 | assert_eq!(vec.borrow()[3], 4); |
| 744 | }); |
| 745 | } |
| 746 | } |