]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | //! OS-based thread local storage |
2 | //! | |
3 | //! This module provides an implementation of OS-based thread local storage, | |
4 | //! using the native OS-provided facilities (think `TlsAlloc` or | |
5 | //! `pthread_setspecific`). The interface of this differs from the other types | |
6 | //! of thread-local-storage provided in this crate in that OS-based TLS can only | |
3dfed10e | 7 | //! get/set pointer-sized data, possibly with an associated destructor. |
1a4d82fc JJ |
8 | //! |
9 | //! This module also provides two flavors of TLS. One is intended for static | |
10 | //! initialization, and does not contain a `Drop` implementation to deallocate | |
11 | //! the OS-TLS key. The other is a type which does implement `Drop` and hence | |
12 | //! has a safe interface. | |
13 | //! | |
14 | //! # Usage | |
15 | //! | |
16 | //! This module should likely not be used directly unless other primitives are | |
3dfed10e | 17 | //! being built on. Types such as `thread_local::spawn::Key` are likely much |
1a4d82fc JJ |
18 | //! more useful in practice than this OS-based version which likely requires |
19 | //! unsafe code to interoperate with. | |
20 | //! | |
c34b1796 | 21 | //! # Examples |
1a4d82fc JJ |
22 | //! |
23 | //! Using a dynamically allocated TLS key. Note that this key can be shared | |
24 | //! among many threads via an `Arc`. | |
25 | //! | |
041b39d2 | 26 | //! ```ignore (cannot-doctest-private-modules) |
1a4d82fc JJ |
27 | //! let key = Key::new(None); |
28 | //! assert!(key.get().is_null()); | |
29 | //! key.set(1 as *mut u8); | |
30 | //! assert!(!key.get().is_null()); | |
31 | //! | |
32 | //! drop(key); // deallocate this TLS slot. | |
33 | //! ``` | |
34 | //! | |
35 | //! Sometimes a statically allocated key is either required or easier to work | |
36 | //! with, however. | |
37 | //! | |
041b39d2 | 38 | //! ```ignore (cannot-doctest-private-modules) |
1a4d82fc JJ |
39 | //! static KEY: StaticKey = INIT; |
40 | //! | |
41 | //! unsafe { | |
42 | //! assert!(KEY.get().is_null()); | |
43 | //! KEY.set(1 as *mut u8); | |
44 | //! } | |
45 | //! ``` | |
46 | ||
47 | #![allow(non_camel_case_types)] | |
dfeec247 | 48 | #![unstable(feature = "thread_local_internals", issue = "none")] |
923072b8 | 49 | #![allow(dead_code)] |
1a4d82fc | 50 | |
1b1a35ee XL |
51 | #[cfg(test)] |
52 | mod tests; | |
53 | ||
532ac7d7 | 54 | use crate::sync::atomic::{self, AtomicUsize, Ordering}; |
3dfed10e | 55 | use crate::sys::thread_local_key as imp; |
1b1a35ee | 56 | use crate::sys_common::mutex::StaticMutex; |
1a4d82fc JJ |
57 | |
58 | /// A type for TLS keys that are statically allocated. | |
59 | /// | |
60 | /// This type is entirely `unsafe` to use as it does not protect against | |
61 | /// use-after-deallocation or use-during-deallocation. | |
62 | /// | |
63 | /// The actual OS-TLS key is lazily allocated when this is used for the first | |
64 | /// time. The key is also deallocated when the Rust runtime exits or `destroy` | |
65 | /// is called, whichever comes first. | |
66 | /// | |
c34b1796 | 67 | /// # Examples |
1a4d82fc | 68 | /// |
041b39d2 | 69 | /// ```ignore (cannot-doctest-private-modules) |
1a4d82fc JJ |
70 | /// use tls::os::{StaticKey, INIT}; |
71 | /// | |
f2b60f7d | 72 | /// // Use a regular global static to store the key. |
1a4d82fc JJ |
73 | /// static KEY: StaticKey = INIT; |
74 | /// | |
f2b60f7d | 75 | /// // The state provided via `get` and `set` is thread-local. |
1a4d82fc JJ |
76 | /// unsafe { |
77 | /// assert!(KEY.get().is_null()); | |
78 | /// KEY.set(1 as *mut u8); | |
79 | /// } | |
80 | /// ``` | |
81 | pub struct StaticKey { | |
62682a34 SL |
82 | /// Inner static TLS key (internals). |
83 | key: AtomicUsize, | |
1a4d82fc JJ |
84 | /// Destructor for the TLS value. |
85 | /// | |
86 | /// See `Key::new` for information about when the destructor runs and how | |
87 | /// it runs. | |
dfeec247 | 88 | dtor: Option<unsafe extern "C" fn(*mut u8)>, |
1a4d82fc JJ |
89 | } |
90 | ||
91 | /// A type for a safely managed OS-based TLS slot. | |
92 | /// | |
93 | /// This type allocates an OS TLS key when it is initialized and will deallocate | |
94 | /// the key when it falls out of scope. When compared with `StaticKey`, this | |
95 | /// type is entirely safe to use. | |
96 | /// | |
97 | /// Implementations will likely, however, contain unsafe code as this type only | |
62682a34 | 98 | /// operates on `*mut u8`, a raw pointer. |
1a4d82fc | 99 | /// |
c34b1796 | 100 | /// # Examples |
1a4d82fc | 101 | /// |
041b39d2 | 102 | /// ```ignore (cannot-doctest-private-modules) |
1a4d82fc JJ |
103 | /// use tls::os::Key; |
104 | /// | |
105 | /// let key = Key::new(None); | |
106 | /// assert!(key.get().is_null()); | |
107 | /// key.set(1 as *mut u8); | |
108 | /// assert!(!key.get().is_null()); | |
109 | /// | |
110 | /// drop(key); // deallocate this TLS slot. | |
111 | /// ``` | |
112 | pub struct Key { | |
113 | key: imp::Key, | |
114 | } | |
115 | ||
116 | /// Constant initialization value for static TLS keys. | |
117 | /// | |
118 | /// This value specifies no destructor by default. | |
62682a34 | 119 | pub const INIT: StaticKey = StaticKey::new(None); |
1a4d82fc | 120 | |
1a4d82fc | 121 | impl StaticKey { |
1b1a35ee | 122 | #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] |
dfeec247 XL |
123 | pub const fn new(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> StaticKey { |
124 | StaticKey { key: atomic::AtomicUsize::new(0), dtor } | |
62682a34 SL |
125 | } |
126 | ||
1a4d82fc JJ |
127 | /// Gets the value associated with this TLS key |
128 | /// | |
129 | /// This will lazily allocate a TLS key from the OS if one has not already | |
130 | /// been allocated. | |
131 | #[inline] | |
dfeec247 XL |
132 | pub unsafe fn get(&self) -> *mut u8 { |
133 | imp::get(self.key()) | |
134 | } | |
1a4d82fc JJ |
135 | |
136 | /// Sets this TLS key to a new value. | |
137 | /// | |
138 | /// This will lazily allocate a TLS key from the OS if one has not already | |
139 | /// been allocated. | |
140 | #[inline] | |
dfeec247 XL |
141 | pub unsafe fn set(&self, val: *mut u8) { |
142 | imp::set(self.key(), val) | |
143 | } | |
1a4d82fc | 144 | |
1a4d82fc JJ |
145 | #[inline] |
146 | unsafe fn key(&self) -> imp::Key { | |
62682a34 | 147 | match self.key.load(Ordering::Relaxed) { |
1a4d82fc | 148 | 0 => self.lazy_init() as imp::Key, |
dfeec247 | 149 | n => n as imp::Key, |
1a4d82fc JJ |
150 | } |
151 | } | |
152 | ||
c34b1796 | 153 | unsafe fn lazy_init(&self) -> usize { |
7cac9316 XL |
154 | // Currently the Windows implementation of TLS is pretty hairy, and |
155 | // it greatly simplifies creation if we just synchronize everything. | |
156 | // | |
157 | // Additionally a 0-index of a tls key hasn't been seen on windows, so | |
158 | // we just simplify the whole branch. | |
159 | if imp::requires_synchronized_create() { | |
b7449926 XL |
160 | // We never call `INIT_LOCK.init()`, so it is UB to attempt to |
161 | // acquire this mutex reentrantly! | |
1b1a35ee | 162 | static INIT_LOCK: StaticMutex = StaticMutex::new(); |
94b46f34 | 163 | let _guard = INIT_LOCK.lock(); |
7cac9316 XL |
164 | let mut key = self.key.load(Ordering::SeqCst); |
165 | if key == 0 { | |
166 | key = imp::create(self.dtor) as usize; | |
167 | self.key.store(key, Ordering::SeqCst); | |
168 | } | |
83c7162d | 169 | rtassert!(key != 0); |
dfeec247 | 170 | return key; |
7cac9316 XL |
171 | } |
172 | ||
fc512014 | 173 | // POSIX allows the key created here to be 0, but the compare_exchange |
1a4d82fc JJ |
174 | // below relies on using 0 as a sentinel value to check who won the |
175 | // race to set the shared TLS key. As far as I know, there is no | |
176 | // guaranteed value that cannot be returned as a posix_key_create key, | |
177 | // so there is no value we can initialize the inner key with to | |
178 | // prove that it has not yet been set. As such, we'll continue using a | |
179 | // value of 0, but with some gyrations to make sure we have a non-0 | |
180 | // value returned from the creation routine. | |
181 | // FIXME: this is clearly a hack, and should be cleaned up. | |
182 | let key1 = imp::create(self.dtor); | |
183 | let key = if key1 != 0 { | |
184 | key1 | |
185 | } else { | |
186 | let key2 = imp::create(self.dtor); | |
187 | imp::destroy(key1); | |
188 | key2 | |
189 | }; | |
83c7162d | 190 | rtassert!(key != 0); |
fc512014 | 191 | match self.key.compare_exchange(0, key as usize, Ordering::SeqCst, Ordering::SeqCst) { |
1a4d82fc | 192 | // The CAS succeeded, so we've created the actual key |
fc512014 | 193 | Ok(_) => key as usize, |
1a4d82fc | 194 | // If someone beat us to the punch, use their key instead |
fc512014 | 195 | Err(n) => { |
dfeec247 XL |
196 | imp::destroy(key); |
197 | n | |
198 | } | |
1a4d82fc JJ |
199 | } |
200 | } | |
201 | } | |
202 | ||
203 | impl Key { | |
9346a6ac | 204 | /// Creates a new managed OS TLS key. |
1a4d82fc JJ |
205 | /// |
206 | /// This key will be deallocated when the key falls out of scope. | |
207 | /// | |
208 | /// The argument provided is an optionally-specified destructor for the | |
209 | /// value of this TLS key. When a thread exits and the value for this key | |
210 | /// is non-null the destructor will be invoked. The TLS value will be reset | |
211 | /// to null before the destructor is invoked. | |
212 | /// | |
213 | /// Note that the destructor will not be run when the `Key` goes out of | |
214 | /// scope. | |
215 | #[inline] | |
dfeec247 | 216 | pub fn new(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key { |
1a4d82fc JJ |
217 | Key { key: unsafe { imp::create(dtor) } } |
218 | } | |
219 | ||
220 | /// See StaticKey::get | |
221 | #[inline] | |
222 | pub fn get(&self) -> *mut u8 { | |
223 | unsafe { imp::get(self.key) } | |
224 | } | |
225 | ||
226 | /// See StaticKey::set | |
227 | #[inline] | |
228 | pub fn set(&self, val: *mut u8) { | |
229 | unsafe { imp::set(self.key, val) } | |
230 | } | |
231 | } | |
232 | ||
233 | impl Drop for Key { | |
234 | fn drop(&mut self) { | |
7cac9316 XL |
235 | // Right now Windows doesn't support TLS key destruction, but this also |
236 | // isn't used anywhere other than tests, so just leak the TLS key. | |
237 | // unsafe { imp::destroy(self.key) } | |
1a4d82fc JJ |
238 | } |
239 | } |