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}