1use std::{
12 hint::spin_loop,
13 sync::{
14 Arc,
15 atomic::{
16 AtomicU64,
17 Ordering,
18 },
19 },
20 thread,
21};
22
23use qubit_fast_cas::CasCell;
24
25#[cfg(feature = "serde")]
26use serde::{
27 Deserialize,
28 Deserializer,
29};
30
31#[cfg(feature = "serde")]
32use crate::validation::{
33 validate_metrics,
34 validate_snapshot_counts,
35};
36use crate::{
37 MetricError,
38 internal::OperationState,
39};
40
41#[derive(Clone, Debug, Eq, PartialEq)]
43pub struct Metric {
44 pub(crate) id: Arc<str>,
46 pub(crate) name: Arc<str>,
48 pub(crate) total: Option<u64>,
50}
51
52impl Metric {
53 #[must_use]
58 pub fn new(id: &str, name: &str) -> Self {
59 Self {
60 id: Arc::from(id),
61 name: Arc::from(name),
62 total: None,
63 }
64 }
65
66 #[must_use]
71 pub const fn total(mut self, total: u64) -> Self {
72 self.total = Some(total);
73 self
74 }
75
76 #[must_use]
78 pub fn id(&self) -> &str {
79 &self.id
80 }
81
82 #[must_use]
84 pub fn name(&self) -> &str {
85 &self.name
86 }
87
88 #[must_use]
90 pub const fn configured_total(&self) -> Option<u64> {
91 self.total
92 }
93}
94
95#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
97pub struct MetricDelta {
98 started: u64,
100 unclassified: u64,
102 succeeded: u64,
104 failed: u64,
106 cancelled: u64,
108}
109
110impl MetricDelta {
111 #[must_use]
113 pub const fn new() -> Self {
114 Self {
115 started: 0,
116 unclassified: 0,
117 succeeded: 0,
118 failed: 0,
119 cancelled: 0,
120 }
121 }
122
123 #[must_use]
125 pub const fn started(mut self, count: u64) -> Self {
126 self.started = count;
127 self
128 }
129
130 #[must_use]
132 pub const fn unclassified(mut self, count: u64) -> Self {
133 self.unclassified = count;
134 self
135 }
136
137 #[must_use]
139 pub const fn succeeded(mut self, count: u64) -> Self {
140 self.succeeded = count;
141 self
142 }
143
144 #[must_use]
146 pub const fn failed(mut self, count: u64) -> Self {
147 self.failed = count;
148 self
149 }
150
151 #[must_use]
153 pub const fn cancelled(mut self, count: u64) -> Self {
154 self.cancelled = count;
155 self
156 }
157}
158
159#[derive(Clone)]
165pub struct MetricHandle {
166 inner: Arc<MetricInner>,
168 operation_state: Arc<OperationState>,
170}
171
172impl MetricHandle {
173 pub(crate) fn new(
175 metric: Metric,
176 operation_state: Arc<OperationState>,
177 ) -> Self {
178 Self {
179 inner: Arc::new(MetricInner::new(metric)),
180 operation_state,
181 }
182 }
183
184 #[must_use]
186 pub fn id(&self) -> &str {
187 self.inner.metric.id()
188 }
189
190 #[must_use]
192 pub fn name(&self) -> &str {
193 self.inner.metric.name()
194 }
195
196 pub fn start(&self, count: u64) -> Result<(), MetricError> {
203 self.apply_delta(MetricDelta::new().started(count))
204 }
205
206 pub fn complete(&self, count: u64) -> Result<(), MetricError> {
213 self.apply_delta(MetricDelta::new().unclassified(count))
214 }
215
216 pub fn succeed(&self, count: u64) -> Result<(), MetricError> {
223 self.apply_delta(MetricDelta::new().succeeded(count))
224 }
225
226 pub fn fail(&self, count: u64) -> Result<(), MetricError> {
233 self.apply_delta(MetricDelta::new().failed(count))
234 }
235
236 pub fn cancel(&self, count: u64) -> Result<(), MetricError> {
243 self.apply_delta(MetricDelta::new().cancelled(count))
244 }
245
246 pub fn apply_delta(&self, delta: MetricDelta) -> Result<(), MetricError> {
254 let metric_id = self.id();
255 let total = self.inner.metric.configured_total();
256 let _update_guard = self.operation_state.enter_update(metric_id)?;
257
258 self.inner.with_update(|counts| {
259 let mut next = *counts;
260 apply_delta_to_counts(&mut next, delta, metric_id)?;
261 next.validate(metric_id, total)?;
262 *counts = next;
263 Ok(())
264 })
265 }
266
267 #[must_use]
271 pub fn snapshot(&self) -> MetricSnapshot {
272 let counts = self.inner.snapshot_counts();
273 MetricSnapshot::from_counts(&self.inner.metric, counts)
274 }
275}
276
277struct MetricInner {
279 metric: Metric,
281 gate: CasCell,
283 active: AtomicU64,
285 completed_unclassified: AtomicU64,
287 succeeded: AtomicU64,
289 failed: AtomicU64,
291 cancelled: AtomicU64,
293}
294
295impl MetricInner {
296 fn new(metric: Metric) -> Self {
298 Self {
299 metric,
300 gate: CasCell::new(0),
301 active: AtomicU64::new(0),
302 completed_unclassified: AtomicU64::new(0),
303 succeeded: AtomicU64::new(0),
304 failed: AtomicU64::new(0),
305 cancelled: AtomicU64::new(0),
306 }
307 }
308
309 fn with_update<R, F>(&self, mut update: F) -> Result<R, MetricError>
311 where
312 F: FnMut(&mut MetricCounts) -> Result<R, MetricError>,
313 {
314 let mut attempts = 0;
315 loop {
316 let version = self.gate.load();
317 if version & 1 != 0 {
318 wait_for_contention(attempts);
319 attempts += 1;
320 continue;
321 }
322
323 match self.gate.compare_set(version, version.wrapping_add(1)) {
324 Ok(()) => {
325 let _guard = MetricGateGuard::new(
326 &self.gate,
327 version.wrapping_add(2),
328 );
329 let mut counts = self.read_counts();
330 let result = update(&mut counts);
331 if result.is_ok() {
332 self.write_counts(&counts);
333 }
334 return result;
335 }
336 Err(_) => {
337 wait_for_contention(attempts);
338 attempts += 1;
339 }
340 }
341 }
342 }
343
344 fn read_counts(&self) -> MetricCounts {
346 MetricCounts {
347 active: self.active.load(Ordering::Acquire),
348 completed_unclassified: self
349 .completed_unclassified
350 .load(Ordering::Acquire),
351 succeeded: self.succeeded.load(Ordering::Acquire),
352 failed: self.failed.load(Ordering::Acquire),
353 cancelled: self.cancelled.load(Ordering::Acquire),
354 }
355 }
356
357 fn write_counts(&self, counts: &MetricCounts) {
359 self.active.store(counts.active, Ordering::Release);
360 self.completed_unclassified
361 .store(counts.completed_unclassified, Ordering::Release);
362 self.succeeded.store(counts.succeeded, Ordering::Release);
363 self.failed.store(counts.failed, Ordering::Release);
364 self.cancelled.store(counts.cancelled, Ordering::Release);
365 }
366
367 fn snapshot_counts(&self) -> MetricCounts {
369 let mut attempts = 0;
370 loop {
371 let start = self.gate.load();
372 if start & 1 != 0 {
373 wait_for_contention(attempts);
374 attempts += 1;
375 continue;
376 }
377
378 let counts = self.read_counts();
379 if start == self.gate.load() {
380 return counts;
381 }
382
383 wait_for_contention(attempts);
384 attempts += 1;
385 }
386 }
387}
388
389#[derive(Clone, Copy)]
391struct MetricCounts {
392 active: u64,
394 completed_unclassified: u64,
396 succeeded: u64,
398 failed: u64,
400 cancelled: u64,
402}
403
404impl MetricCounts {
405 fn completed(self) -> Option<u64> {
409 self.completed_unclassified
410 .checked_add(self.succeeded)?
411 .checked_add(self.failed)?
412 .checked_add(self.cancelled)
413 }
414
415 fn occupied(self) -> Option<u64> {
417 self.completed()?.checked_add(self.active)
418 }
419
420 fn validate(
422 self,
423 metric_id: &str,
424 total: Option<u64>,
425 ) -> Result<(), MetricError> {
426 let occupied =
427 self.occupied().ok_or_else(|| MetricError::CountOverflow {
428 metric_id: metric_id.into(),
429 })?;
430 if let Some(total) = total
431 && occupied > total
432 {
433 return Err(MetricError::TotalExceeded {
434 metric_id: metric_id.into(),
435 total,
436 attempted: occupied,
437 });
438 }
439 Ok(())
440 }
441}
442
443fn apply_delta_to_counts(
445 counts: &mut MetricCounts,
446 delta: MetricDelta,
447 metric_id: &str,
448) -> Result<(), MetricError> {
449 let terminal_delta = delta
450 .unclassified
451 .checked_add(delta.succeeded)
452 .and_then(|value| value.checked_add(delta.failed))
453 .and_then(|value| value.checked_add(delta.cancelled))
454 .ok_or_else(|| MetricError::CountOverflow {
455 metric_id: metric_id.into(),
456 })?;
457 let available_active = counts
458 .active
459 .checked_add(delta.started)
460 .ok_or_else(|| MetricError::CountOverflow {
461 metric_id: metric_id.into(),
462 })?;
463 if terminal_delta > available_active {
464 return Err(MetricError::InsufficientActive {
465 metric_id: metric_id.into(),
466 requested: terminal_delta,
467 available: available_active,
468 });
469 }
470
471 counts.active = available_active - terminal_delta;
472 counts.completed_unclassified = counts
473 .completed_unclassified
474 .checked_add(delta.unclassified)
475 .ok_or_else(|| MetricError::CountOverflow {
476 metric_id: metric_id.into(),
477 })?;
478 counts.succeeded = counts
479 .succeeded
480 .checked_add(delta.succeeded)
481 .ok_or_else(|| MetricError::CountOverflow {
482 metric_id: metric_id.into(),
483 })?;
484 counts.failed =
485 counts.failed.checked_add(delta.failed).ok_or_else(|| {
486 MetricError::CountOverflow {
487 metric_id: metric_id.into(),
488 }
489 })?;
490 counts.cancelled = counts
491 .cancelled
492 .checked_add(delta.cancelled)
493 .ok_or_else(|| MetricError::CountOverflow {
494 metric_id: metric_id.into(),
495 })?;
496 Ok(())
497}
498
499struct MetricGateGuard<'gate> {
501 gate: &'gate CasCell,
502 next_version: u64,
503}
504
505impl<'gate> MetricGateGuard<'gate> {
506 fn new(gate: &'gate CasCell, next_version: u64) -> Self {
507 Self { gate, next_version }
508 }
509}
510
511impl Drop for MetricGateGuard<'_> {
512 fn drop(&mut self) {
513 self.gate.store(self.next_version);
514 }
515}
516
517#[cfg_attr(feature = "serde", derive(serde::Serialize))]
519#[derive(Clone, Debug, Eq, PartialEq)]
520pub struct MetricSnapshot {
521 id: Arc<str>,
523 name: Arc<str>,
525 total: Option<u64>,
527 completed: u64,
529 active: u64,
531 succeeded: u64,
533 failed: u64,
535 cancelled: u64,
537}
538
539impl MetricSnapshot {
540 fn from_counts(metric: &Metric, counts: MetricCounts) -> Self {
542 Self {
543 id: Arc::clone(&metric.id),
544 name: Arc::clone(&metric.name),
545 total: metric.total,
546 completed: counts
547 .completed()
548 .expect("validated metric counts must fit in u64"),
549 active: counts.active,
550 succeeded: counts.succeeded,
551 failed: counts.failed,
552 cancelled: counts.cancelled,
553 }
554 }
555 #[must_use]
557 pub fn id(&self) -> &str {
558 &self.id
559 }
560 #[must_use]
562 pub fn name(&self) -> &str {
563 &self.name
564 }
565 #[must_use]
567 pub const fn total(&self) -> Option<u64> {
568 self.total
569 }
570 #[must_use]
572 pub const fn completed(&self) -> u64 {
573 self.completed
574 }
575 #[must_use]
577 pub const fn unclassified(&self) -> u64 {
578 let classified = self
579 .succeeded
580 .saturating_add(self.failed)
581 .saturating_add(self.cancelled);
582 self.completed.saturating_sub(classified)
583 }
584 #[must_use]
586 pub const fn active(&self) -> u64 {
587 self.active
588 }
589 #[must_use]
591 pub const fn succeeded(&self) -> u64 {
592 self.succeeded
593 }
594 #[must_use]
596 pub const fn failed(&self) -> u64 {
597 self.failed
598 }
599 #[must_use]
601 pub const fn cancelled(&self) -> u64 {
602 self.cancelled
603 }
604 #[must_use]
606 pub fn completion_fraction(&self) -> Option<f64> {
607 self.total
608 .filter(|total| *total > 0)
609 .map(|total| self.completed as f64 / total as f64)
610 }
611}
612
613#[cfg(feature = "serde")]
615#[derive(Deserialize)]
616struct MetricSnapshotWire {
617 id: Arc<str>,
619 name: Arc<str>,
621 total: Option<u64>,
623 completed: u64,
625 active: u64,
627 succeeded: u64,
629 failed: u64,
631 cancelled: u64,
633}
634
635#[cfg(feature = "serde")]
636impl<'de> Deserialize<'de> for MetricSnapshot {
637 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
639 where
640 D: Deserializer<'de>,
641 {
642 let wire = MetricSnapshotWire::deserialize(deserializer)?;
643 let snapshot = Self {
644 id: wire.id,
645 name: wire.name,
646 total: wire.total,
647 completed: wire.completed,
648 active: wire.active,
649 succeeded: wire.succeeded,
650 failed: wire.failed,
651 cancelled: wire.cancelled,
652 };
653 let metric = Metric {
654 id: Arc::clone(&snapshot.id),
655 name: Arc::clone(&snapshot.name),
656 total: snapshot.total,
657 };
658 validate_metrics(std::slice::from_ref(&metric))
659 .map_err(serde::de::Error::custom)?;
660 validate_snapshot_counts(&snapshot)
661 .map_err(serde::de::Error::custom)?;
662 Ok(snapshot)
663 }
664}
665
666#[inline]
668fn wait_for_contention(attempts: usize) {
669 if attempts > 0 && attempts.is_multiple_of(16) {
670 thread::yield_now();
671 } else {
672 spin_loop();
673 }
674}