tracing_core/spin/
once.rs1use core::cell::UnsafeCell;
2use core::fmt;
3use core::hint::spin_loop;
4use core::sync::atomic::Ordering;
5
6#[cfg(feature = "portable-atomic")]
7use portable_atomic::AtomicUsize;
8
9#[cfg(not(feature = "portable-atomic"))]
10use core::sync::atomic::AtomicUsize;
11
12pub struct Once<T> {
17 state: AtomicUsize,
18 data: UnsafeCell<Option<T>>, }
20
21impl<T: fmt::Debug> fmt::Debug for Once<T> {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 match self.r#try() {
24 Some(s) => write!(f, "Once {{ data: ")
25 .and_then(|()| s.fmt(f))
26 .and_then(|()| write!(f, "}}")),
27 None => write!(f, "Once {{ <uninitialized> }}"),
28 }
29 }
30}
31
32unsafe impl<T: Send + Sync> Sync for Once<T> {}
35unsafe impl<T: Send> Send for Once<T> {}
36
37const INCOMPLETE: usize = 0x0;
40const RUNNING: usize = 0x1;
41const COMPLETE: usize = 0x2;
42const PANICKED: usize = 0x3;
43
44use core::hint::unreachable_unchecked as unreachable;
45
46impl<T> Once<T> {
47 pub const INIT: Self = Once {
49 state: AtomicUsize::new(INCOMPLETE),
50 data: UnsafeCell::new(None),
51 };
52
53 pub const fn new() -> Once<T> {
55 Self::INIT
56 }
57
58 fn force_get<'a>(&'a self) -> &'a T {
59 match unsafe { &*self.data.get() }.as_ref() {
60 None => unsafe { unreachable() },
61 Some(p) => p,
62 }
63 }
64
65 pub fn call_once<'a, F>(&'a self, builder: F) -> &'a T
77 where
78 F: FnOnce() -> T,
79 {
80 let mut status = self.state.load(Ordering::SeqCst);
81
82 if status == INCOMPLETE {
83 status = match self.state.compare_exchange(
84 INCOMPLETE,
85 RUNNING,
86 Ordering::SeqCst,
87 Ordering::SeqCst,
88 ) {
89 Ok(status) => {
90 debug_assert_eq!(
91 status, INCOMPLETE,
92 "if compare_exchange succeeded, previous status must be incomplete",
93 );
94 let mut finish = Finish {
97 state: &self.state,
98 panicked: true,
99 };
100 unsafe { *self.data.get() = Some(builder()) };
101 finish.panicked = false;
102
103 self.state.store(COMPLETE, Ordering::SeqCst);
104
105 return self.force_get();
107 }
108 Err(status) => status,
109 }
110 }
111
112 loop {
113 match status {
114 INCOMPLETE => unreachable!(),
115 RUNNING => {
116 spin_loop();
118 status = self.state.load(Ordering::SeqCst)
119 }
120 PANICKED => panic!("Once has panicked"),
121 COMPLETE => return self.force_get(),
122 _ => unsafe { unreachable() },
123 }
124 }
125 }
126
127 pub fn r#try<'a>(&'a self) -> Option<&'a T> {
129 match self.state.load(Ordering::SeqCst) {
130 COMPLETE => Some(self.force_get()),
131 _ => None,
132 }
133 }
134
135 pub fn wait<'a>(&'a self) -> Option<&'a T> {
138 loop {
139 match self.state.load(Ordering::SeqCst) {
140 INCOMPLETE => return None,
141
142 RUNNING => {
143 spin_loop() }
145 COMPLETE => return Some(self.force_get()),
146 PANICKED => panic!("Once has panicked"),
147 _ => unsafe { unreachable() },
148 }
149 }
150 }
151}
152
153struct Finish<'a> {
154 state: &'a AtomicUsize,
155 panicked: bool,
156}
157
158impl<'a> Drop for Finish<'a> {
159 fn drop(&mut self) {
160 if self.panicked {
161 self.state.store(PANICKED, Ordering::SeqCst);
162 }
163 }
164}