Skip to main content

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}