use crate::ffi::tarantool as ffi;
use std::mem::MaybeUninit;
use std::ops::{Add, AddAssign, Sub, SubAssign};
use std::time::Duration;
pub use crate::clock::INFINITY;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Instant(pub(crate) Duration);
impl Instant {
#[must_use]
#[inline(always)]
#[deprecated = "use `now_fiber` or `now_accurate` instead"]
pub fn now() -> Self {
Self::now_accurate()
}
#[must_use]
#[inline]
pub fn now_accurate() -> Self {
unsafe {
let mut timespec = MaybeUninit::<libc::timespec>::zeroed().assume_init();
if libc::clock_gettime(libc::CLOCK_MONOTONIC, (&mut timespec) as *mut _) != 0 {
let err = std::io::Error::last_os_error();
panic!("failed to get time: {}", err)
}
Self(Duration::new(
timespec.tv_sec as u64,
timespec.tv_nsec as u32,
))
}
}
#[must_use]
#[inline(always)]
pub fn now_fiber() -> Self {
let secs = unsafe { ffi::fiber_clock() };
Self(Duration::from_secs_f64(secs))
}
#[must_use]
#[inline]
pub fn elapsed(&self) -> Duration {
Self::now_accurate().duration_since(*self)
}
#[must_use]
#[inline]
pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
self.0.checked_add(duration).map(Instant)
}
#[must_use]
#[inline]
pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
self.0.checked_sub(duration).map(Instant)
}
#[must_use]
#[inline]
pub fn saturating_add(&self, duration: Duration) -> Instant {
Self(self.0.saturating_add(duration))
}
#[must_use]
#[inline]
pub fn saturating_sub(&self, duration: Duration) -> Instant {
Self(self.0.saturating_sub(duration))
}
#[must_use]
#[inline]
pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
self.0.checked_sub(earlier.0)
}
#[must_use]
#[inline]
pub fn duration_since(&self, earlier: Instant) -> Duration {
self.0.saturating_sub(earlier.0)
}
#[inline(always)]
pub fn as_secs(&self) -> u64 {
self.0.as_secs()
}
#[inline(always)]
pub fn as_secs_f64(&self) -> f64 {
self.0.as_secs_f64()
}
#[inline(always)]
pub fn as_secs_f32(&self) -> f32 {
self.0.as_secs_f32()
}
}
impl Add<Duration> for Instant {
type Output = Instant;
fn add(self, other: Duration) -> Instant {
self.checked_add(other)
.expect("overflow when adding duration to instant")
}
}
impl AddAssign<Duration> for Instant {
fn add_assign(&mut self, other: Duration) {
*self = *self + other;
}
}
impl Sub<Duration> for Instant {
type Output = Instant;
fn sub(self, other: Duration) -> Instant {
self.checked_sub(other)
.expect("overflow when subtracting duration from instant")
}
}
impl SubAssign<Duration> for Instant {
fn sub_assign(&mut self, other: Duration) {
*self = *self - other;
}
}
impl Sub<Instant> for Instant {
type Output = Duration;
fn sub(self, other: Instant) -> Duration {
self.duration_since(other)
}
}
#[cfg(test)]
mod tests {
use super::Instant;
use std::time::Duration;
#[test]
fn addition() {
let now = Instant::now_accurate();
assert_eq!(now.checked_add(Duration::MAX), None);
assert_eq!(now.saturating_add(Duration::MAX), Instant(Duration::MAX));
let plus_second = now.checked_add(Duration::from_secs(1)).unwrap();
assert_eq!(plus_second, now.saturating_add(Duration::from_secs(1)));
assert_eq!(plus_second, now + Duration::from_secs(1));
assert!(plus_second > now);
}
#[test]
fn subtraction() {
let now = Instant::now_accurate();
assert_eq!(now.checked_sub(Duration::MAX), None);
assert_eq!(now.saturating_sub(Duration::MAX), Instant(Duration::ZERO));
let minus_second = now.checked_sub(Duration::from_secs(1)).unwrap();
assert_eq!(minus_second, now.saturating_sub(Duration::from_secs(1)));
assert_eq!(minus_second, now - Duration::from_secs(1));
assert!(minus_second < now);
}
#[test]
fn duration_since() {
let now = Instant::now_accurate();
let plus_second = now + Duration::from_secs(1);
let minus_second = now - Duration::from_secs(1);
assert_eq!(
plus_second.duration_since(minus_second),
Duration::from_secs(2)
);
assert_eq!(
plus_second.checked_duration_since(minus_second),
Some(Duration::from_secs(2))
);
assert_eq!(minus_second.duration_since(plus_second), Duration::ZERO);
assert_eq!(minus_second.checked_duration_since(plus_second), None);
}
}
#[cfg(feature = "internal_test")]
mod test {
use super::*;
#[crate::test(tarantool = "crate")]
fn thread_sleep() {
let now_fiber = Instant::now_fiber();
let t0 = Instant::now_accurate();
std::thread::sleep(Duration::from_millis(100));
assert_eq!(now_fiber, Instant::now_fiber());
assert!(t0 < Instant::now_accurate());
}
}