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