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::{self, 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: AtomicUsize,
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: atomic::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 fn lazy_init(&self) -> usize {
46 // POSIX allows the key created here to be KEY_SENTVAL, but the compare_exchange
47 // below relies on using KEY_SENTVAL as a sentinel value to check who won the
48 // race to set the shared TLS key. As far as I know, there is no
49 // guaranteed value that cannot be returned as a posix_key_create key,
50 // so there is no value we can initialize the inner key with to
51 // prove that it has not yet been set. As such, we'll continue using a
52 // value of KEY_SENTVAL, but with some gyrations to make sure we have a non-KEY_SENTVAL
53 // value returned from the creation routine.
54 // FIXME: this is clearly a hack, and should be cleaned up.
55 let key1 = super::create(self.dtor);
56 let key = if key1 as usize != KEY_SENTVAL {
57 key1
58 } else {
59 let key2 = super::create(self.dtor);
60 unsafe {
61 super::destroy(key1);
62 }
63 key2
64 };
65 rtassert!(key as usize != KEY_SENTVAL);
66 match self.key.compare_exchange(
67 KEY_SENTVAL,
68 key as usize,
69 Ordering::Release,
70 Ordering::Acquire,
71 ) {
72 // The CAS succeeded, so we've created the actual key
73 Ok(_) => key as usize,
74 // If someone beat us to the punch, use their key instead
75 Err(n) => unsafe {
76 super::destroy(key);
77 n
78 },
79 }
80 }
81}