1use std::{
2 collections::HashSet,
3 fmt::Debug,
4 sync::atomic::{AtomicBool, AtomicU32, Ordering},
5};
6
7use crate::ENABLED;
8
9#[cfg(feature = "enabled")]
10#[doc(hidden)]
11#[linkme::distributed_slice]
12pub static FAULT_CATALOG: [FaultEntry];
13
14#[cfg(not(feature = "enabled"))]
15#[doc(hidden)]
16pub static FAULT_CATALOG: [&FaultEntry; 0] = [];
17
18pub(crate) fn init_faults() {
19 let mut seen = HashSet::new();
20 for entry in FAULT_CATALOG {
21 if !seen.insert(entry.name) {
23 panic!("Duplicate Precept fault: {}", entry.name);
24 }
25 }
26}
27
28#[derive(Debug)]
33pub struct FaultEntry {
34 name: &'static str,
36
37 enabled: AtomicBool,
39
40 pending_trips: AtomicU32,
43}
44
45impl FaultEntry {
46 pub const fn new(name: &'static str) -> Self {
48 Self {
49 name,
50 enabled: AtomicBool::new(true),
51 pending_trips: AtomicU32::new(0),
52 }
53 }
54
55 pub fn trip(&self) -> bool {
57 if self
58 .pending_trips
59 .fetch_update(Ordering::AcqRel, Ordering::Acquire, |count| {
60 if count > 0 { Some(count - 1) } else { None }
61 })
62 .is_ok()
63 {
64 true
66 } else if self.enabled.load(Ordering::Acquire) {
67 let should_fault = crate::dispatch::choose(&[true, false]);
68 should_fault.is_some_and(|&t| t)
69 } else {
70 false
71 }
72 }
73
74 pub fn enable(&self) {
76 self.enabled.store(true, Ordering::Release);
77 }
78
79 pub fn disable(&self) {
81 self.enabled.store(false, Ordering::Release);
82 }
83
84 pub fn set_pending(&self, count: u32) {
89 self.pending_trips.store(count, Ordering::Release);
90 }
91
92 pub fn count_pending(&self) -> u32 {
94 self.pending_trips.load(Ordering::Acquire)
95 }
96}
97
98pub fn enable_all() {
102 assert!(ENABLED, "Precept is disabled");
103 for entry in FAULT_CATALOG {
104 entry.enable()
105 }
106}
107
108pub fn disable_all() {
110 tracing::warn!("Precept Faults disabled");
111 for entry in FAULT_CATALOG {
112 entry.disable();
113 }
114}
115
116pub fn get_fault_by_name(name: &str) -> Option<&'static FaultEntry> {
120 FAULT_CATALOG.into_iter().find(|&entry| entry.name == name)
121}