1use hdrhistogram::errors::CreationError;
37use hdrhistogram::Counter;
38use hdrhistogram::Histogram;
39
40use tokio::task::AbortHandle;
41
42use std::cell::Cell;
43use std::cell::UnsafeCell;
44
45#[derive(Debug)]
47pub enum Error {
48 CreationError(CreationError),
49}
50
51impl std::fmt::Display for Error {
52 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
53 match self {
54 Error::CreationError(e) => write!(f, "CreationError: {}", e),
55 }
56 }
57}
58
59impl std::error::Error for Error {}
60
61impl From<CreationError> for Error {
62 fn from(e: CreationError) -> Self {
63 Error::CreationError(e)
64 }
65}
66
67pub type Result<T> = std::result::Result<T, Error>;
68
69pub struct EldHistogram<C: Counter> {
76 ht: UnsafeCell<Histogram<C>>,
77
78 fut: Cell<Option<AbortHandle>>,
79
80 resolution: usize,
81}
82
83impl<C: Counter> std::ops::Deref for EldHistogram<C> {
84 type Target = Histogram<C>;
85
86 fn deref(&self) -> &Self::Target {
87 unsafe { &*self.ht.get() }
88 }
89}
90
91impl<C: Counter> std::ops::DerefMut for EldHistogram<C> {
92 fn deref_mut(&mut self) -> &mut Self::Target {
93 unsafe { &mut *self.ht.get() }
94 }
95}
96
97impl<C: Counter + Send + 'static> EldHistogram<C> {
98 pub fn new(resolution: usize) -> Result<Self> {
101 let ht = Histogram::<C>::new(5)?;
102
103 Ok(Self {
104 ht: UnsafeCell::new(ht),
105 fut: Cell::new(None),
106 resolution,
107 })
108 }
109
110 pub fn start(&self) {
115 let r = self.resolution as u64;
116
117 let ht = unsafe { &mut *self.ht.get() };
118 let fut = tokio::spawn(async move {
119 let mut interval =
120 tokio::time::interval(tokio::time::Duration::from_millis(r));
121 loop {
122 interval.tick().await;
123
124 let clock = tokio::time::Instant::now();
125
126 tokio::task::yield_now().await;
127 let _ = ht.record(clock.elapsed().as_nanos() as u64);
128 }
129 });
130
131 self.fut.set(Some(fut.abort_handle()));
132 }
133
134 pub fn stop(&self) {
136 if let Some(fut) = self.fut.take() {
137 fut.abort();
138 }
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[tokio::test]
147 async fn test_eld() {
148 let mut h = EldHistogram::<u64>::new(20).unwrap();
149 h.start();
150 tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
151 h.stop();
152
153 assert!(h.min() > 0);
154 }
155}