blob: 3f8963958d8596e3f8b36ec59bb1dea55acb5a30 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! The `omaha_client::time` module provides a set of types and traits to allow for the expressing
//! of time using both a wall-time clock, and a monotonic clock.
//!
//! The motivation for this is that wall-time is subject to being artibtrarily moved (forwards or
//! backwards as the system's timekeeper decides is necessary), to keep it in sync with external
//! systems.
//!
//! The monotonic clock, on the other hand, provides a consistently moving forward notion of time,
//! but one that is not anchored at any particular external point in time. It's epoch is therefore
//! somewhat opaque by nature. Rust's std::time::Instant takes this to the extreme of making the
//! underlying value completely hidden from callers.
//!
//! One aspect of the `TimeSource` trait and the `ComplexTime` type is that they provide a way to
//! pair the two timelines, and construct a time that is given in terms of each of these dimensions.
//!
//! What it doesn't try to do, is so that these pairings are the _same_ time. They can be
//! observations made concurrently (as in the case of `TimeSource::now() -> ComplexTime`),
//! or they can be bounds for when an event in the future can happen:
//!
//! ```
//! let past_event_wall_time: SystemTime = read_some_system_time();
//! let duration_to_next = Duration::from_secs(1*60*60); // one hour
//! let rough_next_event_time = ComplexTime{
//! wall: past_event_wall_time + duration_to_next,
//! mono: TimeSource::now_in_monotonic() + duration_to_next
//! };
//! timer.wait_until_either(rough_next_event_time).await;
//! ```
//!
//! The above setups up a `ComplexTime` as a bound that based on an expected wall time, and
//! monotonic time relative to `now`, such that the event can be described as "at time X, or within
//! an hour" when used with `Timer::wait_until`, or "after time X, at least an hour from now", if
//! used with `Timer::wait_until_all`.
//!
//! # Usage Guidelines
//!
//! The `ComplexTime` and `PartialComplexTime` structs give the ability to represent a number of
//! states of knowledge about a time.
//!
//! When modeling the known time for something:
//!
//! * `ComplexTime` - When both wall and monotonic times are always known
//! * `PartialComplexTime` - When some time (wall, monotonic, or both) is always known
//! * `Option<ComplexTime> - When time is either known for both timelines, or not at all.
//! * `Option<PartialComplexTime> - Situations where time can be in any of 4 states:
//! * None whatsoever
//! * Only wall time
//! * Only monotonic time
//! * Both are known
//!
//! When modeling the time required (e.g. timer waits):
//! * `ComplexTime` - When both wall and monotonic times are always required
//! * `PartialComplexTime` - When some time (wall, monotonic, or both) is always required, but any
//! or both will suffice.
//!
use chrono::{DateTime, Utc};
use futures::future::BoxFuture;
use std::{
fmt::{Debug, Display},
hash::Hash,
time::{Duration, Instant, SystemTime},
};
// NOTE: Implementations for the main types of this module have been moved to inner modules that
// that are exposed via `pub use`, so that it's easier to read through the declarations of
// the main types and see how they are meant to be used together. Implementations are in the
// same order as the declarations of the types.
// Implementations and tests for `ComplexTime` and `PartialComplexTime`
mod complex;
pub use complex::system_time_conversion;
/// This is a complete `ComplexTime`, which has values on both the wall clock timeline and the
/// monotonic clock timeline.
///
/// It is not necessarily intended that both values refer to the same moment. They can, or they
/// can refer to a time on each clock's respective timeline, e.g. for use with the `Timer` trait.
///
/// The `ComplexTime` type implements all the standard math operations in `std::ops` that are
/// implemented for both `std::time::SystemTime` and `std::time::Instant`. Like those
/// implementations, it will panic on overflow.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ComplexTime {
pub wall: SystemTime,
pub mono: Instant,
}
impl ComplexTime {
/// Truncate the submicrosecond part of the walltime.
pub fn truncate_submicrosecond_walltime(&self) -> ComplexTime {
let nanos_in_one_micro = Duration::from_micros(1).as_nanos();
let submicrosecond_nanos = match self.wall_duration_since(SystemTime::UNIX_EPOCH) {
Ok(duration) => duration.as_nanos() % nanos_in_one_micro,
Err(e) => nanos_in_one_micro - e.duration().as_nanos() % nanos_in_one_micro,
};
ComplexTime {
wall: self.wall - Duration::from_nanos(submicrosecond_nanos as u64),
mono: self.mono,
}
}
/// Compute the Duration since the given SystemTime, for the SystemTime component of this
/// ComplexTime. If this ComplexTime's SystemTime is before the given time, the Duration
/// is returned as Err (same as `SystemTime::duration_since()`)
pub fn wall_duration_since(
&self,
earlier: impl Into<SystemTime>,
) -> Result<Duration, std::time::SystemTimeError> {
self.wall.duration_since(earlier.into())
}
/// Returns `true` if this ComplexTime is after either the SystemTime or the Instant of the
/// given PartialComplexTime.
pub fn is_after_or_eq_any(&self, other: impl Into<PartialComplexTime>) -> bool {
match other.into() {
PartialComplexTime::Wall(w) => (self.wall >= w),
PartialComplexTime::Monotonic(m) => (self.mono >= m),
PartialComplexTime::Complex(c) => (self.wall >= c.wall || self.mono >= c.mono),
}
}
}
// Implementations for `Display`, `Add`, `AddAssign`, `Sub`, `SubAssign` are found in
// `mod complex::complex_time_impls`
//
// Implementations for `From<>` are found in `mod complex::complex_time_type_conversions`
/// `PartialComplexTime` provides a `std::interator::EitherOrBoth`-like type which is specifically
/// for holding either one, or both, of the time types that make up a `ComplexTime`. It's a type
/// that holds a value for at least one of the timelines.
///
/// The important differentiation of this vs. a struct such as:
/// ```
/// struct MaybeBoth {
/// wall: Option<SystemTime>,
/// monotonic: Option<SystemTime>
///}
/// ```
/// is that there is no valid `(None, None)` state for this type to be in, and so code that uses can
/// be certain that _some_ time is specified.
///
/// Like `ComplexTime`, `PartialComplexTime` implements all the standard math operations in
/// `std::ops` that are implemented for both `std::time::SystemTime` and `std::time:Instant`. Like
/// those implementations, they will panic on overflow.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum PartialComplexTime {
/// Just a wall time.
Wall(SystemTime),
/// Just a monotonic time.
Monotonic(Instant),
/// Both a wall and a monotonic time.
Complex(ComplexTime),
}
impl PartialComplexTime {
/// Return the SystemTime component, if one exists.
pub fn checked_to_system_time(self) -> Option<SystemTime> {
self.destructure().0
}
/// Return the Instant component, if one exists.
pub fn checked_to_instant(self) -> Option<Instant> {
self.destructure().1
}
/// Convert the SystemTime component of this PartialComplexTime into i64 microseconds from
/// the UNIX Epoch. Provides coverage over +/- approx 30,000 years from 1970-01-01 UTC.
///
/// Returns None if it doesn't have a wall time or on overflow (instead of panicking)
pub fn checked_to_micros_since_epoch(self) -> Option<i64> {
self.checked_to_system_time()
.and_then(system_time_conversion::checked_system_time_to_micros_from_epoch)
}
/// Return the PartialComplexTime::Wall that represents the same time as the specified
/// microseconds from the UNIX Epoch (1970-01-01 UTC)
pub fn from_micros_since_epoch(micros: i64) -> Self {
PartialComplexTime::from(system_time_conversion::micros_from_epoch_to_system_time(micros))
}
/// Return a new ComplexTime that's based on the time values of this PartialComplexTime,
/// setting either unknown field from the passed-in ComplexTime.
pub fn complete_with(&self, complex: ComplexTime) -> ComplexTime {
let (system, instant) = self.destructure();
ComplexTime::from((system.unwrap_or(complex.wall), instant.unwrap_or(complex.mono)))
}
/// Destructure the PartialComplexTime into it's two components, each as an Option.
pub fn destructure(&self) -> (Option<SystemTime>, Option<Instant>) {
match *self {
PartialComplexTime::Wall(w) => (Some(w), None),
PartialComplexTime::Monotonic(m) => (None, Some(m)),
PartialComplexTime::Complex(c) => (Some(c.wall), Some(c.mono)),
}
}
}
// Implementations for `Display`, `Add`, `AddAssign`, `Sub`, `SubAssign` are found in
// `mod complex::partial_complex_time_impls`
//
// Implementations for `From<>` are found in `mod complex::partial_complex_time_type_conversions`
/// Trait for timers that understand how to work with the `ComplexTime` and `PartialComplexTime`
/// types.
///
/// When using a `PartialComplexTime`, the trait defines Fns for waiting until any of the times have
/// been reached, or until all of them have been reached.
pub trait Timer {
/// Wait until at least one of the given time bounds has been reached.
fn wait_until(&mut self, time: impl Into<PartialComplexTime>) -> BoxFuture<'static, ()>;
/// Wait for the given duration (from now).
fn wait_for(&mut self, duration: Duration) -> BoxFuture<'static, ()>;
}
// Implementations and tests for test `Timers`. Not marked as #[cfg(test)] to allow use in tests
// in crates that use this.
pub mod timers;
/// `TimeSource` is a trait for providing access to both the "System" (aka "Wall") time for
/// platform, as well as its monotonic time.
pub trait TimeSource {
/// Returns the current wall time.
fn now_in_walltime(&self) -> SystemTime;
/// Returns the current montonic time.
fn now_in_monotonic(&self) -> Instant;
/// Returns the current ComplexTime (both wall and monotonic times).
fn now(&self) -> ComplexTime;
}
// Implementations and tests for `TimeSource`
pub mod time_source;
pub use time_source::MockTimeSource;
pub use time_source::StandardTimeSource;
impl<T> TimeSource for &T
where
T: TimeSource,
{
fn now_in_walltime(&self) -> SystemTime {
(*self).now_in_walltime()
}
fn now_in_monotonic(&self) -> Instant {
(*self).now_in_monotonic()
}
fn now(&self) -> ComplexTime {
(*self).now()
}
}
/// Helper struct for providing a consistent, readable `SystemTime`.
///
/// This displays a `SystemTime` in a human-readable date+time in UTC plus the raw `[seconds].[ns]`
/// since epoch of the SystemTime.
///
/// # Example
/// ```
/// let sys_time = SystemTime::UNIX_EPOCH + Duration::from_nanos(994610096026420000);
///
/// assert_eq!(
/// format!("{}", ReadableSystemTime(sys_time)),
/// "2001-07-08 16:34:56.026 UTC (994610096.026420000)"
/// );
/// ```
pub struct ReadableSystemTime(pub SystemTime);
impl Display for ReadableSystemTime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let format = DateTime::<Utc>::from(self.0).format("%Y-%m-%d %H:%M:%S%.3f %Z (%s%.9f)");
Display::fmt(&format, f)
}
}
impl Debug for ReadableSystemTime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
#[cfg(test)]
mod tests_for_readable_system_time {
use super::*;
#[test]
fn test_readable_system_time() {
let sys_time = SystemTime::UNIX_EPOCH + Duration::from_nanos(994610096026420000);
assert_eq!(
format!("{}", ReadableSystemTime(sys_time)),
"2001-07-08 16:34:56.026 UTC (994610096.026420000)"
);
}
}