1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// Copyright 2019 Conflux Foundation. All rights reserved.
// Conflux is free software and distributed under GNU General Public License.
// See http://www.gnu.org/licenses/

use crate::{
    histogram::{Histogram, Sample},
    meter::{register_meter_with_group, Meter},
    metrics::is_enabled,
};
use std::{
    sync::Arc,
    time::{Duration, Instant},
};

pub trait Timer: Send + Sync {
    fn update(&self, _d: Duration) {}

    fn update_since(&self, start_time: Instant) {
        self.update(Instant::now().saturating_duration_since(start_time));
    }
}

fn register_timer_exp_decay(
    group: &str, counter_name: &str, time_name: &str,
) -> Arc<dyn Timer> {
    if !is_enabled() {
        Arc::new(NoopTimer)
    } else {
        Arc::new(StandardTimer {
            meter: register_meter_with_group(group, counter_name),
            histogram: Sample::ExpDecay(0.015)
                .register_with_group(group, time_name, 1024),
        })
    }
}

pub fn register_timer(name: &str) -> Arc<dyn Timer> {
    register_timer_exp_decay(name, "counter", "time_expdec")
}

pub fn register_timer_with_group(group: &str, name: &str) -> Arc<dyn Timer> {
    let counter_name = format!("{}_counter", name);
    let time_name = format!("{}_time_expdec", name);
    register_timer_exp_decay(group, counter_name.as_str(), time_name.as_str())
}

struct NoopTimer;
impl Timer for NoopTimer {}

struct StandardTimer {
    meter: Arc<dyn Meter>,
    histogram: Arc<dyn Histogram>,
}

impl Timer for StandardTimer {
    fn update(&self, d: Duration) {
        self.meter.mark(1);
        self.histogram.update(d.as_nanos() as u64);
    }
}

pub struct ScopeTimer {
    timer: Arc<dyn Timer>,
    start: Instant,
}

impl ScopeTimer {
    /// Call this to measure the time to run to the end of the current scope.
    /// It will update the time from the function called till the returned
    /// instance is dropped to `timer`.
    pub fn time_scope(timer: Arc<dyn Timer>) -> Self {
        Self {
            timer,
            start: Instant::now(),
        }
    }
}

impl Drop for ScopeTimer {
    fn drop(&mut self) { self.timer.update_since(self.start) }
}