Skip to main content

rustpython_common/
static_cell.rs

1#[cfg(feature = "threading")]
2mod threading {
3    use crate::lock::OnceCell;
4
5    pub struct StaticCell<T: 'static> {
6        inner: OnceCell<T>,
7    }
8
9    impl<T> StaticCell<T> {
10        #[doc(hidden)]
11        pub const fn _from_once_cell(inner: OnceCell<T>) -> Self {
12            Self { inner }
13        }
14
15        pub fn get(&'static self) -> Option<&'static T> {
16            self.inner.get()
17        }
18
19        pub fn set(&'static self, value: T) -> Result<(), T> {
20            self.inner.set(value)
21        }
22
23        pub fn get_or_init<F>(&'static self, f: F) -> &'static T
24        where
25            F: FnOnce() -> T,
26        {
27            self.inner.get_or_init(f)
28        }
29
30        pub fn get_or_try_init<F, E>(&'static self, f: F) -> Result<&'static T, E>
31        where
32            F: FnOnce() -> Result<T, E>,
33        {
34            if let Some(val) = self.inner.get() {
35                return Ok(val);
36            }
37            let val = f()?;
38            let _ = self.inner.set(val);
39            Ok(self.inner.get().unwrap())
40        }
41    }
42
43    #[macro_export]
44    macro_rules! static_cell {
45        ($($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty;)+) => {
46            $($(#[$attr])*
47            $vis static $name: $crate::static_cell::StaticCell<$t> =
48                $crate::static_cell::StaticCell::_from_once_cell($crate::lock::OnceCell::new());)+
49        };
50    }
51}
52#[cfg(feature = "threading")]
53pub use threading::*;
54
55#[cfg(all(not(feature = "threading"), feature = "std"))]
56mod non_threading {
57    use crate::lock::OnceCell;
58    use std::thread::LocalKey;
59
60    pub struct StaticCell<T: 'static> {
61        inner: &'static LocalKey<OnceCell<&'static T>>,
62    }
63
64    fn leak<T>(x: T) -> &'static T {
65        Box::leak(Box::new(x))
66    }
67
68    impl<T> StaticCell<T> {
69        #[doc(hidden)]
70        #[must_use]
71        pub const fn _from_local_key(inner: &'static LocalKey<OnceCell<&'static T>>) -> Self {
72            Self { inner }
73        }
74
75        #[must_use]
76        pub fn get(&'static self) -> Option<&'static T> {
77            self.inner.with(|x| x.get().copied())
78        }
79
80        pub fn set(&'static self, value: T) -> Result<(), T> {
81            self.inner.with(|x| {
82                if x.get().is_some() {
83                    Err(value)
84                } else {
85                    let _ = x.set(leak(value));
86                    Ok(())
87                }
88            })
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.with(|x| *x.get_or_init(|| leak(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.with(|x| {
103                if let Some(val) = x.get() {
104                    Ok(*val)
105                } else {
106                    let val = leak(f()?);
107                    let _ = x.set(val);
108                    Ok(val)
109                }
110            })
111        }
112    }
113
114    #[macro_export]
115    macro_rules! static_cell {
116        ($($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty;)+) => {
117            $($(#[$attr])*
118            $vis static $name: $crate::static_cell::StaticCell<$t> = {
119                ::std::thread_local! {
120                     $vis static $name: $crate::lock::OnceCell<&'static $t> = const {
121                         $crate::lock::OnceCell::new()
122                     };
123                }
124                $crate::static_cell::StaticCell::_from_local_key(&$name)
125            };)+
126        };
127    }
128}
129#[cfg(all(not(feature = "threading"), feature = "std"))]
130pub use non_threading::*;
131
132// Same as `threading` variant, but wraps unsync::OnceCell with Sync.
133#[cfg(all(not(feature = "threading"), not(feature = "std")))]
134mod no_std {
135    use crate::lock::OnceCell;
136
137    // unsync::OnceCell is !Sync, but without std there can be no threads.
138    struct SyncOnceCell<T>(OnceCell<T>);
139    // SAFETY: Without std, threading is impossible.
140    unsafe impl<T> Sync for SyncOnceCell<T> {}
141
142    pub struct StaticCell<T: 'static> {
143        inner: SyncOnceCell<T>,
144    }
145
146    impl<T> StaticCell<T> {
147        #[doc(hidden)]
148        pub const fn _from_once_cell(inner: OnceCell<T>) -> Self {
149            Self {
150                inner: SyncOnceCell(inner),
151            }
152        }
153
154        pub fn get(&'static self) -> Option<&'static T> {
155            self.inner.0.get()
156        }
157
158        pub fn set(&'static self, value: T) -> Result<(), T> {
159            self.inner.0.set(value)
160        }
161
162        pub fn get_or_init<F>(&'static self, f: F) -> &'static T
163        where
164            F: FnOnce() -> T,
165        {
166            self.inner.0.get_or_init(f)
167        }
168
169        pub fn get_or_try_init<F, E>(&'static self, f: F) -> Result<&'static T, E>
170        where
171            F: FnOnce() -> Result<T, E>,
172        {
173            if let Some(val) = self.inner.0.get() {
174                return Ok(val);
175            }
176            let val = f()?;
177            let _ = self.inner.0.set(val);
178            Ok(self.inner.0.get().unwrap())
179        }
180    }
181
182    #[macro_export]
183    macro_rules! static_cell {
184        ($($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty;)+) => {
185            $($(#[$attr])*
186            $vis static $name: $crate::static_cell::StaticCell<$t> =
187                $crate::static_cell::StaticCell::_from_once_cell($crate::lock::OnceCell::new());)+
188        };
189    }
190}
191#[cfg(all(not(feature = "threading"), not(feature = "std")))]
192pub use no_std::*;