use crate::epoch::Epoch;
use ::std::time::Duration;
pub trait MonotonicClock {
fn epoch(&self) -> Epoch;
fn now(&self) -> Duration;
fn is_ticking(&self) -> bool;
#[inline]
fn time(&self) -> Duration {
self.now() + *self.epoch()
}
#[inline]
fn time_as_float(&self) -> f64 {
let time = self.time();
time.as_secs() as f64 + time.subsec_nanos() as f64 * 1e-9
}
#[inline]
fn as_float(&self) -> f64 {
self.time_as_float()
}
}
#[derive(Debug, Clone)]
pub struct Clock {
inner: ::std::sync::Arc<::std::sync::RwLock<InnerClock>>,
}
unsafe impl Sync for Clock {}
unsafe impl Send for Clock {}
impl Clock {
pub fn new() -> Self {
Self {
inner: ::std::sync::Arc::new(::std::sync::RwLock::new(InnerClock::new())),
}
}
pub fn now(&self) -> Duration {
self.inner.read().unwrap().now()
}
pub fn start(&self) {
self.inner.write().unwrap().start();
}
pub fn stop(&self) -> Option<Duration> {
self.inner.write().unwrap().stop()
}
pub fn reset(&self) {
self.inner.write().unwrap().reset();
}
pub fn resume(&self) -> Option<Duration> {
self.inner.write().unwrap().resume()
}
}
impl MonotonicClock for Clock {
fn epoch(&self) -> Epoch {
self.inner.read().unwrap().epoch()
}
fn now(&self) -> Duration {
self.inner.read().unwrap().now()
}
fn is_ticking(&self) -> bool {
self.inner.read().unwrap().is_ticking()
}
}
impl Default for Clock {
#[inline]
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct InnerClock {
epoch: Epoch, start: ::std::time::Instant,
stop: Option<::std::time::Instant>,
}
impl InnerClock {
#[inline]
pub fn new() -> Self {
Self {
epoch: Epoch::from_unix(),
start: ::std::time::Instant::now(),
stop: None,
}
}
#[inline]
pub fn epoch(&self) -> Epoch {
self.epoch
}
#[inline]
pub fn reset(&mut self) {
self.epoch = Epoch::from_unix();
self.start = ::std::time::Instant::now();
self.stop = None;
}
#[inline]
pub fn start(&mut self) {
self.start = ::std::time::Instant::now();
self.stop = None;
}
#[inline]
pub fn resume(&mut self) -> Option<Duration> {
if let Some(stop) = self.stop {
self.stop = None;
::std::time::Instant::now().checked_duration_since(stop)
} else {
Some(Duration::new(0, 0))
}
}
#[inline]
pub fn stop(&mut self) -> Option<Duration> {
if self.stop.is_none() {
self.stop = Some(::std::time::Instant::now());
}
self.stop.map(|stop| stop - self.start)
}
#[inline]
pub fn now(&self) -> Duration {
if let Some(stop) = self.stop {
stop.duration_since(self.start)
} else {
::std::time::Instant::now().duration_since(self.start)
}
}
#[inline]
pub fn is_ticking(&self) -> bool {
self.stop.is_none()
}
}
impl Default for InnerClock {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl MonotonicClock for InnerClock {
#[inline]
fn epoch(&self) -> Epoch {
self.epoch
}
#[inline]
fn now(&self) -> Duration {
self.now()
}
#[inline]
fn is_ticking(&self) -> bool {
self.is_ticking()
}
}
impl ::std::fmt::Display for InnerClock {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{}", self.time_as_float())
}
}
impl ::std::convert::From<InnerClock> for Duration {
fn from(mc: InnerClock) -> Self {
mc.time()
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert2::assert;
#[test]
fn test_monotonic_clock() {
let clock = Clock::new();
assert!(clock.now() < Duration::from_secs(1));
::std::thread::sleep(Duration::from_secs(1));
assert!(clock.now() > Duration::from_secs(1));
::std::thread::sleep(Duration::from_secs(2));
let stopped_at = clock.stop().unwrap();
assert!(clock.now() > Duration::from_secs(2));
assert!(clock.now() == stopped_at);
::std::thread::sleep(Duration::from_secs(2));
assert!(clock.now() > Duration::from_secs(2));
assert!(clock.now() == stopped_at);
clock.resume();
::std::thread::sleep(Duration::from_secs(1));
assert!(clock.now() > stopped_at);
clock.reset();
assert!(clock.now() < Duration::from_secs(1));
}
#[test]
fn test_monotonic_clock_since_unix_epoch() {
let clock = Clock::new();
eprintln!("clock.epoch = {:?}", clock.epoch());
eprintln!("clock.now() = {:?}", clock.time());
}
}