1use super::{TimerCompletion, TimerCompletionOutcome, TimerEpoch, TimerOutcomeSnapshot};
4
5#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
10pub struct TimerCounters {
11 requested: u64,
12 armed: u64,
13 started: u64,
14 completed: u64,
15 succeeded: u64,
16 no_work: u64,
17 retryable_failure: u64,
18 invariant_failure: u64,
19 cancelled: u64,
20 stale: u64,
21 coalesced: u64,
22 interrupted: u64,
23}
24
25impl TimerCounters {
26 pub const fn record_request(&mut self) {
28 self.requested = self.requested.saturating_add(1);
29 }
30
31 pub const fn record_arm(&mut self) {
33 self.armed = self.armed.saturating_add(1);
34 }
35
36 pub const fn record_start(&mut self) {
38 self.started = self.started.saturating_add(1);
39 }
40
41 pub const fn record_completion(&mut self, outcome: TimerCompletionOutcome) {
43 self.completed = self.completed.saturating_add(1);
44 match outcome {
45 TimerCompletionOutcome::Success => {
46 self.succeeded = self.succeeded.saturating_add(1);
47 }
48 TimerCompletionOutcome::NoWork => {
49 self.no_work = self.no_work.saturating_add(1);
50 }
51 TimerCompletionOutcome::RetryableFailure => {
52 self.retryable_failure = self.retryable_failure.saturating_add(1);
53 }
54 TimerCompletionOutcome::InvariantFailure => {
55 self.invariant_failure = self.invariant_failure.saturating_add(1);
56 }
57 }
58 }
59
60 pub const fn record_cancellation(&mut self) {
62 self.cancelled = self.cancelled.saturating_add(1);
63 }
64
65 pub const fn record_stale(&mut self) {
67 self.stale = self.stale.saturating_add(1);
68 }
69
70 pub const fn record_coalesced(&mut self) {
72 self.coalesced = self.coalesced.saturating_add(1);
73 }
74
75 pub const fn record_interruption(&mut self) {
81 self.interrupted = self.interrupted.saturating_add(1);
82 }
83
84 #[must_use]
86 pub const fn requested(self) -> u64 {
87 self.requested
88 }
89
90 #[must_use]
92 pub const fn armed(self) -> u64 {
93 self.armed
94 }
95
96 #[must_use]
98 pub const fn started(self) -> u64 {
99 self.started
100 }
101
102 #[must_use]
104 pub const fn completed(self) -> u64 {
105 self.completed
106 }
107
108 #[must_use]
110 pub const fn succeeded(self) -> u64 {
111 self.succeeded
112 }
113
114 #[must_use]
116 pub const fn no_work(self) -> u64 {
117 self.no_work
118 }
119
120 #[must_use]
122 pub const fn retryable_failure(self) -> u64 {
123 self.retryable_failure
124 }
125
126 #[must_use]
128 pub const fn invariant_failure(self) -> u64 {
129 self.invariant_failure
130 }
131
132 #[must_use]
134 pub const fn cancelled(self) -> u64 {
135 self.cancelled
136 }
137
138 #[must_use]
140 pub const fn stale(self) -> u64 {
141 self.stale
142 }
143
144 #[must_use]
146 pub const fn coalesced(self) -> u64 {
147 self.coalesced
148 }
149
150 #[must_use]
152 pub const fn interrupted(self) -> u64 {
153 self.interrupted
154 }
155
156 #[must_use]
158 pub const fn completion_partition_is_valid(self) -> bool {
159 self.completed
160 == self
161 .succeeded
162 .saturating_add(self.no_work)
163 .saturating_add(self.retryable_failure)
164 .saturating_add(self.invariant_failure)
165 }
166}
167
168#[derive(Clone, Copy, Debug, Eq, PartialEq)]
170pub struct TimerMeasurement {
171 pub instructions: u64,
173 pub elapsed_ns: u64,
175}
176
177#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
179pub struct MeasurementSummary {
180 samples: u64,
181 total: u64,
182 latest: Option<u64>,
183 maximum: Option<u64>,
184}
185
186impl MeasurementSummary {
187 pub const fn record(&mut self, value: u64) {
189 self.samples = self.samples.saturating_add(1);
190 self.total = self.total.saturating_add(value);
191 self.latest = Some(value);
192 self.maximum = Some(match self.maximum {
193 Some(current) if current > value => current,
194 Some(_) | None => value,
195 });
196 }
197
198 #[must_use]
200 pub const fn samples(self) -> u64 {
201 self.samples
202 }
203
204 #[must_use]
206 pub const fn total(self) -> u64 {
207 self.total
208 }
209
210 #[must_use]
212 pub const fn latest(self) -> Option<u64> {
213 self.latest
214 }
215
216 #[must_use]
218 pub const fn maximum(self) -> Option<u64> {
219 self.maximum
220 }
221}
222
223#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
225pub struct TimerPerformance {
226 instructions: MeasurementSummary,
227 elapsed_ns: MeasurementSummary,
228}
229
230impl TimerPerformance {
231 pub const fn record(&mut self, measurement: TimerMeasurement) {
233 self.instructions.record(measurement.instructions);
234 self.elapsed_ns.record(measurement.elapsed_ns);
235 }
236
237 #[must_use]
239 pub const fn instructions(self) -> MeasurementSummary {
240 self.instructions
241 }
242
243 #[must_use]
245 pub const fn elapsed_ns(self) -> MeasurementSummary {
246 self.elapsed_ns
247 }
248}
249
250#[derive(Clone, Copy, Debug, Eq, PartialEq)]
252pub struct TimerObservabilitySnapshot {
253 epoch: TimerEpoch,
254 outcomes: TimerOutcomeSnapshot,
255 counters: TimerCounters,
256 performance: TimerPerformance,
257}
258
259impl TimerObservabilitySnapshot {
260 #[must_use]
262 pub fn new(epoch: TimerEpoch) -> Self {
263 Self {
264 epoch,
265 outcomes: TimerOutcomeSnapshot::default(),
266 counters: TimerCounters::default(),
267 performance: TimerPerformance::default(),
268 }
269 }
270
271 pub fn begin_epoch(&mut self, epoch: TimerEpoch) {
277 *self = Self::new(epoch);
278 }
279
280 pub const fn record_completion(
284 &mut self,
285 completion: TimerCompletion,
286 completed_at_ns: u64,
287 measurement: Option<TimerMeasurement>,
288 ) {
289 self.outcomes.record_completion(completion, completed_at_ns);
290 self.counters.record_completion(completion.outcome);
291 if let Some(measurement) = measurement {
292 self.performance.record(measurement);
293 }
294 }
295
296 pub const fn record_interruption(&mut self, observed_at_ns: u64) {
298 self.outcomes.record_interruption(observed_at_ns);
299 self.counters.record_interruption();
300 }
301
302 pub const fn record_request(&mut self) {
304 self.counters.record_request();
305 }
306
307 pub const fn record_arm(&mut self) {
309 self.counters.record_arm();
310 }
311
312 pub const fn record_start(&mut self) {
314 self.counters.record_start();
315 }
316
317 pub const fn record_cancellation(&mut self) {
319 self.counters.record_cancellation();
320 }
321
322 pub const fn record_stale(&mut self) {
324 self.counters.record_stale();
325 }
326
327 pub const fn record_coalesced(&mut self) {
329 self.counters.record_coalesced();
330 }
331
332 #[must_use]
334 pub const fn epoch(self) -> TimerEpoch {
335 self.epoch
336 }
337
338 #[must_use]
340 pub const fn outcomes(self) -> TimerOutcomeSnapshot {
341 self.outcomes
342 }
343
344 #[must_use]
346 pub const fn counters(self) -> TimerCounters {
347 self.counters
348 }
349
350 #[must_use]
352 pub const fn performance(self) -> TimerPerformance {
353 self.performance
354 }
355
356 #[must_use]
358 pub const fn consecutive_expected_failures(self) -> u64 {
359 self.outcomes.consecutive_expected_failures()
360 }
361}
362
363#[cfg(test)]
364mod tests {
365 use super::*;
366
367 #[test]
368 fn all_counters_saturate() {
369 let mut counters = TimerCounters {
370 requested: u64::MAX,
371 armed: u64::MAX,
372 started: u64::MAX,
373 completed: u64::MAX,
374 succeeded: u64::MAX,
375 no_work: u64::MAX,
376 retryable_failure: u64::MAX,
377 invariant_failure: u64::MAX,
378 cancelled: u64::MAX,
379 stale: u64::MAX,
380 coalesced: u64::MAX,
381 interrupted: u64::MAX,
382 };
383
384 counters.record_request();
385 counters.record_arm();
386 counters.record_start();
387 counters.record_completion(TimerCompletionOutcome::Success);
388 counters.record_cancellation();
389 counters.record_stale();
390 counters.record_coalesced();
391 counters.record_interruption();
392
393 assert_eq!(counters.requested(), u64::MAX);
394 assert_eq!(counters.armed(), u64::MAX);
395 assert_eq!(counters.started(), u64::MAX);
396 assert_eq!(counters.completed(), u64::MAX);
397 assert_eq!(counters.succeeded(), u64::MAX);
398 assert_eq!(counters.cancelled(), u64::MAX);
399 assert_eq!(counters.stale(), u64::MAX);
400 assert_eq!(counters.coalesced(), u64::MAX);
401 assert_eq!(counters.interrupted(), u64::MAX);
402 assert!(counters.completion_partition_is_valid());
403 }
404
405 #[test]
406 fn measurement_count_and_total_saturate_while_latest_and_maximum_advance() {
407 let mut summary = MeasurementSummary {
408 samples: u64::MAX,
409 total: u64::MAX,
410 latest: Some(10),
411 maximum: Some(20),
412 };
413
414 summary.record(30);
415
416 assert_eq!(summary.samples(), u64::MAX);
417 assert_eq!(summary.total(), u64::MAX);
418 assert_eq!(summary.latest(), Some(30));
419 assert_eq!(summary.maximum(), Some(30));
420 }
421}