1use std::cell::UnsafeCell;
2#[cfg(not(feature = "no-state-check"))]
3use std::sync::atomic::{AtomicU8, Ordering::SeqCst};
4
5const UNINITIALIZED: u8 = 0;
6const INITIALIZING: u8 = 1;
7const INITIALIZED: u8 = 2;
8
9pub struct State<T> {
10 data: UnsafeCell<Option<T>>,
11 #[cfg(not(feature = "no-state-check"))]
12 status: AtomicU8,
13}
14unsafe impl<T> Sync for State<T> {}
15
16#[macro_export]
17macro_rules! set_once {
18 ($state:expr, $f:expr) => {{
19 use std::sync::Once;
20 static ONCE: Once = Once::new();
21 ONCE.call_once(|| {
22 $state.set($f());
23 });
24 }};
25}
26
27#[macro_export]
28macro_rules! local_global_state {
29 ($mod_name:ident, $type_name:ident, $test_default:expr) => {
30 mod $mod_name {
31 use super::*;
32 static GLOBAL: $crate::state::State<$type_name> = $crate::state::State::new();
33
34 #[cfg(not(feature = "no-state-check"))]
35 thread_local! {
36 static LOCAL: std::cell::Cell<Option<&'static $type_name>> = std::cell::Cell::new(None);
37 }
38
39 pub fn get() -> &'static $type_name {
40 #[cfg(test)]
41 $crate::set_once!(GLOBAL, $test_default);
42
43 #[cfg(not(feature = "no-state-check"))]
44 if let Some(local) = LOCAL.get() {
45 return local;
46 }
47
48 GLOBAL.get()
49 }
50
51 pub fn set(data: $type_name) {
52 #[cfg(test)]
53 log::info!("測試中,不設定狀態");
54 #[cfg(not(test))]
55 GLOBAL.set(data);
56 }
57
58 #[cfg(not(feature = "no-state-check"))]
59 pub fn set_local(data: &'static $type_name) {
60 LOCAL.set(Some(data));
61 }
62 }
63 };
64}
65
66impl<T: Sized> State<T> {
67 pub const fn new() -> State<T> {
68 State {
69 data: UnsafeCell::new(None),
70 #[cfg(not(feature = "no-state-check"))]
71 status: AtomicU8::new(UNINITIALIZED),
72 }
73 }
74
75 pub fn set(&self, data: T) {
76 #[cfg(not(feature = "no-state-check"))]
77 {
78 let status = self
79 .status
80 .compare_exchange(UNINITIALIZED, INITIALIZING, SeqCst, SeqCst);
81 log::debug!("設定前的狀態:{:?}", status);
82 if status.is_err() {
83 panic!("多次設定狀態");
84 }
85 }
86 let ptr = self.data.get();
87 unsafe {
88 *ptr = Some(data);
89 }
90 #[cfg(not(feature = "no-state-check"))]
91 self.status.store(INITIALIZED, SeqCst);
92 }
93 pub fn get(&self) -> &T {
94 #[cfg(not(feature = "no-state-check"))]
95 match self.status.load(SeqCst) {
96 UNINITIALIZED => {
97 panic!("還沒設定就取狀態");
98 }
99 INITIALIZING => {
100 while self.status.load(SeqCst) == INITIALIZING {
101 std::hint::spin_loop();
102 }
103 }
104 _ => (),
105 }
106 let ptr = self.data.get();
107 unsafe { (&*ptr).as_ref().unwrap() }
108 }
109}