nros_node/executor/
level_liveness.rs1use portable_atomic::{AtomicU8, AtomicU32, Ordering::SeqCst};
33
34pub const MAX_LEVELS: usize = 32;
37
38pub(crate) struct LevelRegistry {
40 count: AtomicU8,
41 checked_in: AtomicU32,
42 spins: [AtomicU32; MAX_LEVELS],
43 last_spin_us: [AtomicU32; MAX_LEVELS],
44}
45
46impl LevelRegistry {
47 pub(crate) const fn new() -> Self {
48 Self {
49 count: AtomicU8::new(1),
50 checked_in: AtomicU32::new(0),
51 spins: [const { AtomicU32::new(0) }; MAX_LEVELS],
52 last_spin_us: [const { AtomicU32::new(0) }; MAX_LEVELS],
53 }
54 }
55
56 fn all(&self) -> u32 {
57 let n = self.count.load(SeqCst) as u32;
58 if n >= 32 { u32::MAX } else { (1u32 << n) - 1 }
59 }
60
61 pub(crate) fn set_count(&self, n: u8) -> bool {
62 if n == 0 || n as usize > MAX_LEVELS {
63 return false;
64 }
65 self.count.store(n, SeqCst);
66 true
67 }
68
69 pub(crate) fn count(&self) -> u8 {
70 self.count.load(SeqCst)
71 }
72
73 #[cfg_attr(not(any(has_rmw, test)), allow(dead_code))]
76 pub(crate) fn check_in(&self, level: u8, now_us: Option<u64>) {
77 let l = level as usize;
78 if l >= MAX_LEVELS {
79 return;
80 }
81 if let Some(t) = now_us {
82 self.last_spin_us[l].store(t as u32, SeqCst);
83 }
84 self.spins[l].fetch_add(1, SeqCst);
85 self.checked_in.fetch_or(1u32 << l, SeqCst);
86 }
87
88 pub(crate) fn spins(&self, level: u8) -> u32 {
89 self.spins.get(level as usize).map_or(0, |s| s.load(SeqCst))
90 }
91
92 pub(crate) fn last_spin_us(&self, level: u8) -> Option<u32> {
93 (self.spins(level) != 0).then(|| self.last_spin_us[level as usize].load(SeqCst))
94 }
95
96 pub(crate) fn every_alive(&self) -> bool {
97 let all = self.all();
98 self.checked_in.load(SeqCst) & all == all
99 }
100
101 pub(crate) fn take_every_alive(&self) -> bool {
102 let all = self.all();
103 loop {
104 let cur = self.checked_in.load(SeqCst);
105 if cur & all != all {
106 return false;
107 }
108 if self
109 .checked_in
110 .compare_exchange(cur, cur & !all, SeqCst, SeqCst)
111 .is_ok()
112 {
113 return true;
114 }
115 }
116 }
117}
118
119pub(crate) static LEVELS: LevelRegistry = LevelRegistry::new();
120
121pub fn tier_level_by(
129 n: usize,
130 index: usize,
131 smaller_is_urgent: bool,
132 priority_of: impl Fn(usize) -> i64,
133) -> Option<u8> {
134 if index >= n {
135 return None;
136 }
137 let mine = priority_of(index);
138 let ahead = (0..n)
139 .filter(|&j| {
140 let q = priority_of(j);
141 (if smaller_is_urgent {
142 q < mine
143 } else {
144 q > mine
145 }) || (q == mine && j < index)
146 })
147 .count();
148 u8::try_from(ahead).ok()
149}
150
151pub fn tier_level(priorities: &[i64], index: usize, smaller_is_urgent: bool) -> Option<u8> {
153 tier_level_by(priorities.len(), index, smaller_is_urgent, |i| {
154 priorities[i]
155 })
156}
157
158pub fn set_level_count(n: u8) -> bool {
162 LEVELS.set_count(n)
163}
164
165pub fn level_count() -> u8 {
167 LEVELS.count()
168}
169
170pub fn level_spins(level: u8) -> u32 {
172 LEVELS.spins(level)
173}
174
175pub fn level_last_spin_us(level: u8) -> Option<u32> {
178 LEVELS.last_spin_us(level)
179}
180
181pub fn every_level_alive() -> bool {
184 LEVELS.every_alive()
185}
186
187pub fn take_every_level_alive() -> bool {
191 LEVELS.take_every_alive()
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 #[test]
202 fn a_tiers_level_is_its_urgency_order() {
203 let p = [6, 5, 8, 6];
204 let levels: [Option<u8>; 4] = core::array::from_fn(|i| tier_level(&p, i, true));
205 assert_eq!(levels, [Some(1), Some(0), Some(3), Some(2)]);
206 let levels: [Option<u8>; 4] = core::array::from_fn(|i| tier_level(&p, i, false));
207 assert_eq!(levels, [Some(1), Some(3), Some(0), Some(2)]);
208 assert_eq!(tier_level(&p, 4, true), None);
209 assert_eq!(tier_level(&[5], 0, true), Some(0));
210 }
211
212 #[test]
215 fn every_level_must_check_in_before_the_feed() {
216 let r = LevelRegistry::new();
217 assert_eq!(r.count(), 1, "an image that states nothing has one level");
218 assert!(!r.set_count(0) && !r.set_count(33));
219 assert!(r.set_count(2));
220 assert_eq!(r.last_spin_us(1), None);
221 r.check_in(0, Some(1_000));
222 assert!(!r.every_alive(), "level 1 has not checked in");
223 assert!(!r.take_every_alive());
224 r.check_in(1, Some(1_250));
225 assert!(r.every_alive());
226 assert!(r.take_every_alive());
227 assert!(!r.every_alive(), "taken: a new round starts");
228 r.check_in(0, Some(2_000));
229 assert!(!r.take_every_alive(), "level 0 alone does not feed");
230 assert_eq!(r.last_spin_us(0), Some(2_000));
231 assert_eq!((r.spins(0), r.spins(1)), (2, 1));
232 r.check_in(1, None);
234 assert_eq!(r.last_spin_us(1), Some(1_250));
235 assert!(r.take_every_alive());
236 }
237}