rustpython_common/
static_cell.rs

1#[cfg(not(feature = "threading"))]
2mod non_threading {
3    use crate::lock::OnceCell;
4    use std::thread::LocalKey;
5
6    pub struct StaticCell<T: 'static> {
7        inner: &'static LocalKey<OnceCell<&'static T>>,
8    }
9
10    fn leak<T>(x: T) -> &'static T {
11        Box::leak(Box::new(x))
12    }
13
14    impl<T> StaticCell<T> {
15        #[doc(hidden)]
16        pub const fn _from_localkey(inner: &'static LocalKey<OnceCell<&'static T>>) -> Self {
17            Self { inner }
18        }
19
20        pub fn get(&'static self) -> Option<&'static T> {
21            self.inner.with(|x| x.get().copied())
22        }
23
24        pub fn set(&'static self, value: T) -> Result<(), T> {
25            // thread-safe because it's a unsync::OnceCell
26            self.inner.with(|x| {
27                if x.get().is_some() {
28                    Err(value)
29                } else {
30                    // will never fail
31                    let _ = x.set(leak(value));
32                    Ok(())
33                }
34            })
35        }
36
37        pub fn get_or_init<F>(&'static self, f: F) -> &'static T
38        where
39            F: FnOnce() -> T,
40        {
41            self.inner.with(|x| *x.get_or_init(|| leak(f())))
42        }
43
44        pub fn get_or_try_init<F, E>(&'static self, f: F) -> Result<&'static T, E>
45        where
46            F: FnOnce() -> Result<T, E>,
47        {
48            self.inner
49                .with(|x| x.get_or_try_init(|| f().map(leak)).copied())
50        }
51    }
52
53    #[macro_export]
54    macro_rules! static_cell {
55        ($($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty;)+) => {
56            $($(#[$attr])*
57            $vis static $name: $crate::static_cell::StaticCell<$t> = {
58                ::std::thread_local! {
59                     $vis static $name: $crate::lock::OnceCell<&'static $t> = $crate::lock::OnceCell::new();
60                }
61                $crate::static_cell::StaticCell::_from_localkey(&$name)
62            };)+
63        };
64    }
65}
66#[cfg(not(feature = "threading"))]
67pub use non_threading::*;
68
69#[cfg(feature = "threading")]
70mod threading {
71    use crate::lock::OnceCell;
72
73    pub struct StaticCell<T: 'static> {
74        inner: OnceCell<T>,
75    }
76
77    impl<T> StaticCell<T> {
78        #[doc(hidden)]
79        pub const fn _from_oncecell(inner: OnceCell<T>) -> Self {
80            Self { inner }
81        }
82
83        pub fn get(&'static self) -> Option<&'static T> {
84            self.inner.get()
85        }
86
87        pub fn set(&'static self, value: T) -> Result<(), T> {
88            self.inner.set(value)
89        }
90
91        pub fn get_or_init<F>(&'static self, f: F) -> &'static T
92        where
93            F: FnOnce() -> T,
94        {
95            self.inner.get_or_init(f)
96        }
97
98        pub fn get_or_try_init<F, E>(&'static self, f: F) -> Result<&'static T, E>
99        where
100            F: FnOnce() -> Result<T, E>,
101        {
102            self.inner.get_or_try_init(f)
103        }
104    }
105
106    #[macro_export]
107    macro_rules! static_cell {
108        ($($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty;)+) => {
109            $($(#[$attr])*
110            $vis static $name: $crate::static_cell::StaticCell<$t> =
111                $crate::static_cell::StaticCell::_from_oncecell($crate::lock::OnceCell::new());)+
112        };
113    }
114}
115#[cfg(feature = "threading")]
116pub use threading::*;