John Park | 33858a3 | 2018-09-28 23:05:48 -0700 | [diff] [blame] | 1 | #include "aos/util/phased_loop.h" |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 2 | |
Stephan Pleines | b117767 | 2024-05-27 17:48:32 -0700 | [diff] [blame] | 3 | #include <compare> |
| 4 | #include <ratio> |
| 5 | |
Austin Schuh | 99f7c6a | 2024-06-25 22:07:44 -0700 | [diff] [blame^] | 6 | #include "absl/log/check.h" |
| 7 | #include "absl/log/log.h" |
Austin Schuh | f257f3c | 2019-10-27 21:00:43 -0700 | [diff] [blame] | 8 | |
Stephan Pleines | f63bde8 | 2024-01-13 15:59:33 -0800 | [diff] [blame] | 9 | namespace aos::time { |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 10 | |
Austin Schuh | f257f3c | 2019-10-27 21:00:43 -0700 | [diff] [blame] | 11 | PhasedLoop::PhasedLoop(const monotonic_clock::duration interval, |
| 12 | const monotonic_clock::time_point monotonic_now, |
| 13 | const monotonic_clock::duration offset) |
| 14 | : interval_(interval), offset_(offset), last_time_(offset) { |
| 15 | CHECK(offset >= monotonic_clock::duration(0)); |
| 16 | CHECK(interval > monotonic_clock::duration(0)); |
| 17 | CHECK(offset < interval); |
| 18 | Reset(monotonic_now); |
| 19 | } |
| 20 | |
| 21 | void PhasedLoop::set_interval_and_offset( |
| 22 | const monotonic_clock::duration interval, |
James Kuszmaul | 20dcc7c | 2023-01-20 11:06:31 -0800 | [diff] [blame] | 23 | const monotonic_clock::duration offset, |
| 24 | std::optional<monotonic_clock::time_point> monotonic_now) { |
Milind Upadhyay | 42589bb | 2021-05-19 20:05:16 -0700 | [diff] [blame] | 25 | // Update last_time_ to the new offset so that we have an even interval |
James Kuszmaul | 20dcc7c | 2023-01-20 11:06:31 -0800 | [diff] [blame] | 26 | // In doing so, set things so that last_time_ will only ever decrease on calls |
| 27 | // to set_interval_and_offset. |
| 28 | last_time_ += offset - offset_ - |
| 29 | (offset > offset_ ? interval : monotonic_clock::duration(0)); |
Milind Upadhyay | 42589bb | 2021-05-19 20:05:16 -0700 | [diff] [blame] | 30 | |
Austin Schuh | f257f3c | 2019-10-27 21:00:43 -0700 | [diff] [blame] | 31 | interval_ = interval; |
| 32 | offset_ = offset; |
| 33 | CHECK(offset_ >= monotonic_clock::duration(0)); |
| 34 | CHECK(interval_ > monotonic_clock::duration(0)); |
| 35 | CHECK(offset_ < interval_); |
James Kuszmaul | 20dcc7c | 2023-01-20 11:06:31 -0800 | [diff] [blame] | 36 | // Reset effectively clears the skipped iteration count and ensures that the |
| 37 | // last time is in the interval (monotonic_now - interval, monotonic_now], |
| 38 | // which means that a call to Iterate(monotonic_now) will return 1 and set a |
| 39 | // wakeup time after monotonic_now. |
| 40 | if (monotonic_now.has_value()) { |
| 41 | Iterate(monotonic_now.value()); |
| 42 | } |
Austin Schuh | f257f3c | 2019-10-27 21:00:43 -0700 | [diff] [blame] | 43 | } |
| 44 | |
| 45 | monotonic_clock::duration PhasedLoop::OffsetFromIntervalAndTime( |
| 46 | const monotonic_clock::duration interval, |
| 47 | const monotonic_clock::time_point monotonic_trigger) { |
| 48 | CHECK(interval > monotonic_clock::duration(0)); |
| 49 | return monotonic_trigger.time_since_epoch() - |
| 50 | (monotonic_trigger.time_since_epoch() / interval) * interval + |
| 51 | ((monotonic_trigger.time_since_epoch() >= monotonic_clock::zero()) |
| 52 | ? monotonic_clock::zero() |
| 53 | : interval); |
| 54 | } |
| 55 | |
Austin Schuh | 8aec1ed | 2016-05-01 13:29:20 -0700 | [diff] [blame] | 56 | int PhasedLoop::Iterate(const monotonic_clock::time_point now) { |
Brian Silverman | 8babd8f | 2020-06-23 16:38:50 -0700 | [diff] [blame] | 57 | auto next_time = monotonic_clock::epoch(); |
| 58 | // Round up to the next whole interval, ignoring offset_. |
| 59 | { |
| 60 | const auto offset_now = (now - offset_).time_since_epoch(); |
| 61 | monotonic_clock::duration prerounding; |
| 62 | if (now.time_since_epoch() >= offset_) { |
| 63 | // We're above 0, so rounding up means away from 0. |
| 64 | prerounding = offset_now + interval_; |
| 65 | } else { |
| 66 | // We're below 0, so rounding up means towards 0. |
| 67 | prerounding = offset_now + monotonic_clock::duration(1); |
| 68 | } |
| 69 | next_time += (prerounding / interval_) * interval_; |
| 70 | } |
| 71 | // Add offset_ back in. |
| 72 | next_time += offset_; |
Brian Silverman | dcaa3f7 | 2015-11-29 05:32:08 +0000 | [diff] [blame] | 73 | |
Austin Schuh | 8aec1ed | 2016-05-01 13:29:20 -0700 | [diff] [blame] | 74 | const monotonic_clock::duration difference = next_time - last_time_; |
Milind Upadhyay | 42589bb | 2021-05-19 20:05:16 -0700 | [diff] [blame] | 75 | |
Austin Schuh | 8aec1ed | 2016-05-01 13:29:20 -0700 | [diff] [blame] | 76 | const int result = difference / interval_; |
Austin Schuh | 8aec1ed | 2016-05-01 13:29:20 -0700 | [diff] [blame] | 77 | CHECK_EQ( |
| 78 | 0, (next_time - offset_).time_since_epoch().count() % interval_.count()); |
Brian Silverman | 8babd8f | 2020-06-23 16:38:50 -0700 | [diff] [blame] | 79 | CHECK(next_time > now); |
Austin Schuh | f257f3c | 2019-10-27 21:00:43 -0700 | [diff] [blame] | 80 | CHECK(next_time - now <= interval_); |
Brian Silverman | dcaa3f7 | 2015-11-29 05:32:08 +0000 | [diff] [blame] | 81 | last_time_ = next_time; |
| 82 | return result; |
| 83 | } |
| 84 | |
Stephan Pleines | f63bde8 | 2024-01-13 15:59:33 -0800 | [diff] [blame] | 85 | } // namespace aos::time |