]> git.proxmox.com Git - rustc.git/blame - src/libstd/thread/scoped_tls.rs
Imported Upstream version 1.3.0+dfsg1
[rustc.git] / src / libstd / thread / scoped_tls.rs
CommitLineData
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
45use prelude::v1::*;
46
1a4d82fc 47#[doc(hidden)]
62682a34 48pub 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 60pub 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
71macro_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)]
88macro_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 103macro_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")]
122impl<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 227mod 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 251mod 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)]
279mod 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}