1use std::{
2 cell::RefCell,
3 sync::{
4 Arc, Mutex, OnceLock,
5 atomic::{AtomicU8, Ordering},
6 },
7 time::Duration,
8};
9
10use super::GraphGenerationStats;
11
12thread_local! {
13 static GENERATION_PROGRESS_CONTEXT: RefCell<Vec<String>> = const { RefCell::new(Vec::new()) };
14}
15
16pub struct GenerationProgressContextGuard;
17
18impl Drop for GenerationProgressContextGuard {
19 fn drop(&mut self) {
20 GENERATION_PROGRESS_CONTEXT.with(|context| {
21 context.borrow_mut().pop();
22 });
23 }
24}
25
26pub fn enter_progress_context(context: impl Into<String>) -> GenerationProgressContextGuard {
27 GENERATION_PROGRESS_CONTEXT.with(|stack| stack.borrow_mut().push(context.into()));
28 GenerationProgressContextGuard
29}
30
31pub fn detailed_progress_message(message: &str) -> String {
32 GENERATION_PROGRESS_CONTEXT.with(|context| {
33 let context = context.borrow();
34 if context.is_empty() {
35 message.to_string()
36 } else {
37 format!("{} / {message}", context.join(" / "))
38 }
39 })
40}
41
42pub fn enter_detailed_progress_span(message: &str) -> Option<tracing::span::EnteredSpan> {
43 if !detailed_progress_enabled() {
44 return None;
45 }
46
47 let progress_message = detailed_progress_message(message);
48 Some(
49 tracing::info_span!(
50 "Generation progress",
51 indicatif.pb_show = true,
52 indicatif.pb_msg = %progress_message,
53 )
54 .entered(),
55 )
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum GenerationProgressMode {
60 Detailed,
61 Aggregate,
62}
63
64impl GenerationProgressMode {
65 fn as_u8(self) -> u8 {
66 match self {
67 Self::Detailed => 0,
68 Self::Aggregate => 1,
69 }
70 }
71
72 fn from_u8(value: u8) -> Self {
73 match value {
74 1 => Self::Aggregate,
75 _ => Self::Detailed,
76 }
77 }
78}
79
80static GENERATION_PROGRESS_MODE: AtomicU8 = AtomicU8::new(0);
81
82pub struct GenerationProgressModeGuard {
83 previous: u8,
84}
85
86impl GenerationProgressModeGuard {
87 pub fn set(mode: GenerationProgressMode) -> Self {
88 let previous = GENERATION_PROGRESS_MODE.swap(mode.as_u8(), Ordering::Relaxed);
89 Self { previous }
90 }
91}
92
93impl Drop for GenerationProgressModeGuard {
94 fn drop(&mut self) {
95 GENERATION_PROGRESS_MODE.store(self.previous, Ordering::Relaxed);
96 }
97}
98
99pub fn current_generation_progress_mode() -> GenerationProgressMode {
100 GenerationProgressMode::from_u8(GENERATION_PROGRESS_MODE.load(Ordering::Relaxed))
101}
102
103pub fn detailed_progress_enabled() -> bool {
104 current_generation_progress_mode() == GenerationProgressMode::Detailed
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub enum GenerationProcessKind {
109 Amplitude,
110 CrossSection,
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114pub enum GenerationProgressPhase {
115 GraphPreprocessing,
116 GraphGeneration,
117 Backend,
118}
119
120pub trait GenerationProgressObserver: Send + Sync {
121 fn begin_phase(
122 &self,
123 _phase: GenerationProgressPhase,
124 _kind: GenerationProcessKind,
125 _process: &str,
126 _integrand: &str,
127 _total_graphs: usize,
128 _total_cuts: Option<usize>,
129 ) {
130 }
131
132 fn graph_started(
133 &self,
134 _kind: GenerationProcessKind,
135 _integrand: &str,
136 _graph: &str,
137 _cut_count: Option<usize>,
138 ) {
139 }
140
141 fn graph_finished(
142 &self,
143 _kind: GenerationProcessKind,
144 _integrand: &str,
145 _graph: &str,
146 _stats: &GraphGenerationStats,
147 _completed_cuts: Option<usize>,
148 ) {
149 }
150
151 fn cuts_discovered(
152 &self,
153 _integrand: &str,
154 _graph: &str,
155 _st_cut_count: usize,
156 _valid_cut_count: usize,
157 ) {
158 }
159
160 fn cut_finished(&self, _integrand: &str, _graph: &str, _cut_count: usize) {}
161
162 fn backend_started(&self, _kind: GenerationProcessKind, _integrand: &str, _graph_count: usize) {
163 }
164
165 fn backend_finished(&self, _kind: GenerationProcessKind, _integrand: &str, _elapsed: Duration) {
166 }
167}
168
169static GENERATION_PROGRESS_OBSERVER: OnceLock<Mutex<Option<Arc<dyn GenerationProgressObserver>>>> =
170 OnceLock::new();
171
172fn observer_slot() -> &'static Mutex<Option<Arc<dyn GenerationProgressObserver>>> {
173 GENERATION_PROGRESS_OBSERVER.get_or_init(|| Mutex::new(None))
174}
175
176fn observer() -> Option<Arc<dyn GenerationProgressObserver>> {
177 observer_slot().lock().ok().and_then(|guard| guard.clone())
178}
179
180pub struct GenerationProgressObserverGuard {
181 previous: Option<Arc<dyn GenerationProgressObserver>>,
182}
183
184impl GenerationProgressObserverGuard {
185 pub fn set(observer: Arc<dyn GenerationProgressObserver>) -> Self {
186 let previous = observer_slot()
187 .lock()
188 .expect("generation progress observer mutex is poisoned")
189 .replace(observer);
190 Self { previous }
191 }
192}
193
194impl Drop for GenerationProgressObserverGuard {
195 fn drop(&mut self) {
196 *observer_slot()
197 .lock()
198 .expect("generation progress observer mutex is poisoned") = self.previous.take();
199 }
200}
201
202pub fn begin_phase(
203 phase: GenerationProgressPhase,
204 kind: GenerationProcessKind,
205 process: &str,
206 integrand: &str,
207 total_graphs: usize,
208 total_cuts: Option<usize>,
209) {
210 if let Some(observer) = observer() {
211 observer.begin_phase(phase, kind, process, integrand, total_graphs, total_cuts);
212 }
213}
214
215pub fn graph_started(
216 kind: GenerationProcessKind,
217 integrand: &str,
218 graph: &str,
219 cut_count: Option<usize>,
220) {
221 if let Some(observer) = observer() {
222 observer.graph_started(kind, integrand, graph, cut_count);
223 }
224}
225
226pub fn graph_finished(
227 kind: GenerationProcessKind,
228 integrand: &str,
229 graph: &str,
230 stats: &GraphGenerationStats,
231 completed_cuts: Option<usize>,
232) {
233 if let Some(observer) = observer() {
234 observer.graph_finished(kind, integrand, graph, stats, completed_cuts);
235 }
236}
237
238pub fn cuts_discovered(integrand: &str, graph: &str, st_cut_count: usize, valid_cut_count: usize) {
239 if let Some(observer) = observer() {
240 observer.cuts_discovered(integrand, graph, st_cut_count, valid_cut_count);
241 }
242}
243
244pub fn cut_finished(integrand: &str, graph: &str, cut_count: usize) {
245 if let Some(observer) = observer() {
246 observer.cut_finished(integrand, graph, cut_count);
247 }
248}
249
250pub fn backend_started(kind: GenerationProcessKind, integrand: &str, graph_count: usize) {
251 if let Some(observer) = observer() {
252 observer.backend_started(kind, integrand, graph_count);
253 }
254}
255
256pub fn backend_finished(kind: GenerationProcessKind, integrand: &str, elapsed: Duration) {
257 if let Some(observer) = observer() {
258 observer.backend_finished(kind, integrand, elapsed);
259 }
260}