about summary refs log tree commit diff
path: root/library/std/src/sys/thread_local/static_local.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/sys/thread_local/static_local.rs')
-rw-r--r--library/std/src/sys/thread_local/static_local.rs108
1 files changed, 108 insertions, 0 deletions
diff --git a/library/std/src/sys/thread_local/static_local.rs b/library/std/src/sys/thread_local/static_local.rs
new file mode 100644
index 00000000000..4f2b6868962
--- /dev/null
+++ b/library/std/src/sys/thread_local/static_local.rs
@@ -0,0 +1,108 @@
+use super::lazy::LazyKeyInner;
+use crate::fmt;
+
+#[doc(hidden)]
+#[allow_internal_unstable(thread_local_internals)]
+#[allow_internal_unsafe]
+#[unstable(feature = "thread_local_internals", issue = "none")]
+#[rustc_macro_transparency = "semitransparent"]
+pub macro thread_local_inner {
+    // used to generate the `LocalKey` value for const-initialized thread locals
+    (@key $t:ty, const $init:expr) => {{
+        #[inline] // see comments below
+        #[deny(unsafe_op_in_unsafe_fn)]
+        // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint
+        #[cfg_attr(bootstrap, allow(static_mut_ref))]
+        #[cfg_attr(not(bootstrap), allow(static_mut_refs))]
+        unsafe fn __getit(
+            _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+        ) -> $crate::option::Option<&'static $t> {
+            const INIT_EXPR: $t = $init;
+
+            // wasm without atomics maps directly to `static mut`, and dtors
+            // aren't implemented because thread dtors aren't really a thing
+            // on wasm right now
+            //
+            // FIXME(#84224) this should come after the `target_thread_local`
+            // block.
+            static mut VAL: $t = INIT_EXPR;
+            unsafe { $crate::option::Option::Some(&VAL) }
+        }
+
+        unsafe {
+            $crate::thread::LocalKey::new(__getit)
+        }
+    }},
+
+    // used to generate the `LocalKey` value for `thread_local!`
+    (@key $t:ty, $init:expr) => {
+        {
+            #[inline]
+            fn __init() -> $t { $init }
+            #[inline]
+            unsafe fn __getit(
+                init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+            ) -> $crate::option::Option<&'static $t> {
+                static __KEY: $crate::thread::local_impl::Key<$t> =
+                    $crate::thread::local_impl::Key::new();
+
+                unsafe {
+                    __KEY.get(move || {
+                        if let $crate::option::Option::Some(init) = init {
+                            if let $crate::option::Option::Some(value) = init.take() {
+                                return value;
+                            } else if $crate::cfg!(debug_assertions) {
+                                $crate::unreachable!("missing default value");
+                            }
+                        }
+                        __init()
+                    })
+                }
+            }
+
+            unsafe {
+                $crate::thread::LocalKey::new(__getit)
+            }
+        }
+    },
+    ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
+        $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
+            $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
+    },
+}
+
+/// On some targets like wasm there's no threads, so no need to generate
+/// thread locals and we can instead just use plain statics!
+
+pub struct Key<T> {
+    inner: LazyKeyInner<T>,
+}
+
+unsafe impl<T> Sync for Key<T> {}
+
+impl<T> fmt::Debug for Key<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("Key").finish_non_exhaustive()
+    }
+}
+
+impl<T> Key<T> {
+    pub const fn new() -> Key<T> {
+        Key { inner: LazyKeyInner::new() }
+    }
+
+    pub unsafe fn get(&self, init: impl FnOnce() -> T) -> Option<&'static T> {
+        // SAFETY: The caller must ensure no reference is ever handed out to
+        // the inner cell nor mutable reference to the Option<T> inside said
+        // cell. This make it safe to hand a reference, though the lifetime
+        // of 'static is itself unsafe, making the get method unsafe.
+        let value = unsafe {
+            match self.inner.get() {
+                Some(ref value) => value,
+                None => self.inner.initialize(init),
+            }
+        };
+
+        Some(value)
+    }
+}