tick/runtime/mod.rs
1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Infrastructure for integrating time primitives into async runtimes.
5//!
6//! This module provides the necessary components to bridge time-based operations
7//! with async runtime execution. The primary workflow involves:
8//!
9//! 1. Start with an [`InactiveClock`] that can be safely moved across threads
10//! 2. Activate it using [`InactiveClock::activate`] to get a [`Clock`] and [`ClockDriver`]
11//! 3. Use the [`ClockDriver`] to periodically advance timers in your runtime loop
12//! 4. Use the [`Clock`] for time operations like creating timers and measuring time
13//!
14//! # Integration with Runtimes
15//!
16//! Different runtime architectures can integrate this module as follows:
17//!
18//! ## Thread-per-core Runtimes
19//!
20//! In thread-per-core architectures, each thread should own an isolated clock with its own
21//! timer storage. This eliminates cross-thread lock contention and scales linearly.
22//!
23//! The pattern is to clone the [`InactiveClock`], relocate each clone to its target thread
24//! using [`ThreadAware::relocate`], and then activate:
25//!
26//! ```rust
27//! # use thread_aware::ThreadAware;
28//! # use thread_aware::affinity::pinned_affinities;
29//! # use tick::runtime::InactiveClock;
30//! # let affinities = pinned_affinities(&[1, 1]);
31//! let root = InactiveClock::default();
32//!
33//! // Clone and relocate to each thread's affinity
34//! let mut inactive_1 = root.clone();
35//! inactive_1.relocate(Some(affinities[0]), affinities[0]);
36//! let mut inactive_2 = root;
37//! inactive_2.relocate(Some(affinities[1]), affinities[1]);
38//!
39//! // On thread 1: activate and drive timers independently
40//! let (clock_1, driver_1) = inactive_1.activate();
41//!
42//! // On thread 2: activate and drive timers independently
43//! let (clock_2, driver_2) = inactive_2.activate();
44//! ```
45//!
46//! After relocation, each thread's clock and driver operate on an independent set of timers.
47//! Timers registered on `clock_1` are only visible to `driver_1`, and the other way around. Each driver
48//! must be advanced independently by its owning thread.
49//!
50//! ## Multi-threaded Runtimes
51//!
52//! In multi-threaded runtimes where tasks may run on any thread, activate once and share the
53//! clock across threads. The driver should be kept on a dedicated thread or task for timer
54//! advancement:
55//!
56//! ```rust
57//! # use tick::runtime::InactiveClock;
58//! let (clock, driver) = InactiveClock::default().activate();
59//!
60//! // Share `clock` across threads (it is Clone + Send + Sync)
61//! // Keep `driver` on a single thread to advance timers
62//! ```
63//!
64//! [`Clock`]: crate::Clock
65//! [`InactiveClock::activate`]: InactiveClock::activate
66//! [`ThreadAware::relocate`]: thread_aware::ThreadAware::relocate
67
68mod clock_driver;
69mod clock_gone;
70mod inactive_clock;
71
72pub use clock_driver::ClockDriver;
73pub use clock_gone::ClockGone;
74pub use inactive_clock::{InactiveClock, Isolated, Shared};