]>
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")] |
c34b1796 | 49 | #![allow(dead_code)] // sys isn't exported yet |
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 | /// | |
72 | /// static KEY: StaticKey = INIT; | |
73 | /// | |
74 | /// unsafe { | |
75 | /// assert!(KEY.get().is_null()); | |
76 | /// KEY.set(1 as *mut u8); | |
77 | /// } | |
78 | /// ``` | |
79 | pub struct StaticKey { | |
62682a34 SL |
80 | /// Inner static TLS key (internals). |
81 | key: AtomicUsize, | |
1a4d82fc JJ |
82 | /// Destructor for the TLS value. |
83 | /// | |
84 | /// See `Key::new` for information about when the destructor runs and how | |
85 | /// it runs. | |
dfeec247 | 86 | dtor: Option<unsafe extern "C" fn(*mut u8)>, |
1a4d82fc JJ |
87 | } |
88 | ||
89 | /// A type for a safely managed OS-based TLS slot. | |
90 | /// | |
91 | /// This type allocates an OS TLS key when it is initialized and will deallocate | |
92 | /// the key when it falls out of scope. When compared with `StaticKey`, this | |
93 | /// type is entirely safe to use. | |
94 | /// | |
95 | /// Implementations will likely, however, contain unsafe code as this type only | |
62682a34 | 96 | /// operates on `*mut u8`, a raw pointer. |
1a4d82fc | 97 | /// |
c34b1796 | 98 | /// # Examples |
1a4d82fc | 99 | /// |
041b39d2 | 100 | /// ```ignore (cannot-doctest-private-modules) |
1a4d82fc JJ |
101 | /// use tls::os::Key; |
102 | /// | |
103 | /// let key = Key::new(None); | |
104 | /// assert!(key.get().is_null()); | |
105 | /// key.set(1 as *mut u8); | |
106 | /// assert!(!key.get().is_null()); | |
107 | /// | |
108 | /// drop(key); // deallocate this TLS slot. | |
109 | /// ``` | |
110 | pub struct Key { | |
111 | key: imp::Key, | |
112 | } | |
113 | ||
114 | /// Constant initialization value for static TLS keys. | |
115 | /// | |
116 | /// This value specifies no destructor by default. | |
62682a34 | 117 | pub const INIT: StaticKey = StaticKey::new(None); |
1a4d82fc | 118 | |
1a4d82fc | 119 | impl StaticKey { |
1b1a35ee | 120 | #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] |
dfeec247 XL |
121 | pub const fn new(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> StaticKey { |
122 | StaticKey { key: atomic::AtomicUsize::new(0), dtor } | |
62682a34 SL |
123 | } |
124 | ||
1a4d82fc JJ |
125 | /// Gets the value associated with this TLS key |
126 | /// | |
127 | /// This will lazily allocate a TLS key from the OS if one has not already | |
128 | /// been allocated. | |
129 | #[inline] | |
dfeec247 XL |
130 | pub unsafe fn get(&self) -> *mut u8 { |
131 | imp::get(self.key()) | |
132 | } | |
1a4d82fc JJ |
133 | |
134 | /// Sets this TLS key to a new value. | |
135 | /// | |
136 | /// This will lazily allocate a TLS key from the OS if one has not already | |
137 | /// been allocated. | |
138 | #[inline] | |
dfeec247 XL |
139 | pub unsafe fn set(&self, val: *mut u8) { |
140 | imp::set(self.key(), val) | |
141 | } | |
1a4d82fc | 142 | |
1a4d82fc JJ |
143 | #[inline] |
144 | unsafe fn key(&self) -> imp::Key { | |
62682a34 | 145 | match self.key.load(Ordering::Relaxed) { |
1a4d82fc | 146 | 0 => self.lazy_init() as imp::Key, |
dfeec247 | 147 | n => n as imp::Key, |
1a4d82fc JJ |
148 | } |
149 | } | |
150 | ||
c34b1796 | 151 | unsafe fn lazy_init(&self) -> usize { |
7cac9316 XL |
152 | // Currently the Windows implementation of TLS is pretty hairy, and |
153 | // it greatly simplifies creation if we just synchronize everything. | |
154 | // | |
155 | // Additionally a 0-index of a tls key hasn't been seen on windows, so | |
156 | // we just simplify the whole branch. | |
157 | if imp::requires_synchronized_create() { | |
b7449926 XL |
158 | // We never call `INIT_LOCK.init()`, so it is UB to attempt to |
159 | // acquire this mutex reentrantly! | |
1b1a35ee | 160 | static INIT_LOCK: StaticMutex = StaticMutex::new(); |
94b46f34 | 161 | let _guard = INIT_LOCK.lock(); |
7cac9316 XL |
162 | let mut key = self.key.load(Ordering::SeqCst); |
163 | if key == 0 { | |
164 | key = imp::create(self.dtor) as usize; | |
165 | self.key.store(key, Ordering::SeqCst); | |
166 | } | |
83c7162d | 167 | rtassert!(key != 0); |
dfeec247 | 168 | return key; |
7cac9316 XL |
169 | } |
170 | ||
1a4d82fc JJ |
171 | // POSIX allows the key created here to be 0, but the compare_and_swap |
172 | // below relies on using 0 as a sentinel value to check who won the | |
173 | // race to set the shared TLS key. As far as I know, there is no | |
174 | // guaranteed value that cannot be returned as a posix_key_create key, | |
175 | // so there is no value we can initialize the inner key with to | |
176 | // prove that it has not yet been set. As such, we'll continue using a | |
177 | // value of 0, but with some gyrations to make sure we have a non-0 | |
178 | // value returned from the creation routine. | |
179 | // FIXME: this is clearly a hack, and should be cleaned up. | |
180 | let key1 = imp::create(self.dtor); | |
181 | let key = if key1 != 0 { | |
182 | key1 | |
183 | } else { | |
184 | let key2 = imp::create(self.dtor); | |
185 | imp::destroy(key1); | |
186 | key2 | |
187 | }; | |
83c7162d | 188 | rtassert!(key != 0); |
62682a34 | 189 | match self.key.compare_and_swap(0, key as usize, Ordering::SeqCst) { |
1a4d82fc | 190 | // The CAS succeeded, so we've created the actual key |
c34b1796 | 191 | 0 => key as usize, |
1a4d82fc | 192 | // If someone beat us to the punch, use their key instead |
dfeec247 XL |
193 | n => { |
194 | imp::destroy(key); | |
195 | n | |
196 | } | |
1a4d82fc JJ |
197 | } |
198 | } | |
199 | } | |
200 | ||
201 | impl Key { | |
9346a6ac | 202 | /// Creates a new managed OS TLS key. |
1a4d82fc JJ |
203 | /// |
204 | /// This key will be deallocated when the key falls out of scope. | |
205 | /// | |
206 | /// The argument provided is an optionally-specified destructor for the | |
207 | /// value of this TLS key. When a thread exits and the value for this key | |
208 | /// is non-null the destructor will be invoked. The TLS value will be reset | |
209 | /// to null before the destructor is invoked. | |
210 | /// | |
211 | /// Note that the destructor will not be run when the `Key` goes out of | |
212 | /// scope. | |
213 | #[inline] | |
dfeec247 | 214 | pub fn new(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key { |
1a4d82fc JJ |
215 | Key { key: unsafe { imp::create(dtor) } } |
216 | } | |
217 | ||
218 | /// See StaticKey::get | |
219 | #[inline] | |
220 | pub fn get(&self) -> *mut u8 { | |
221 | unsafe { imp::get(self.key) } | |
222 | } | |
223 | ||
224 | /// See StaticKey::set | |
225 | #[inline] | |
226 | pub fn set(&self, val: *mut u8) { | |
227 | unsafe { imp::set(self.key, val) } | |
228 | } | |
229 | } | |
230 | ||
231 | impl Drop for Key { | |
232 | fn drop(&mut self) { | |
7cac9316 XL |
233 | // Right now Windows doesn't support TLS key destruction, but this also |
234 | // isn't used anywhere other than tests, so just leak the TLS key. | |
235 | // unsafe { imp::destroy(self.key) } | |
1a4d82fc JJ |
236 | } |
237 | } |