1use std::sync::Arc;
5use std::time::Duration;
6use std::time::Instant;
7
8use parking_lot::RwLock;
9use sketches_ddsketch::DDSketch;
10
11#[derive(Clone, Default)]
16pub struct Timer(Arc<RwLock<DDSketch>>);
17
18impl std::fmt::Debug for Timer {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 f.debug_tuple("Timer").finish_non_exhaustive()
21 }
22}
23
24impl Timer {
25 pub(crate) fn new() -> Self {
26 Self(Default::default())
27 }
28
29 pub fn update(&self, duration: Duration) {
31 self.0.write().add(duration.as_secs_f64());
32 }
33
34 pub fn total(&self) -> Duration {
36 self.0
37 .read()
38 .sum()
39 .map(Duration::from_secs_f64)
40 .unwrap_or_default()
41 }
42
43 #[expect(clippy::expect_used)]
46 pub fn quantile(&self, quantile: f64) -> Option<Duration> {
47 assert!(
48 (0.0..=1.0).contains(&quantile),
49 "quantile must be between 0.0 and 1.0"
50 );
51
52 self.0
53 .read()
54 .quantile(quantile)
55 .expect("quantile range checked")
56 .map(Duration::from_secs_f64)
57 }
58
59 pub fn count(&self) -> usize {
61 self.0.read().count()
62 }
63
64 pub fn is_empty(&self) -> bool {
66 self.0.read().count() == 0
67 }
68
69 pub fn time(&self) -> TimeGuard<'_> {
71 TimeGuard {
72 source: self,
73 start: Instant::now(),
74 }
75 }
76}
77
78pub struct TimeGuard<'a> {
80 source: &'a Timer,
81 start: Instant,
82}
83
84impl Drop for TimeGuard<'_> {
85 fn drop(&mut self) {
86 let elapsed = self.start.elapsed();
87 self.source.update(elapsed);
88 }
89}