hopr_transport_protocol/
timer.rs

1use futures::future::{select, Either};
2use futures::pin_mut;
3use futures::FutureExt;
4use std::time::Duration;
5use tracing::{trace, warn};
6
7use hopr_async_runtime::prelude::sleep;
8use hopr_platform::time::native::current_time;
9use hopr_primitive_types::prelude::AsUnixTimestamp;
10
11/// Construct an infinitely running background loop producing ticks with a given period
12/// with the maximum tick duration at most the period.
13pub async fn execute_on_tick<F>(cycle: Duration, action: impl Fn() -> F, operation: String)
14where
15    F: std::future::Future<Output = ()> + Send,
16{
17    loop {
18        let start = current_time().as_unix_timestamp();
19
20        let timeout = sleep(cycle).fuse();
21        let todo = (action)().fuse();
22
23        pin_mut!(timeout, todo);
24
25        match select(timeout, todo).await {
26            Either::Left(_) => warn!(operation, "Timer tick interrupted by timeout"),
27            Either::Right(_) => {
28                trace!(operation, "Timer tick finished");
29
30                let action_duration = current_time().as_unix_timestamp().saturating_sub(start);
31                if let Some(remaining) = cycle.checked_sub(action_duration) {
32                    trace!(
33                        remaining_time_in_ms = remaining.as_millis(),
34                        "Universal timer sleeping for",
35                    );
36                    sleep(remaining).await
37                }
38            }
39        };
40    }
41}