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
7//! get/set pointer-sized data, possibly with an associated destructor.
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
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.
20//!
21//! # Examples
22//!
23//! Using a dynamically allocated TLS key. Note that this key can be shared
24//! among many threads via an `Arc`.
25//!
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());
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//!
38//! ```ignore (cannot-doctest-private-modules)
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)]
48#![unstable(feature = "thread_local_internals", issue = "none")]
49#![allow(dead_code)]
50
51#[cfg(test)]
52mod tests;
53
54use crate::sync::atomic::{self, AtomicUsize, Ordering};
55use crate::sys::thread_local_key as imp;
56
57/// A type for TLS keys that are statically allocated.
58///
59/// This type is entirely `unsafe` to use as it does not protect against
60/// use-after-deallocation or use-during-deallocation.
61///
62/// The actual OS-TLS key is lazily allocated when this is used for the first
63/// time. The key is also deallocated when the Rust runtime exits or `destroy`
64/// is called, whichever comes first.
65///
66/// # Examples
67///
68/// ```ignore (cannot-doctest-private-modules)
69/// use tls::os::{StaticKey, INIT};
70///
71/// // Use a regular global static to store the key.
72/// static KEY: StaticKey = INIT;
73///
74/// // The state provided via `get` and `set` is thread-local.
75/// unsafe {
76/// assert!(KEY.get().is_null());
77/// KEY.set(1 as *mut u8);
78/// }
79/// ```
80pub struct StaticKey {
81 /// Inner static TLS key (internals).
82 key: AtomicUsize,
83 /// Destructor for the TLS value.
84 ///
85 /// See `Key::new` for information about when the destructor runs and how
86 /// it runs.
87 dtor: Option<unsafe extern "C" fn(*mut u8)>,
88}
89
90/// Constant initialization value for static TLS keys.
91///
92/// This value specifies no destructor by default.
93pub const INIT: StaticKey = StaticKey::new(dtor:None);
94
95// Define a sentinel value that is likely not to be returned
96// as a TLS key.
97#[cfg(not(target_os = "nto"))]
98const KEY_SENTVAL: usize = 0;
99// On QNX Neutrino, 0 is always returned when currently not in use.
100// Using 0 would mean to always create two keys and remote the first
101// one (with value of 0) immediately afterwards.
102#[cfg(target_os = "nto")]
103const KEY_SENTVAL: usize = libc::PTHREAD_KEYS_MAX + 1;
104
105impl StaticKey {
106 #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")]
107 pub const fn new(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> StaticKey {
108 StaticKey { key: atomic::AtomicUsize::new(KEY_SENTVAL), dtor }
109 }
110
111 /// Gets the value associated with this TLS key
112 ///
113 /// This will lazily allocate a TLS key from the OS if one has not already
114 /// been allocated.
115 #[inline]
116 pub unsafe fn get(&self) -> *mut u8 {
117 imp::get(self.key())
118 }
119
120 /// Sets this TLS key to a new value.
121 ///
122 /// This will lazily allocate a TLS key from the OS if one has not already
123 /// been allocated.
124 #[inline]
125 pub unsafe fn set(&self, val: *mut u8) {
126 imp::set(self.key(), val)
127 }
128
129 #[inline]
130 unsafe fn key(&self) -> imp::Key {
131 match self.key.load(Ordering::Relaxed) {
132 KEY_SENTVAL => self.lazy_init() as imp::Key,
133 n => n as imp::Key,
134 }
135 }
136
137 unsafe fn lazy_init(&self) -> usize {
138 // POSIX allows the key created here to be KEY_SENTVAL, but the compare_exchange
139 // below relies on using KEY_SENTVAL as a sentinel value to check who won the
140 // race to set the shared TLS key. As far as I know, there is no
141 // guaranteed value that cannot be returned as a posix_key_create key,
142 // so there is no value we can initialize the inner key with to
143 // prove that it has not yet been set. As such, we'll continue using a
144 // value of KEY_SENTVAL, but with some gyrations to make sure we have a non-KEY_SENTVAL
145 // value returned from the creation routine.
146 // FIXME: this is clearly a hack, and should be cleaned up.
147 let key1 = imp::create(self.dtor);
148 let key = if key1 as usize != KEY_SENTVAL {
149 key1
150 } else {
151 let key2 = imp::create(self.dtor);
152 imp::destroy(key1);
153 key2
154 };
155 rtassert!(key as usize != KEY_SENTVAL);
156 match self.key.compare_exchange(
157 KEY_SENTVAL,
158 key as usize,
159 Ordering::SeqCst,
160 Ordering::SeqCst,
161 ) {
162 // The CAS succeeded, so we've created the actual key
163 Ok(_) => key as usize,
164 // If someone beat us to the punch, use their key instead
165 Err(n) => {
166 imp::destroy(key);
167 n
168 }
169 }
170 }
171}
172