use std::error::Error;
use std::fmt;
use std::ops::{Add, AddAssign, Sub, SubAssign};
use std::sync::atomic::{AtomicI64, AtomicU32, Ordering};
use std::time::{Duration, SystemTime};
use crate::util::sync_cell::TearableAtomic;
const NANOS_PER_SEC: u32 = 1_000_000_000;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MonotonicTime {
secs: i64,
nanos: u32,
}
impl MonotonicTime {
pub const EPOCH: Self = Self { secs: 0, nanos: 0 };
pub const MIN: Self = Self {
secs: i64::MIN,
nanos: 0,
};
pub const MAX: Self = Self {
secs: i64::MAX,
nanos: NANOS_PER_SEC - 1,
};
pub const fn new(secs: i64, subsec_nanos: u32) -> Self {
assert!(
subsec_nanos < NANOS_PER_SEC,
"invalid number of nanoseconds"
);
Self {
secs,
nanos: subsec_nanos,
}
}
pub fn from_system(leap_secs: i64) -> Result<Self, SystemTimeError> {
let utc_timestamp = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.map_err(|_| SystemTimeError::InvalidSystemTime)?;
Self::new(leap_secs, 0)
.checked_add(utc_timestamp)
.ok_or(SystemTimeError::OutOfRange)
}
pub const fn as_secs(&self) -> i64 {
self.secs
}
pub const fn as_unix_secs(&self, leap_secs: i64) -> i64 {
if let Some(secs) = self.secs.checked_sub(leap_secs) {
secs
} else {
panic!("timestamp outside representable range");
}
}
pub const fn subsec_nanos(&self) -> u32 {
self.nanos
}
pub const fn checked_add(self, rhs: Duration) -> Option<Self> {
let mut secs = self.secs.wrapping_add(rhs.as_secs() as i64);
if secs < self.secs {
return None;
}
let mut nanos = self.nanos + rhs.subsec_nanos();
if nanos >= NANOS_PER_SEC {
secs = if let Some(s) = secs.checked_add(1) {
s
} else {
return None;
};
nanos -= NANOS_PER_SEC;
}
Some(Self { secs, nanos })
}
pub const fn checked_sub(self, rhs: Duration) -> Option<Self> {
let mut secs = self.secs.wrapping_sub(rhs.as_secs() as i64);
if secs > self.secs {
return None;
}
let nanos = if self.nanos < rhs.subsec_nanos() {
secs = if let Some(s) = secs.checked_sub(1) {
s
} else {
return None;
};
(self.nanos + NANOS_PER_SEC) - rhs.subsec_nanos()
} else {
self.nanos - rhs.subsec_nanos()
};
Some(Self { secs, nanos })
}
pub fn duration_since(self, earlier: Self) -> Duration {
self.checked_duration_since(earlier)
.expect("attempt to substract a timestamp from an earlier timestamp")
}
pub const fn checked_duration_since(self, earlier: Self) -> Option<Duration> {
let (secs, nanos) = if earlier.nanos > self.nanos {
if let Some(s) = self.secs.checked_sub(1) {
(s, self.nanos + NANOS_PER_SEC)
} else {
return None;
}
} else {
(self.secs, self.nanos)
};
if secs < earlier.secs {
return None;
}
let delta_secs = secs.wrapping_sub(earlier.secs) as u64;
let delta_nanos = nanos - earlier.nanos;
Some(Duration::new(delta_secs, delta_nanos))
}
}
impl Add<Duration> for MonotonicTime {
type Output = Self;
fn add(self, other: Duration) -> Self {
self.checked_add(other)
.expect("overflow when adding duration to timestamp")
}
}
impl Sub<Duration> for MonotonicTime {
type Output = Self;
fn sub(self, other: Duration) -> Self {
self.checked_sub(other)
.expect("overflow when subtracting duration from timestamp")
}
}
impl AddAssign<Duration> for MonotonicTime {
fn add_assign(&mut self, other: Duration) {
*self = *self + other;
}
}
impl SubAssign<Duration> for MonotonicTime {
fn sub_assign(&mut self, other: Duration) {
*self = *self - other;
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SystemTimeError {
InvalidSystemTime,
OutOfRange,
}
impl fmt::Display for SystemTimeError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidSystemTime => write!(fmt, "invalid system time"),
Self::OutOfRange => write!(fmt, "timestamp outside representable range"),
}
}
}
impl Error for SystemTimeError {}
pub(crate) struct TearableAtomicTime {
secs: AtomicI64,
nanos: AtomicU32,
}
impl TearableAtomicTime {
pub(crate) fn new(time: MonotonicTime) -> Self {
Self {
secs: AtomicI64::new(time.secs),
nanos: AtomicU32::new(time.nanos),
}
}
}
impl TearableAtomic for TearableAtomicTime {
type Value = MonotonicTime;
fn tearable_load(&self) -> MonotonicTime {
MonotonicTime {
secs: self.secs.load(Ordering::Relaxed),
nanos: self.nanos.load(Ordering::Relaxed),
}
}
fn tearable_store(&self, value: MonotonicTime) {
self.secs.store(value.secs, Ordering::Relaxed);
self.nanos.store(value.nanos, Ordering::Relaxed);
}
}
#[cfg(all(test, not(asynchronix_loom)))]
mod tests {
use super::*;
#[test]
fn time_equality() {
let t0 = MonotonicTime::new(123, 123_456_789);
let t1 = MonotonicTime::new(123, 123_456_789);
let t2 = MonotonicTime::new(123, 123_456_790);
let t3 = MonotonicTime::new(124, 123_456_789);
assert_eq!(t0, t1);
assert_ne!(t0, t2);
assert_ne!(t0, t3);
}
#[test]
fn time_ordering() {
let t0 = MonotonicTime::new(0, 1);
let t1 = MonotonicTime::new(1, 0);
assert!(t1 > t0);
}
#[cfg(not(miri))]
#[test]
fn time_from_system_smoke() {
const START_OF_2022: i64 = 1640995200;
const START_OF_2050: i64 = 2524608000;
let now_secs = MonotonicTime::from_system(0).unwrap().as_secs();
assert!(now_secs > START_OF_2022);
assert!(now_secs < START_OF_2050);
}
#[test]
#[should_panic]
fn time_invalid() {
MonotonicTime::new(123, 1_000_000_000);
}
#[test]
fn time_duration_since_smoke() {
let t0 = MonotonicTime::new(100, 100_000_000);
let t1 = MonotonicTime::new(123, 223_456_789);
assert_eq!(
t1.checked_duration_since(t0),
Some(Duration::new(23, 123_456_789))
);
}
#[test]
fn time_duration_with_carry() {
let t0 = MonotonicTime::new(100, 200_000_000);
let t1 = MonotonicTime::new(101, 100_000_000);
assert_eq!(
t1.checked_duration_since(t0),
Some(Duration::new(0, 900_000_000))
);
}
#[test]
fn time_duration_since_extreme() {
const MIN_TIME: MonotonicTime = MonotonicTime::new(i64::MIN, 0);
const MAX_TIME: MonotonicTime = MonotonicTime::new(i64::MAX, NANOS_PER_SEC - 1);
assert_eq!(
MAX_TIME.checked_duration_since(MIN_TIME),
Some(Duration::new(u64::MAX, NANOS_PER_SEC - 1))
);
}
#[test]
fn time_duration_since_invalid() {
let t0 = MonotonicTime::new(100, 0);
let t1 = MonotonicTime::new(99, 0);
assert_eq!(t1.checked_duration_since(t0), None);
}
#[test]
fn time_add_duration_smoke() {
let t = MonotonicTime::new(-100, 100_000_000);
let dt = Duration::new(400, 300_000_000);
assert_eq!(t + dt, MonotonicTime::new(300, 400_000_000));
}
#[test]
fn time_add_duration_with_carry() {
let t = MonotonicTime::new(-100, 900_000_000);
let dt1 = Duration::new(400, 100_000_000);
let dt2 = Duration::new(400, 300_000_000);
assert_eq!(t + dt1, MonotonicTime::new(301, 0));
assert_eq!(t + dt2, MonotonicTime::new(301, 200_000_000));
}
#[test]
fn time_add_duration_extreme() {
let t = MonotonicTime::new(i64::MIN, 0);
let dt = Duration::new(u64::MAX, NANOS_PER_SEC - 1);
assert_eq!(t + dt, MonotonicTime::new(i64::MAX, NANOS_PER_SEC - 1));
}
#[test]
#[should_panic]
fn time_add_duration_overflow() {
let t = MonotonicTime::new(i64::MIN, 1);
let dt = Duration::new(u64::MAX, NANOS_PER_SEC - 1);
let _ = t + dt;
}
#[test]
fn time_sub_duration_smoke() {
let t = MonotonicTime::new(100, 500_000_000);
let dt = Duration::new(400, 300_000_000);
assert_eq!(t - dt, MonotonicTime::new(-300, 200_000_000));
}
#[test]
fn time_sub_duration_with_carry() {
let t = MonotonicTime::new(100, 100_000_000);
let dt1 = Duration::new(400, 100_000_000);
let dt2 = Duration::new(400, 300_000_000);
assert_eq!(t - dt1, MonotonicTime::new(-300, 0));
assert_eq!(t - dt2, MonotonicTime::new(-301, 800_000_000));
}
#[test]
fn time_sub_duration_extreme() {
let t = MonotonicTime::new(i64::MAX, NANOS_PER_SEC - 1);
let dt = Duration::new(u64::MAX, NANOS_PER_SEC - 1);
assert_eq!(t - dt, MonotonicTime::new(i64::MIN, 0));
}
#[test]
#[should_panic]
fn time_sub_duration_overflow() {
let t = MonotonicTime::new(i64::MAX, NANOS_PER_SEC - 2);
let dt = Duration::new(u64::MAX, NANOS_PER_SEC - 1);
let _ = t - dt;
}
}