1use super::{TimerCompletion, TimerCompletionOutcome, TimerEpoch, TimerOutcomeSnapshot};
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub struct TimerCounters {
11 schedule_requests: u64,
12 wakeups_armed: u64,
13 work_dispatched: u64,
14 scheduler_started: u64,
15 work_started: u64,
16 work_completed: u64,
17 succeeded: u64,
18 no_work: u64,
19 retryable_failure: u64,
20 invariant_failure: u64,
21 cancelled: u64,
22 stale_wakeups: u64,
23 stale_work: u64,
24 coalesced: u64,
25 unacknowledged: u64,
26}
27
28impl TimerCounters {
29 const EMPTY: Self = Self {
30 schedule_requests: 0,
31 wakeups_armed: 0,
32 work_dispatched: 0,
33 scheduler_started: 0,
34 work_started: 0,
35 work_completed: 0,
36 succeeded: 0,
37 no_work: 0,
38 retryable_failure: 0,
39 invariant_failure: 0,
40 cancelled: 0,
41 stale_wakeups: 0,
42 stale_work: 0,
43 coalesced: 0,
44 unacknowledged: 0,
45 };
46
47 pub(crate) const fn record_schedule_request(&mut self) {
48 self.schedule_requests = self.schedule_requests.saturating_add(1);
49 }
50
51 pub(crate) const fn record_wakeup_armed(&mut self) {
52 self.wakeups_armed = self.wakeups_armed.saturating_add(1);
53 }
54
55 pub(crate) const fn record_work_dispatched(&mut self) {
56 self.work_dispatched = self.work_dispatched.saturating_add(1);
57 }
58
59 pub(crate) const fn record_scheduler_started(&mut self) {
60 self.scheduler_started = self.scheduler_started.saturating_add(1);
61 }
62
63 pub(crate) const fn record_work_started(&mut self) {
64 self.work_started = self.work_started.saturating_add(1);
65 }
66
67 pub(crate) const fn record_completion(&mut self, outcome: TimerCompletionOutcome) {
68 self.work_completed = self.work_completed.saturating_add(1);
69 match outcome {
70 TimerCompletionOutcome::Success => {
71 self.succeeded = self.succeeded.saturating_add(1);
72 }
73 TimerCompletionOutcome::NoWork => {
74 self.no_work = self.no_work.saturating_add(1);
75 }
76 TimerCompletionOutcome::RetryableFailure => {
77 self.retryable_failure = self.retryable_failure.saturating_add(1);
78 }
79 TimerCompletionOutcome::InvariantFailure => {
80 self.invariant_failure = self.invariant_failure.saturating_add(1);
81 }
82 }
83 }
84
85 pub(crate) const fn record_cancellation(&mut self) {
86 self.cancelled = self.cancelled.saturating_add(1);
87 }
88
89 pub(crate) const fn record_stale_wakeup(&mut self) {
90 self.stale_wakeups = self.stale_wakeups.saturating_add(1);
91 }
92
93 pub(crate) const fn record_stale_work(&mut self) {
94 self.stale_work = self.stale_work.saturating_add(1);
95 }
96
97 pub(crate) const fn record_coalesced(&mut self) {
98 self.coalesced = self.coalesced.saturating_add(1);
99 }
100
101 pub(crate) const fn record_unacknowledged(&mut self) {
102 self.unacknowledged = self.unacknowledged.saturating_add(1);
103 }
104
105 #[must_use]
107 pub const fn schedule_requests(self) -> u64 {
108 self.schedule_requests
109 }
110
111 #[must_use]
113 pub const fn wakeups_armed(self) -> u64 {
114 self.wakeups_armed
115 }
116
117 #[must_use]
119 pub const fn work_dispatched(self) -> u64 {
120 self.work_dispatched
121 }
122
123 #[must_use]
125 pub const fn scheduler_started(self) -> u64 {
126 self.scheduler_started
127 }
128
129 #[must_use]
131 pub const fn work_started(self) -> u64 {
132 self.work_started
133 }
134
135 #[must_use]
137 pub const fn work_completed(self) -> u64 {
138 self.work_completed
139 }
140
141 #[must_use]
143 pub const fn succeeded(self) -> u64 {
144 self.succeeded
145 }
146
147 #[must_use]
149 pub const fn no_work(self) -> u64 {
150 self.no_work
151 }
152
153 #[must_use]
155 pub const fn retryable_failure(self) -> u64 {
156 self.retryable_failure
157 }
158
159 #[must_use]
161 pub const fn invariant_failure(self) -> u64 {
162 self.invariant_failure
163 }
164
165 #[must_use]
167 pub const fn cancelled(self) -> u64 {
168 self.cancelled
169 }
170
171 #[must_use]
173 pub const fn stale_wakeups(self) -> u64 {
174 self.stale_wakeups
175 }
176
177 #[must_use]
179 pub const fn stale_work(self) -> u64 {
180 self.stale_work
181 }
182
183 #[must_use]
185 pub const fn coalesced(self) -> u64 {
186 self.coalesced
187 }
188
189 #[must_use]
191 pub const fn unacknowledged(self) -> u64 {
192 self.unacknowledged
193 }
194
195 #[must_use]
197 #[cfg(test)]
198 pub(crate) const fn completion_partition_is_valid(self) -> bool {
199 self.work_completed
200 == self
201 .succeeded
202 .saturating_add(self.no_work)
203 .saturating_add(self.retryable_failure)
204 .saturating_add(self.invariant_failure)
205 }
206}
207
208#[derive(Clone, Copy, Debug, Eq, PartialEq)]
210pub struct MeasurementSummary {
211 samples: u64,
212 total: u64,
213 latest: Option<u64>,
214 maximum: Option<u64>,
215}
216
217impl MeasurementSummary {
218 const EMPTY: Self = Self {
219 samples: 0,
220 total: 0,
221 latest: None,
222 maximum: None,
223 };
224
225 pub(crate) const fn record(&mut self, value: u64) {
226 self.samples = self.samples.saturating_add(1);
227 self.total = self.total.saturating_add(value);
228 self.latest = Some(value);
229 self.maximum = Some(match self.maximum {
230 Some(current) if current > value => current,
231 Some(_) | None => value,
232 });
233 }
234
235 #[must_use]
237 pub const fn samples(self) -> u64 {
238 self.samples
239 }
240
241 #[must_use]
243 pub const fn total(self) -> u64 {
244 self.total
245 }
246
247 #[must_use]
249 pub const fn latest(self) -> Option<u64> {
250 self.latest
251 }
252
253 #[must_use]
255 pub const fn maximum(self) -> Option<u64> {
256 self.maximum
257 }
258}
259
260#[derive(Clone, Copy, Debug, Eq, PartialEq)]
262pub struct TimerPerformance {
263 scheduler_instructions: MeasurementSummary,
264 work_instructions: MeasurementSummary,
265}
266
267impl TimerPerformance {
268 const EMPTY: Self = Self {
269 scheduler_instructions: MeasurementSummary::EMPTY,
270 work_instructions: MeasurementSummary::EMPTY,
271 };
272
273 pub(crate) const fn record_scheduler(&mut self, instructions: u64) {
274 self.scheduler_instructions.record(instructions);
275 }
276
277 pub(crate) const fn record_work(&mut self, instructions: u64) {
278 self.work_instructions.record(instructions);
279 }
280
281 #[must_use]
283 pub const fn scheduler_instructions(self) -> MeasurementSummary {
284 self.scheduler_instructions
285 }
286
287 #[must_use]
289 pub const fn work_instructions(self) -> MeasurementSummary {
290 self.work_instructions
291 }
292}
293
294#[derive(Clone, Copy, Debug, Eq, PartialEq)]
296pub struct TimerObservabilitySnapshot {
297 epoch: TimerEpoch,
298 outcomes: TimerOutcomeSnapshot,
299 counters: TimerCounters,
300 performance: TimerPerformance,
301}
302
303impl TimerObservabilitySnapshot {
304 pub(crate) const fn new(epoch: TimerEpoch) -> Self {
305 Self {
306 epoch,
307 outcomes: TimerOutcomeSnapshot::EMPTY,
308 counters: TimerCounters::EMPTY,
309 performance: TimerPerformance::EMPTY,
310 }
311 }
312
313 pub(crate) const fn record_completion(
314 &mut self,
315 completion: TimerCompletion,
316 completed_at_ns: u64,
317 ) {
318 self.outcomes.record_completion(completion, completed_at_ns);
319 self.counters.record_completion(completion.outcome());
320 }
321
322 pub(crate) const fn record_unacknowledged(&mut self, observed_at_ns: u64) {
323 self.outcomes.record_unacknowledged(observed_at_ns);
324 self.counters.record_unacknowledged();
325 }
326
327 pub(crate) const fn counters_mut(&mut self) -> &mut TimerCounters {
328 &mut self.counters
329 }
330
331 pub(crate) const fn record_scheduler_instructions(&mut self, instructions: u64) {
332 self.performance.record_scheduler(instructions);
333 }
334
335 pub(crate) const fn record_work_instructions(&mut self, instructions: u64) {
336 self.performance.record_work(instructions);
337 }
338
339 #[must_use]
341 pub const fn epoch(self) -> TimerEpoch {
342 self.epoch
343 }
344
345 #[must_use]
347 pub const fn outcomes(self) -> TimerOutcomeSnapshot {
348 self.outcomes
349 }
350
351 #[must_use]
353 pub const fn counters(self) -> TimerCounters {
354 self.counters
355 }
356
357 #[must_use]
359 pub const fn performance(self) -> TimerPerformance {
360 self.performance
361 }
362}
363
364#[cfg(test)]
365mod tests {
366 use super::*;
367
368 #[test]
369 fn all_counters_saturate() {
370 let mut counters = TimerCounters {
371 schedule_requests: u64::MAX,
372 wakeups_armed: u64::MAX,
373 work_dispatched: u64::MAX,
374 scheduler_started: u64::MAX,
375 work_started: u64::MAX,
376 work_completed: u64::MAX,
377 succeeded: u64::MAX,
378 no_work: u64::MAX,
379 retryable_failure: u64::MAX,
380 invariant_failure: u64::MAX,
381 cancelled: u64::MAX,
382 stale_wakeups: u64::MAX,
383 stale_work: u64::MAX,
384 coalesced: u64::MAX,
385 unacknowledged: u64::MAX,
386 };
387
388 counters.record_schedule_request();
389 counters.record_wakeup_armed();
390 counters.record_work_dispatched();
391 counters.record_scheduler_started();
392 counters.record_work_started();
393 counters.record_completion(TimerCompletionOutcome::Success);
394 counters.record_cancellation();
395 counters.record_stale_wakeup();
396 counters.record_stale_work();
397 counters.record_coalesced();
398 counters.record_unacknowledged();
399
400 assert_eq!(counters.schedule_requests(), u64::MAX);
401 assert_eq!(counters.wakeups_armed(), u64::MAX);
402 assert_eq!(counters.work_dispatched(), u64::MAX);
403 assert_eq!(counters.work_completed(), u64::MAX);
404 assert_eq!(counters.cancelled(), u64::MAX);
405 assert_eq!(counters.stale_wakeups(), u64::MAX);
406 assert_eq!(counters.stale_work(), u64::MAX);
407 assert_eq!(counters.coalesced(), u64::MAX);
408 assert_eq!(counters.unacknowledged(), u64::MAX);
409 assert!(counters.completion_partition_is_valid());
410 }
411
412 #[test]
413 fn instruction_roles_are_separate_and_saturating() {
414 let mut performance = TimerPerformance::EMPTY;
415 performance.record_scheduler(20);
416 performance.record_work(30);
417 performance.record_work(10);
418
419 assert_eq!(performance.scheduler_instructions().total(), 20);
420 assert_eq!(performance.work_instructions().samples(), 2);
421 assert_eq!(performance.work_instructions().total(), 40);
422 assert_eq!(performance.work_instructions().latest(), Some(10));
423 assert_eq!(performance.work_instructions().maximum(), Some(30));
424 }
425}