]>
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 | //! Scoped thread-local storage | |
12 | //! | |
13 | //! This module provides the ability to generate *scoped* thread-local | |
14 | //! variables. In this sense, scoped indicates that thread local storage | |
15 | //! actually stores a reference to a value, and this reference is only placed | |
16 | //! in storage for a scoped amount of time. | |
17 | //! | |
18 | //! There are no restrictions on what types can be placed into a scoped | |
19 | //! variable, but all scoped variables are initialized to the equivalent of | |
20 | //! null. Scoped thread local storage is useful when a value is present for a known | |
21 | //! period of time and it is not required to relinquish ownership of the | |
22 | //! contents. | |
23 | //! | |
c34b1796 | 24 | //! # Examples |
1a4d82fc JJ |
25 | //! |
26 | //! ``` | |
c1a9b12d SL |
27 | //! #![feature(scoped_tls)] |
28 | //! | |
c34b1796 | 29 | //! scoped_thread_local!(static FOO: u32); |
1a4d82fc JJ |
30 | //! |
31 | //! // Initially each scoped slot is empty. | |
32 | //! assert!(!FOO.is_set()); | |
33 | //! | |
34 | //! // When inserting a value, the value is only in place for the duration | |
35 | //! // of the closure specified. | |
36 | //! FOO.set(&1, || { | |
37 | //! FOO.with(|slot| { | |
38 | //! assert_eq!(*slot, 1); | |
39 | //! }); | |
40 | //! }); | |
41 | //! ``` | |
42 | ||
c34b1796 | 43 | #![unstable(feature = "thread_local_internals")] |
1a4d82fc JJ |
44 | |
45 | use prelude::v1::*; | |
46 | ||
1a4d82fc | 47 | #[doc(hidden)] |
62682a34 | 48 | pub use self::imp::KeyInner as __KeyInner; |
1a4d82fc JJ |
49 | |
50 | /// Type representing a thread local storage key corresponding to a reference | |
51 | /// to the type parameter `T`. | |
52 | /// | |
53 | /// Keys are statically allocated and can contain a reference to an instance of | |
54 | /// type `T` scoped to a particular lifetime. Keys provides two methods, `set` | |
55 | /// and `with`, both of which currently use closures to control the scope of | |
56 | /// their contents. | |
c34b1796 AL |
57 | #[unstable(feature = "scoped_tls", |
58 | reason = "scoped TLS has yet to have wide enough use to fully consider \ | |
59 | stabilizing its interface")] | |
62682a34 | 60 | pub struct ScopedKey<T> { inner: fn() -> &'static imp::KeyInner<T> } |
1a4d82fc JJ |
61 | |
62 | /// Declare a new scoped thread local storage key. | |
63 | /// | |
64 | /// This macro declares a `static` item on which methods are used to get and | |
65 | /// set the value stored within. | |
d9579d0f AL |
66 | /// |
67 | /// See [ScopedKey documentation](thread/struct.ScopedKey.html) for more | |
68 | /// information. | |
1a4d82fc | 69 | #[macro_export] |
c34b1796 | 70 | #[allow_internal_unstable] |
1a4d82fc JJ |
71 | macro_rules! scoped_thread_local { |
72 | (static $name:ident: $t:ty) => ( | |
c1a9b12d | 73 | static $name: $crate::thread::ScopedKey<$t> = |
62682a34 | 74 | __scoped_thread_local_inner!($t); |
1a4d82fc JJ |
75 | ); |
76 | (pub static $name:ident: $t:ty) => ( | |
c1a9b12d | 77 | pub static $name: $crate::thread::ScopedKey<$t> = |
62682a34 | 78 | __scoped_thread_local_inner!($t); |
1a4d82fc JJ |
79 | ); |
80 | } | |
81 | ||
62682a34 SL |
82 | #[doc(hidden)] |
83 | #[unstable(feature = "thread_local_internals", | |
84 | reason = "should not be necessary")] | |
1a4d82fc | 85 | #[macro_export] |
62682a34 SL |
86 | #[allow_internal_unstable] |
87 | #[cfg(no_elf_tls)] | |
88 | macro_rules! __scoped_thread_local_inner { | |
89 | ($t:ty) => {{ | |
c1a9b12d SL |
90 | static _KEY: $crate::thread::__ScopedKeyInner<$t> = |
91 | $crate::thread::__ScopedKeyInner::new(); | |
92 | fn _getit() -> &'static $crate::thread::__ScopedKeyInner<$t> { &_KEY } | |
93 | $crate::thread::ScopedKey::new(_getit) | |
62682a34 SL |
94 | }} |
95 | } | |
96 | ||
1a4d82fc | 97 | #[doc(hidden)] |
62682a34 SL |
98 | #[unstable(feature = "thread_local_internals", |
99 | reason = "should not be necessary")] | |
100 | #[macro_export] | |
c34b1796 | 101 | #[allow_internal_unstable] |
62682a34 | 102 | #[cfg(not(no_elf_tls))] |
1a4d82fc | 103 | macro_rules! __scoped_thread_local_inner { |
62682a34 | 104 | ($t:ty) => {{ |
1a4d82fc JJ |
105 | #[cfg_attr(not(any(windows, |
106 | target_os = "android", | |
107 | target_os = "ios", | |
c1a9b12d | 108 | target_os = "netbsd", |
85aaf69f | 109 | target_os = "openbsd", |
1a4d82fc JJ |
110 | target_arch = "aarch64")), |
111 | thread_local)] | |
c1a9b12d SL |
112 | static _KEY: $crate::thread::__ScopedKeyInner<$t> = |
113 | $crate::thread::__ScopedKeyInner::new(); | |
114 | fn _getit() -> &'static $crate::thread::__ScopedKeyInner<$t> { &_KEY } | |
115 | $crate::thread::ScopedKey::new(_getit) | |
62682a34 | 116 | }} |
d9579d0f AL |
117 | } |
118 | ||
c34b1796 AL |
119 | #[unstable(feature = "scoped_tls", |
120 | reason = "scoped TLS has yet to have wide enough use to fully consider \ | |
121 | stabilizing its interface")] | |
122 | impl<T> ScopedKey<T> { | |
62682a34 SL |
123 | #[doc(hidden)] |
124 | pub const fn new(inner: fn() -> &'static imp::KeyInner<T>) -> ScopedKey<T> { | |
125 | ScopedKey { inner: inner } | |
126 | } | |
127 | ||
9346a6ac | 128 | /// Inserts a value into this scoped thread local storage slot for a |
1a4d82fc JJ |
129 | /// duration of a closure. |
130 | /// | |
131 | /// While `cb` is running, the value `t` will be returned by `get` unless | |
132 | /// this function is called recursively inside of `cb`. | |
133 | /// | |
134 | /// Upon return, this function will restore the previous value, if any | |
135 | /// was available. | |
136 | /// | |
c34b1796 | 137 | /// # Examples |
1a4d82fc JJ |
138 | /// |
139 | /// ``` | |
c1a9b12d SL |
140 | /// #![feature(scoped_tls)] |
141 | /// | |
c34b1796 | 142 | /// scoped_thread_local!(static FOO: u32); |
1a4d82fc JJ |
143 | /// |
144 | /// FOO.set(&100, || { | |
145 | /// let val = FOO.with(|v| *v); | |
146 | /// assert_eq!(val, 100); | |
147 | /// | |
148 | /// // set can be called recursively | |
149 | /// FOO.set(&101, || { | |
150 | /// // ... | |
151 | /// }); | |
152 | /// | |
153 | /// // Recursive calls restore the previous value. | |
154 | /// let val = FOO.with(|v| *v); | |
155 | /// assert_eq!(val, 100); | |
156 | /// }); | |
157 | /// ``` | |
158 | pub fn set<R, F>(&'static self, t: &T, cb: F) -> R where | |
159 | F: FnOnce() -> R, | |
160 | { | |
161 | struct Reset<'a, T: 'a> { | |
62682a34 | 162 | key: &'a imp::KeyInner<T>, |
1a4d82fc JJ |
163 | val: *mut T, |
164 | } | |
d9579d0f | 165 | impl<'a, T> Drop for Reset<'a, T> { |
1a4d82fc JJ |
166 | fn drop(&mut self) { |
167 | unsafe { self.key.set(self.val) } | |
168 | } | |
169 | } | |
170 | ||
62682a34 | 171 | let inner = (self.inner)(); |
1a4d82fc | 172 | let prev = unsafe { |
62682a34 SL |
173 | let prev = inner.get(); |
174 | inner.set(t as *const T as *mut T); | |
1a4d82fc JJ |
175 | prev |
176 | }; | |
177 | ||
62682a34 | 178 | let _reset = Reset { key: inner, val: prev }; |
1a4d82fc JJ |
179 | cb() |
180 | } | |
181 | ||
9346a6ac | 182 | /// Gets a value out of this scoped variable. |
1a4d82fc JJ |
183 | /// |
184 | /// This function takes a closure which receives the value of this | |
185 | /// variable. | |
186 | /// | |
187 | /// # Panics | |
188 | /// | |
189 | /// This function will panic if `set` has not previously been called. | |
190 | /// | |
c34b1796 | 191 | /// # Examples |
1a4d82fc JJ |
192 | /// |
193 | /// ```no_run | |
c1a9b12d SL |
194 | /// #![feature(scoped_tls)] |
195 | /// | |
c34b1796 | 196 | /// scoped_thread_local!(static FOO: u32); |
1a4d82fc JJ |
197 | /// |
198 | /// FOO.with(|slot| { | |
199 | /// // work with `slot` | |
200 | /// }); | |
201 | /// ``` | |
202 | pub fn with<R, F>(&'static self, cb: F) -> R where | |
203 | F: FnOnce(&T) -> R | |
204 | { | |
205 | unsafe { | |
62682a34 | 206 | let ptr = (self.inner)().get(); |
1a4d82fc JJ |
207 | assert!(!ptr.is_null(), "cannot access a scoped thread local \ |
208 | variable without calling `set` first"); | |
209 | cb(&*ptr) | |
210 | } | |
211 | } | |
212 | ||
213 | /// Test whether this TLS key has been `set` for the current thread. | |
214 | pub fn is_set(&'static self) -> bool { | |
62682a34 | 215 | unsafe { !(self.inner)().get().is_null() } |
1a4d82fc JJ |
216 | } |
217 | } | |
218 | ||
85aaf69f SL |
219 | #[cfg(not(any(windows, |
220 | target_os = "android", | |
221 | target_os = "ios", | |
c1a9b12d | 222 | target_os = "netbsd", |
85aaf69f | 223 | target_os = "openbsd", |
d9579d0f AL |
224 | target_arch = "aarch64", |
225 | no_elf_tls)))] | |
62682a34 | 226 | #[doc(hidden)] |
1a4d82fc | 227 | mod imp { |
c1a9b12d | 228 | use cell::Cell; |
1a4d82fc | 229 | |
62682a34 | 230 | pub struct KeyInner<T> { inner: Cell<*mut T> } |
1a4d82fc JJ |
231 | |
232 | unsafe impl<T> ::marker::Sync for KeyInner<T> { } | |
233 | ||
1a4d82fc | 234 | impl<T> KeyInner<T> { |
62682a34 SL |
235 | pub const fn new() -> KeyInner<T> { |
236 | KeyInner { inner: Cell::new(0 as *mut _) } | |
237 | } | |
238 | pub unsafe fn set(&self, ptr: *mut T) { self.inner.set(ptr); } | |
239 | pub unsafe fn get(&self) -> *mut T { self.inner.get() } | |
1a4d82fc JJ |
240 | } |
241 | } | |
242 | ||
85aaf69f SL |
243 | #[cfg(any(windows, |
244 | target_os = "android", | |
245 | target_os = "ios", | |
c1a9b12d | 246 | target_os = "netbsd", |
85aaf69f | 247 | target_os = "openbsd", |
d9579d0f AL |
248 | target_arch = "aarch64", |
249 | no_elf_tls))] | |
62682a34 | 250 | #[doc(hidden)] |
1a4d82fc | 251 | mod imp { |
62682a34 SL |
252 | use prelude::v1::*; |
253 | ||
254 | use cell::Cell; | |
1a4d82fc JJ |
255 | use marker; |
256 | use sys_common::thread_local::StaticKey as OsStaticKey; | |
257 | ||
1a4d82fc JJ |
258 | pub struct KeyInner<T> { |
259 | pub inner: OsStaticKey, | |
c34b1796 | 260 | pub marker: marker::PhantomData<Cell<T>>, |
1a4d82fc JJ |
261 | } |
262 | ||
62682a34 | 263 | unsafe impl<T> marker::Sync for KeyInner<T> { } |
1a4d82fc | 264 | |
1a4d82fc | 265 | impl<T> KeyInner<T> { |
62682a34 SL |
266 | pub const fn new() -> KeyInner<T> { |
267 | KeyInner { | |
268 | inner: OsStaticKey::new(None), | |
269 | marker: marker::PhantomData | |
270 | } | |
271 | } | |
1a4d82fc | 272 | pub unsafe fn set(&self, ptr: *mut T) { self.inner.set(ptr as *mut _) } |
1a4d82fc JJ |
273 | pub unsafe fn get(&self) -> *mut T { self.inner.get() as *mut _ } |
274 | } | |
275 | } | |
276 | ||
277 | ||
278 | #[cfg(test)] | |
279 | mod tests { | |
280 | use cell::Cell; | |
281 | use prelude::v1::*; | |
282 | ||
c34b1796 | 283 | scoped_thread_local!(static FOO: u32); |
1a4d82fc JJ |
284 | |
285 | #[test] | |
286 | fn smoke() { | |
c34b1796 | 287 | scoped_thread_local!(static BAR: u32); |
1a4d82fc JJ |
288 | |
289 | assert!(!BAR.is_set()); | |
290 | BAR.set(&1, || { | |
291 | assert!(BAR.is_set()); | |
292 | BAR.with(|slot| { | |
293 | assert_eq!(*slot, 1); | |
294 | }); | |
295 | }); | |
296 | assert!(!BAR.is_set()); | |
297 | } | |
298 | ||
299 | #[test] | |
300 | fn cell_allowed() { | |
c34b1796 | 301 | scoped_thread_local!(static BAR: Cell<u32>); |
1a4d82fc JJ |
302 | |
303 | BAR.set(&Cell::new(1), || { | |
304 | BAR.with(|slot| { | |
305 | assert_eq!(slot.get(), 1); | |
306 | }); | |
307 | }); | |
308 | } | |
309 | ||
310 | #[test] | |
311 | fn scope_item_allowed() { | |
312 | assert!(!FOO.is_set()); | |
313 | FOO.set(&1, || { | |
314 | assert!(FOO.is_set()); | |
315 | FOO.with(|slot| { | |
316 | assert_eq!(*slot, 1); | |
317 | }); | |
318 | }); | |
319 | assert!(!FOO.is_set()); | |
320 | } | |
321 | } |