1 //! OS-based thread local storage
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
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.
16 //! This module should likely not be used directly unless other primitives are
17 //! being built on. types such as `thread_local::spawn::Key` are likely much
18 //! more useful in practice than this OS-based version which likely requires
19 //! unsafe code to interoperate with.
23 //! Using a dynamically allocated TLS key. Note that this key can be shared
24 //! among many threads via an `Arc`.
26 //! ```ignore (cannot-doctest-private-modules)
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());
32 //! drop(key); // deallocate this TLS slot.
35 //! Sometimes a statically allocated key is either required or easier to work
38 //! ```ignore (cannot-doctest-private-modules)
39 //! static KEY: StaticKey = INIT;
42 //! assert!(KEY.get().is_null());
43 //! KEY.set(1 as *mut u8);
47 #![allow(non_camel_case_types)]
48 #![unstable(feature = "thread_local_internals", issue = "none")]
49 #![allow(dead_code)] // sys isn't exported yet
52 use crate::sync::atomic::{self, AtomicUsize, Ordering};
53 use crate::sys::thread_local as imp;
54 use crate::sys_common::mutex::Mutex;
56 /// A type for TLS keys that are statically allocated.
58 /// This type is entirely `unsafe` to use as it does not protect against
59 /// use-after-deallocation or use-during-deallocation.
61 /// The actual OS-TLS key is lazily allocated when this is used for the first
62 /// time. The key is also deallocated when the Rust runtime exits or `destroy`
63 /// is called, whichever comes first.
67 /// ```ignore (cannot-doctest-private-modules)
68 /// use tls::os::{StaticKey, INIT};
70 /// static KEY: StaticKey = INIT;
73 /// assert!(KEY.get().is_null());
74 /// KEY.set(1 as *mut u8);
77 pub struct StaticKey {
78 /// Inner static TLS key (internals).
80 /// Destructor for the TLS value.
82 /// See `Key::new` for information about when the destructor runs and how
84 dtor: Option<unsafe extern "C" fn(*mut u8)>,
87 /// A type for a safely managed OS-based TLS slot.
89 /// This type allocates an OS TLS key when it is initialized and will deallocate
90 /// the key when it falls out of scope. When compared with `StaticKey`, this
91 /// type is entirely safe to use.
93 /// Implementations will likely, however, contain unsafe code as this type only
94 /// operates on `*mut u8`, a raw pointer.
98 /// ```ignore (cannot-doctest-private-modules)
101 /// let key = Key::new(None);
102 /// assert!(key.get().is_null());
103 /// key.set(1 as *mut u8);
104 /// assert!(!key.get().is_null());
106 /// drop(key); // deallocate this TLS slot.
112 /// Constant initialization value for static TLS keys.
114 /// This value specifies no destructor by default.
115 pub const INIT: StaticKey = StaticKey::new(None);
118 pub const fn new(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> StaticKey {
119 StaticKey { key: atomic::AtomicUsize::new(0), dtor }
122 /// Gets the value associated with this TLS key
124 /// This will lazily allocate a TLS key from the OS if one has not already
127 pub unsafe fn get(&self) -> *mut u8 {
131 /// Sets this TLS key to a new value.
133 /// This will lazily allocate a TLS key from the OS if one has not already
136 pub unsafe fn set(&self, val: *mut u8) {
137 imp::set(self.key(), val)
141 unsafe fn key(&self) -> imp::Key {
142 match self.key.load(Ordering::Relaxed) {
143 0 => self.lazy_init() as imp::Key,
148 unsafe fn lazy_init(&self) -> usize {
149 // Currently the Windows implementation of TLS is pretty hairy, and
150 // it greatly simplifies creation if we just synchronize everything.
152 // Additionally a 0-index of a tls key hasn't been seen on windows, so
153 // we just simplify the whole branch.
154 if imp::requires_synchronized_create() {
155 // We never call `INIT_LOCK.init()`, so it is UB to attempt to
156 // acquire this mutex reentrantly!
157 static INIT_LOCK: Mutex = Mutex::new();
158 let _guard = INIT_LOCK.lock();
159 let mut key = self.key.load(Ordering::SeqCst);
161 key = imp::create(self.dtor) as usize;
162 self.key.store(key, Ordering::SeqCst);
168 // POSIX allows the key created here to be 0, but the compare_and_swap
169 // below relies on using 0 as a sentinel value to check who won the
170 // race to set the shared TLS key. As far as I know, there is no
171 // guaranteed value that cannot be returned as a posix_key_create key,
172 // so there is no value we can initialize the inner key with to
173 // prove that it has not yet been set. As such, we'll continue using a
174 // value of 0, but with some gyrations to make sure we have a non-0
175 // value returned from the creation routine.
176 // FIXME: this is clearly a hack, and should be cleaned up.
177 let key1 = imp::create(self.dtor);
178 let key = if key1 != 0 {
181 let key2 = imp::create(self.dtor);
186 match self.key.compare_and_swap(0, key as usize, Ordering::SeqCst) {
187 // The CAS succeeded, so we've created the actual key
189 // If someone beat us to the punch, use their key instead
199 /// Creates a new managed OS TLS key.
201 /// This key will be deallocated when the key falls out of scope.
203 /// The argument provided is an optionally-specified destructor for the
204 /// value of this TLS key. When a thread exits and the value for this key
205 /// is non-null the destructor will be invoked. The TLS value will be reset
206 /// to null before the destructor is invoked.
208 /// Note that the destructor will not be run when the `Key` goes out of
211 pub fn new(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key {
212 Key { key: unsafe { imp::create(dtor) } }
215 /// See StaticKey::get
217 pub fn get(&self) -> *mut u8 {
218 unsafe { imp::get(self.key) }
221 /// See StaticKey::set
223 pub fn set(&self, val: *mut u8) {
224 unsafe { imp::set(self.key, val) }
230 // Right now Windows doesn't support TLS key destruction, but this also
231 // isn't used anywhere other than tests, so just leak the TLS key.
232 // unsafe { imp::destroy(self.key) }
236 pub unsafe fn register_dtor_fallback(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
237 // The fallback implementation uses a vanilla OS-based TLS key to track
238 // the list of destructors that need to be run for this thread. The key
239 // then has its own destructor which runs all the other destructors.
241 // The destructor for DTORS is a little special in that it has a `while`
242 // loop to continuously drain the list of registered destructors. It
243 // *should* be the case that this loop always terminates because we
244 // provide the guarantee that a TLS key cannot be set after it is
245 // flagged for destruction.
247 static DTORS: StaticKey = StaticKey::new(Some(run_dtors));
248 type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
249 if DTORS.get().is_null() {
250 let v: Box<List> = box Vec::new();
251 DTORS.set(Box::into_raw(v) as *mut u8);
253 let list: &mut List = &mut *(DTORS.get() as *mut List);
254 list.push((t, dtor));
256 unsafe extern "C" fn run_dtors(mut ptr: *mut u8) {
257 while !ptr.is_null() {
258 let list: Box<List> = Box::from_raw(ptr as *mut List);
259 for (ptr, dtor) in list.into_iter() {
263 DTORS.set(ptr::null_mut());
270 use super::{Key, StaticKey};
272 fn assert_sync<T: Sync>() {}
273 fn assert_send<T: Send>() {}
277 assert_sync::<Key>();
278 assert_send::<Key>();
280 let k1 = Key::new(None);
281 let k2 = Key::new(None);
282 assert!(k1.get().is_null());
283 assert!(k2.get().is_null());
286 assert_eq!(k1.get() as usize, 1);
287 assert_eq!(k2.get() as usize, 2);
292 static K1: StaticKey = StaticKey::new(None);
293 static K2: StaticKey = StaticKey::new(None);
296 assert!(K1.get().is_null());
297 assert!(K2.get().is_null());
300 assert_eq!(K1.get() as usize, 1);
301 assert_eq!(K2.get() as usize, 2);