//! Thread local support for platforms with native TLS.
//!
//! To achieve the best performance, we choose from four different types for
//! the TLS variable, depending on the method of initialization used (`const`
//! or lazy) and the drop requirements of the stored type:
//!
//! |         | `Drop`               | `!Drop`             |
//! |--------:|:--------------------:|:-------------------:|
//! | `const` | `EagerStorage<T>`    | `T`                 |
//! | lazy    | `LazyStorage<T, ()>` | `LazyStorage<T, !>` |
//!
//! For `const` initialization and `!Drop` types, we simply use `T` directly,
//! but for other situations, we implement a state machine to handle
//! initialization of the variable and its destructor and destruction.
//! Upon accessing the TLS variable, the current state is compared:
//!
//! 1. If the state is `Initial`, initialize the storage, transition the state
//!    to `Alive` and (if applicable) register the destructor, and return a
//!    reference to the value.
//! 2. If the state is `Alive`, initialization was previously completed, so
//!    return a reference to the value.
//! 3. If the state is `Destroyed`, the destructor has been run already, so
//!    return [`None`].
//!
//! The TLS destructor sets the state to `Destroyed` and drops the current value.
//!
//! To simplify the code, we make `LazyStorage` generic over the destroyed state
//! and use the `!` type (never type) as type parameter for `!Drop` types. This
//! eliminates the `Destroyed` state for these values, which can allow more niche
//! optimizations to occur for the `State` enum. For `Drop` types, `()` is used.

use crate::cell::Cell;
use crate::ptr;

mod eager;
mod lazy;

pub use eager::Storage as EagerStorage;
pub use lazy::Storage as LazyStorage;

#[doc(hidden)]
#[allow_internal_unstable(
    thread_local_internals,
    cfg_target_thread_local,
    thread_local,
    never_type
)]
#[allow_internal_unsafe]
#[unstable(feature = "thread_local_internals", issue = "none")]
#[rustc_macro_transparency = "semiopaque"]
pub macro thread_local_inner {
    // NOTE: we cannot import `LocalKey`, `LazyStorage` or `EagerStorage` with a `use` because that
    // can shadow user provided type or type alias with a matching name. Please update the shadowing
    // test in `tests/thread.rs` if these types are renamed.

    // Used to generate the `LocalKey` value for const-initialized thread locals.
    (@key $t:ty, $(#[$align_attr:meta])*, const $init:expr) => {{
        const __RUST_STD_INTERNAL_INIT: $t = $init;

        unsafe {
            $crate::thread::LocalKey::new(const {
                if $crate::mem::needs_drop::<$t>() {
                    |_| {
                        #[thread_local]
                        $(#[$align_attr])*
                        static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::EagerStorage<$t>
                            = $crate::thread::local_impl::EagerStorage::new(__RUST_STD_INTERNAL_INIT);
                        __RUST_STD_INTERNAL_VAL.get()
                    }
                } else {
                    |_| {
                        #[thread_local]
                        $(#[$align_attr])*
                        static __RUST_STD_INTERNAL_VAL: $t = __RUST_STD_INTERNAL_INIT;
                        &__RUST_STD_INTERNAL_VAL
                    }
                }
            })
        }
    }},

    // used to generate the `LocalKey` value for `thread_local!`
    (@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{
        #[inline]
        fn __rust_std_internal_init_fn() -> $t {
            $init
        }

        unsafe {
            $crate::thread::LocalKey::new(const {
                if $crate::mem::needs_drop::<$t>() {
                    |__rust_std_internal_init| {
                        #[thread_local]
                        $(#[$align_attr])*
                        static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::LazyStorage<$t, ()>
                            = $crate::thread::local_impl::LazyStorage::new();
                        __RUST_STD_INTERNAL_VAL.get_or_init(__rust_std_internal_init, __rust_std_internal_init_fn)
                    }
                } else {
                    |__rust_std_internal_init| {
                        #[thread_local]
                        $(#[$align_attr])*
                        static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::LazyStorage<$t, !>
                            = $crate::thread::local_impl::LazyStorage::new();
                        __RUST_STD_INTERNAL_VAL.get_or_init(__rust_std_internal_init, __rust_std_internal_init_fn)
                    }
                }
            })
        }
    }},
}

#[rustc_macro_transparency = "semiopaque"]
pub(crate) macro local_pointer {
    () => {},
    ($vis:vis static $name:ident; $($rest:tt)*) => {
        #[thread_local]
        $vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new();
        $crate::sys::thread_local::local_pointer! { $($rest)* }
    },
}

pub(crate) struct LocalPointer {
    p: Cell<*mut ()>,
}

impl LocalPointer {
    pub const fn __new() -> LocalPointer {
        LocalPointer { p: Cell::new(ptr::null_mut()) }
    }

    pub fn get(&self) -> *mut () {
        self.p.get()
    }

    pub fn set(&self, p: *mut ()) {
        self.p.set(p)
    }
}
