arty/documentation/time.rs
1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Clocks, delays, and tests that do not have to wait for real time.
5//!
6//! Arty re-exports [`Clock`](crate::time::Clock) and, with `test-util`,
7//! [`ClockControl`](crate::time::ClockControl) through `arty::time`. Use
8//! [`tick`] directly for delay and timeout futures, extension traits,
9//! stopwatches, and periodic timers. The `time` feature works without an Arty
10//! runtime; `rt` also enables time and drives timers for its workers.
11//!
12//! # Use the task's clock
13//!
14//! Each task gets a clock through [`Builtins::clock`](crate::task::Builtins::clock).
15//! Use a stopwatch for elapsed time and `system_time()` for a wall-clock
16//! timestamp, which can change independently of elapsed time.
17//!
18//! ```
19//! use std::future::pending;
20//! use std::time::Duration;
21//!
22//! use arty::task::Builtins;
23//! use tick::FutureExt;
24//!
25//! # #[arty::main]
26//! # async fn main(cx: Builtins) {
27//! let watch = cx.clock().stopwatch();
28//! cx.clock().delay(Duration::from_millis(1)).await;
29//! assert!(watch.elapsed() >= Duration::from_millis(1));
30//!
31//! let result = pending::<()>()
32//! .timeout(cx.clock(), Duration::from_millis(1))
33//! .await;
34//! assert!(result.is_err());
35//! # }
36//! ```
37//!
38//! A busy worker may resume a task later than the requested delay. A timeout
39//! limits how long you wait for a result; it does not cancel a spawned task.
40//!
41//! # Control time in tests
42//!
43//! Enable `test-util` in an Arty dev-dependency. A second
44//! [`ClockControl`](crate::time::ClockControl) parameter to
45//! [`arty::test`](crate::test) gives the test control over its runtime's clocks.
46//! Time starts at the UNIX epoch and does not advance automatically.
47//!
48//! A delay starts its timer when first polled. Poll it before advancing time:
49//!
50//! ```test_harness
51//! use std::future::{Future, poll_fn};
52//! use std::pin::pin;
53//! use std::task::Poll;
54//! use std::time::Duration;
55//!
56//! use arty::task::Builtins;
57//! use arty::time::ClockControl;
58//!
59//! #[arty::test]
60//! async fn controlled_delay(cx: Builtins, control: ClockControl) {
61//! let watch = cx.clock().stopwatch();
62//! let mut delay = pin!(cx.clock().delay(Duration::from_secs(30)));
63//! poll_fn(|context| {
64//! assert!(delay.as_mut().poll(context).is_pending());
65//! Poll::Ready(())
66//! })
67//! .await;
68//! control.advance(Duration::from_secs(30));
69//! assert_eq!(watch.elapsed(), Duration::from_secs(30));
70//! delay.await;
71//! }
72//! ```
73//!
74//! `auto_advance_timers(true)` is convenient for one delay after another, but
75//! it advances eagerly and does not guarantee concurrent timers finish in order.
76//! Advance time explicitly when ordering matters. `auto_advance(duration)`
77//! instead advances time on reads.
78//!
79//! `ClockControl` can be combined with `workers`. For a custom runtime builder,
80//! create the control in a synchronous test and pass a clone to
81//! [`RuntimeBuilder::clock`](crate::runtime::RuntimeBuilder::clock).
82//!
83//! # Without an Arty runtime
84//!
85//! Enabling `time` alone does not start a timer driver.
86//! The underlying `tick::SimpleClock` reads time without a driver, but delays
87//! on [`Clock`](crate::time::Clock) need a driver or controlled time. See
88//! [`tick`] for using these clocks outside Arty.