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 | |
9 | use 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`. |
15 | pub 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" ))] |
25 | const 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" )] |
30 | const KEY_SENTVAL: usize = libc::PTHREAD_KEYS_MAX + 1; |
31 | |
32 | impl 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 | } |
82 | |