blob: f63481ee9d669931a0770160127ea6891bee594b [file] [log] [blame]
John Park33858a32018-09-28 23:05:48 -07001#ifndef AOS_UTIL_PHASED_LOOP_H_
2#define AOS_UTIL_PHASED_LOOP_H_
brians343bc112013-02-10 01:53:46 +00003
Stephan Pleinesb1177672024-05-27 17:48:32 -07004#include <chrono>
James Kuszmaul20dcc7c2023-01-20 11:06:31 -08005#include <optional>
6
John Park33858a32018-09-28 23:05:48 -07007#include "aos/time/time.h"
Brian Silvermandcaa3f72015-11-29 05:32:08 +00008
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -08009namespace aos::time {
brians343bc112013-02-10 01:53:46 +000010
Brian Silvermandcaa3f72015-11-29 05:32:08 +000011// Handles sleeping until a fixed offset from some time interval.
12class PhasedLoop {
13 public:
14 // For example, with interval = 1s and offset = 0.1s this will fire at:
15 // 0.1s
16 // 1.1s
17 // ...
18 // 10000.1s
Austin Schuhf2a50ba2016-12-24 16:16:26 -080019 // offset must be >= chrono::seconds(0) and < interval.
Austin Schuh8aec1ed2016-05-01 13:29:20 -070020 PhasedLoop(
21 const monotonic_clock::duration interval,
Austin Schuhd32b3622019-06-23 18:49:06 -070022 const monotonic_clock::time_point monotonic_now,
Austin Schuhf257f3c2019-10-27 21:00:43 -070023 const monotonic_clock::duration offset = monotonic_clock::duration(0));
Brian Silvermandcaa3f72015-11-29 05:32:08 +000024
Austin Schuh5d4b0982017-04-08 14:36:08 -070025 // Updates the offset and interval.
James Kuszmaul20dcc7c2023-01-20 11:06:31 -080026 //
27 // After a call to set_interval_and_offset with monotonic_now = nullopt, the
28 // following will hold, for any allowed values of interval and offset:
29 // auto original_time = loop.sleep_time();
30 // loop.set_interval_and_offset(interval, offset);
31 // CHECK_LE(loop.sleep_time(), original_time);
32 // CHECK_EQ(0, loop.Iterate(original_time));
33 //
34 // Note that this will not be the behavior that all (or even necessarily most)
35 // users want, since it doesn't necessarily preserve a "keep the iteration
36 // time as consistent as possible" concept. However, it *is* better defined
37 // than the alternative, where if you decrease the offset by, e.g., 1ms on a
38 // 100ms interval, then the behavior will vary depending on whather you are
39 // going from 0ms->999ms offset or from 1ms->0ms offset.
40 //
41 // If monotonic_now is set, then the following will hold:
42 // auto original_time = loop.sleep_time();
43 // loop.set_interval_and_offset(interval, offset, monotonic_now);
44 // CHECK_LE(loop.sleep_time(), monotonic_now);
45 // CHECK_EQ(0, loop.Iterate(monotonic_now));
46 void set_interval_and_offset(
47 const monotonic_clock::duration interval,
48 const monotonic_clock::duration offset,
49 std::optional<monotonic_clock::time_point> monotonic_now = std::nullopt);
Austin Schuh5d4b0982017-04-08 14:36:08 -070050
51 // Computes the offset given an interval and a time that we should trigger.
52 static monotonic_clock::duration OffsetFromIntervalAndTime(
53 const monotonic_clock::duration interval,
Austin Schuhf257f3c2019-10-27 21:00:43 -070054 const monotonic_clock::time_point monotonic_trigger);
Austin Schuh5d4b0982017-04-08 14:36:08 -070055
Brian Silvermandcaa3f72015-11-29 05:32:08 +000056 // Resets the count of skipped iterations.
Austin Schuh8aec1ed2016-05-01 13:29:20 -070057 // Iterate(monotonic_now) will return 1 and set sleep_time() to something
58 // within interval of monotonic_now.
Austin Schuhd32b3622019-06-23 18:49:06 -070059 void Reset(const monotonic_clock::time_point monotonic_now) {
Austin Schuh8aec1ed2016-05-01 13:29:20 -070060 Iterate(monotonic_now - interval_);
61 }
Brian Silvermandcaa3f72015-11-29 05:32:08 +000062
Austin Schuh8aec1ed2016-05-01 13:29:20 -070063 // Calculates the next time to run after monotonic_now.
Brian Silvermandcaa3f72015-11-29 05:32:08 +000064 // The result can be retrieved with sleep_time().
65 // Returns the number of iterations which have passed (1 if this is called
Austin Schuh8aec1ed2016-05-01 13:29:20 -070066 // often enough). This can be < 1 iff monotonic_now goes backwards between
67 // calls.
Austin Schuh60e77942022-05-16 17:48:24 -070068 int Iterate(
69 const monotonic_clock::time_point monotonic_now = monotonic_clock::now());
Brian Silvermandcaa3f72015-11-29 05:32:08 +000070
71 // Sleeps until the next time and returns the number of iterations which have
72 // passed.
73 int SleepUntilNext() {
Austin Schuh8aec1ed2016-05-01 13:29:20 -070074 const int r = Iterate(monotonic_clock::now());
75 ::std::this_thread::sleep_until(sleep_time());
Brian Silvermandcaa3f72015-11-29 05:32:08 +000076 return r;
77 }
78
Austin Schuh8aec1ed2016-05-01 13:29:20 -070079 monotonic_clock::time_point sleep_time() const { return last_time_; }
Brian Silvermandcaa3f72015-11-29 05:32:08 +000080
Austin Schuhde8a8ff2019-11-30 15:25:36 -080081 monotonic_clock::duration interval() const { return interval_; }
82 monotonic_clock::duration offset() const { return offset_; }
83
Brian Silvermandcaa3f72015-11-29 05:32:08 +000084 private:
Austin Schuh5d4b0982017-04-08 14:36:08 -070085 monotonic_clock::duration interval_, offset_;
Brian Silvermandcaa3f72015-11-29 05:32:08 +000086
87 // The time we most recently slept until.
Austin Schuh8aec1ed2016-05-01 13:29:20 -070088 monotonic_clock::time_point last_time_ = monotonic_clock::epoch();
Brian Silvermandcaa3f72015-11-29 05:32:08 +000089};
90
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -080091} // namespace aos::time
brians343bc112013-02-10 01:53:46 +000092
John Park33858a32018-09-28 23:05:48 -070093#endif // AOS_UTIL_PHASED_LOOP_H_