std/sys/thread_local/key/racy.rs
1//! A `LazyKey` implementation using racy initialization.
2//!
3//! Unfortunately, none of the platforms currently supported by `std` allows
4//! creating TLS keys at compile-time. Thus we need a way to lazily create keys.
5//! Instead of blocking API like `OnceLock`, we use racy initialization, which
6//! should be more lightweight and avoids circular dependencies with the rest of
7//! `std`.
8
9use crate::sync::atomic::{Atomic, AtomicUsize, Ordering};
10
11/// A type for TLS keys that are statically allocated.
12///
13/// This is basically a `LazyLock<Key>`, but avoids blocking and circular
14/// dependencies with the rest of `std`.
15pub struct LazyKey {
16 /// Inner static TLS key (internals).
17 key: Atomic<usize>,
18 /// Destructor for the TLS value.
19 dtor: Option<unsafe extern "C" fn(*mut u8)>,
20}
21
22// Define a sentinel value that is likely not to be returned
23// as a TLS key.
24#[cfg(not(target_os = "nto"))]
25const KEY_SENTVAL: usize = 0;
26// On QNX Neutrino, 0 is always returned when currently not in use.
27// Using 0 would mean to always create two keys and remote the first
28// one (with value of 0) immediately afterwards.
29#[cfg(target_os = "nto")]
30const KEY_SENTVAL: usize = libc::PTHREAD_KEYS_MAX + 1;
31
32impl LazyKey {
33 pub const fn new(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> LazyKey {
34 LazyKey { key: AtomicUsize::new(KEY_SENTVAL), dtor }
35 }
36
37 #[inline]
38 pub fn force(&self) -> super::Key {
39 match self.key.load(Ordering::Acquire) {
40 KEY_SENTVAL => self.lazy_init() as super::Key,
41 n => n as super::Key,
42 }
43 }
44
45 #[cold]
46 fn lazy_init(&self) -> usize {
47 // POSIX allows the key created here to be KEY_SENTVAL, but the compare_exchange
48 // below relies on using KEY_SENTVAL as a sentinel value to check who won the
49 // race to set the shared TLS key. As far as I know, there is no
50 // guaranteed value that cannot be returned as a posix_key_create key,
51 // so there is no value we can initialize the inner key with to
52 // prove that it has not yet been set. As such, we'll continue using a
53 // value of KEY_SENTVAL, but with some gyrations to make sure we have a non-KEY_SENTVAL
54 // value returned from the creation routine.
55 // FIXME: this is clearly a hack, and should be cleaned up.
56 let key1 = super::create(self.dtor);
57 let key = if key1 as usize != KEY_SENTVAL {
58 key1
59 } else {
60 let key2 = super::create(self.dtor);
61 unsafe {
62 super::destroy(key1);
63 }
64 key2
65 };
66 rtassert!(key as usize != KEY_SENTVAL);
67 match self.key.compare_exchange(
68 KEY_SENTVAL,
69 key as usize,
70 Ordering::Release,
71 Ordering::Acquire,
72 ) {
73 // The CAS succeeded, so we've created the actual key
74 Ok(_) => key as usize,
75 // If someone beat us to the punch, use their key instead
76 Err(n) => unsafe {
77 super::destroy(key);
78 n
79 },
80 }
81 }
82}