| 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::{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`. |
| 15 | pub 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" ))] |
| 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: 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 | |