     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77

    use crate::loom::thread::LocalKey;

    use std::cell::Cell;
    use std::marker;

    /// Set a reference as a thread-local
    macro_rules! scoped_thread_local {
        ($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty) => (
            $(#[$attrs])*
            $vis static $name: $crate::macros::scoped_tls::ScopedKey<$ty>
                = $crate::macros::scoped_tls::ScopedKey {
                    inner: {
                        thread_local!(static FOO: ::std::cell::Cell<*const ()> = {
                            std::cell::Cell::new(::std::ptr::null())
                        });
                        &FOO
                    },
                    _marker: ::std::marker::PhantomData,
                };
        )
    }

    /// Type representing a thread local storage key corresponding to a reference
    /// to the type parameter `T`.
    pub(crate) struct ScopedKey<T> {
        pub(crate) inner: &'static LocalKey<Cell<*const ()>>,
        pub(crate) _marker: marker::PhantomData<T>,
    }

    unsafe impl<T> Sync for ScopedKey<T> {}

    impl<T> ScopedKey<T> {
        /// Inserts a value into this scoped thread local storage slot for a
        /// duration of a closure.
        pub(crate) fn set<F, R>(&'static self, t: &T, f: F) -> R
        where
            F: FnOnce() -> R,
        {
            struct Reset {
                key: &'static LocalKey<Cell<*const ()>>,
                val: *const (),
            }

            impl Drop for Reset {
                fn drop(&mut self) {
                    self.key.with(|c| c.set(self.val));
                }
            }

            let prev = self.inner.with(|c| {
                let prev = c.get();
                c.set(t as *const _ as *const ());
                prev
            });

            let _reset = Reset {
                key: self.inner,
                val: prev,
            };

            f()
        }

        /// Gets a value out of this scoped variable.
        pub(crate) fn with<F, R>(&'static self, f: F) -> R
        where
            F: FnOnce(Option<&T>) -> R,
        {
            let val = self.inner.with(|c| c.get());

            if val.is_null() {
                f(None)
            } else {
                unsafe { f(Some(&*(val as *const T))) }
            }
        }
    }