//! Utilities to track and compare times and timeouts
//!
//! Contains [`TrackingNow`], and variants.
//!
//! Each one records the current time,
//! and can be used to see if prospective timeouts have expired yet,
//! via the [`PartialOrd`] implementations.
//!
//! Each can be compared with a prospective wakeup time via a `.cmp()` method,
//! and via implementations of [`PartialOrd`] (including via `<` operators etc.)
//!
//! Each tracks every such comparison,
//! and can yield the earliest *unexpired* timeout that was asked about.
//!
//! I.e., the timeout tracker tells you when (in the future)
//! any of the comparisons you have made, might produce different answers.
//! So, that can be used to know how long to sleep for when waiting for timeout(s).
//!
//! This approach means you must be sure to actually perform the timeout action
//! whenever a comparison tells you the relevant period has elapsed.
//! If you fail to do so, the timeout tracker will still disregard the event
//! for the purposes of calculating how to wait, since it is in the past.
//! So if you use the timeout tracker to decide how long to sleep,
//! you won't be woken up until *something else* occurs.
//! (When the timeout has *exactly* elapsed, you should eagerly perform the action.
//! Otherwise the timeout tracker will calculate a zero timeout and you'll spin.)
//!
//! Each tracker has interior mutability,
//! which is necessary because `PartialOrd` (`<=` etc.) only passes immutable references.
//! Most are `Send`, none are `Sync`,
//! so use in thread-safe async code is somewhat restricted.
//! (Recommended use is to do all work influencing timeout calculations synchronously;
//! otherwise, in any case, you risk the time advancing mid-calculations.)
//!
//! `Clone` gives you a *copy*, not a handle onto the same tracker.
//! Comparisons done with the clone do not update the original.
//! (Exception: `TrackingInstantOffsetNow::clone`.)
//!
//! The types are:
//!
//! * [`TrackingNow`]: tracks timeouts based on both [`SystemTime`] and [`Instant`],
//! * [`TrackingSystemTimeNow`]: tracks timeouts based on [`SystemTime`]
//! * [`TrackingInstantNow`]: tracks timeouts based on [`Instant`]
//! * [`TrackingInstantOffsetNow`]: `InstantTrackingNow` but with an offset applied
//!
//! # Advantages, disadvantages, and alternatives
//!
//! Using `TrackingNow` allows time-dependent code to be written
//! in a natural, imperative, style,
//! even if the timeout calculations are complex.
//! Simply test whether it is time yet to do each thing, and if so do it.
//!
//! This can conveniently be combined with an idempotent imperative style
//! for handling non-time-based inputs:
//! for each possible action, you can decide in one place whether it needs doing.
//! (Use a `select_biased!`, with [`wait_for_earliest`](TrackingNow::wait_for_earliest)
//! as one of the branches.)
//!
//! This approach makes it harder to write bugs where
//! some of the consequences of events are forgotten.
//! Each timeout calculation is always done afresh from all its inputs.
//! There is only ever one place where each action is considered,
//! and the consideration is always redone from first principles.
//!
//! However, this is not necessarily the most performant approach.
//! Each iteration of the event loop doesn't know *why* it has woken up,
//! so must simply re-test all of the things that might need to be done.
//!
//! When higher performance is needed, consider maintaining timeouts as state:
//! either as wakeup times or durations, or as actual `Future`s,
//! depending on how frequently they are going to occur,
//! and how much they need to be modified.
//! With this approach you must remember to update or recalculate the timeout,
//! on every change to any of the inputs to each timeout calculation.
//! You must write code to check (or perform) each action,
//! in the handler for each event that might trigger it.
//! Omitting a call is easy,
//! can result in mysterious ordering-dependent "stuckess" bugs,
//! and is often detectable only by very comprehensive testing.
//!
//! # Example
//!
//! ```
//! use std::sync::{Arc, Mutex};
//! use std::time::Duration;
//! use futures::task::SpawnExt as _;
//! use tor_rtcompat::{ToplevelBlockOn as _, SleepProvider as _};
//!
//! # use tor_hsservice::timeout_track_for_doctests_unstable_no_semver_guarantees as timeout_track;
//! # #[cfg(all)] // works like #[cfg(FALSE)]. Instead, we have this workaround ^.
//! use crate::timeout_track;
//! use timeout_track::TrackingInstantNow;
//!
//! // Test harness
//! let runtime = tor_rtmock::MockRuntime::new();
//! let actions = Arc::new(Mutex::new("".to_string())); // initial letters of performed actions
//! let perform_action = {
//! let actions = actions.clone();
//! move |s: &str| actions.lock().unwrap().extend(s.chars().take(1))
//! };
//!
//! runtime.spawn({
//! let runtime = runtime.clone();
//!
//! // Example program which models cooking a stir-fry
//! async move {
//! perform_action("add ingredients");
//! let started = runtime.now();
//! let mut last_stirred = started;
//! loop {
//! let now_track = TrackingInstantNow::now(&runtime);
//!
//! const STIR_EVERY: Duration = Duration::from_secs(25);
//! // In production, we might avoid panics: .. >= last_stirred.checked_add(..)
//! if now_track >= last_stirred + STIR_EVERY {
//! perform_action("stir");
//! last_stirred = now_track.get_now_untracked();
//! continue;
//! }
//!
//! const COOK_FOR: Duration = Duration::from_secs(3 * 60);
//! if now_track >= started + COOK_FOR {
//! break;
//! }
//!
//! now_track.wait_for_earliest(&runtime).await;
//! }
//! perform_action("dish up");
//! }
//! }).unwrap();
//!
//! // Do a test run
//! runtime.block_on(async {
//! runtime.advance_by(Duration::from_secs(1 * 60)).await;
//! assert_eq!(*actions.lock().unwrap(), "ass");
//! runtime.advance_by(Duration::from_secs(2 * 60)).await;
//! assert_eq!(*actions.lock().unwrap(), "asssssssd");
//! });
//! ```
// TODO - eventually we hope this will become pub, in another crate
// Rustdoc complains that we link to these private docs from these docs which are
// themselves only formatted with --document-private-items.
// TODO - Remove when this is actually public
#![allow(rustdoc::private_intra_doc_links)]
use std::cell::Cell;
use std::cmp::Ordering;
use std::time::{Duration, Instant, SystemTime};
use derive_deftly::{define_derive_deftly, Deftly};
use futures::{future, select_biased, FutureExt as _};
use itertools::chain;
use tor_rtcompat::{SleepProvider, SleepProviderExt as _};
//========== derive-deftly macros, which must come first ==========
define_derive_deftly! {
/// Defines methods and types which are common to trackers for `Instant` and `SystemTime`
SingleTimeoutTracker for struct, expect items:
// type of the `now` field, ie the absolute time type
${define NOW $(
${when approx_equal($fname, now)}
$ftype
) }
// type that we track, ie the inner contents of the `Cell