blob: f5d6c92763b9f28014bd173f10c080c33022e243 [file] [log] [blame]
Alex Perrycb7da4b2019-08-28 19:35:56 -07001#include "aos/events/event_scheduler.h"
2
3#include <algorithm>
4#include <deque>
5
6#include "aos/events/event_loop.h"
Tyler Chatow67ddb032020-01-12 14:30:04 -08007#include "aos/logging/implementations.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -07008
9namespace aos {
10
11EventScheduler::Token EventScheduler::Schedule(
Austin Schuh8bd96322020-02-13 21:18:22 -080012 monotonic_clock::time_point time, ::std::function<void()> callback) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070013 return events_list_.emplace(time, callback);
14}
15
16void EventScheduler::Deschedule(EventScheduler::Token token) {
Brian Silvermanbd405c02020-06-23 16:25:23 -070017 // We basically want to DCHECK some nontrivial logic. Guard it with NDEBUG to ensure the compiler
18 // realizes it's all unnecessary when not doing debug checks.
19#ifndef NDEBUG
20 {
21 bool found = false;
22 auto i = events_list_.begin();
23 while (i != events_list_.end()) {
24 if (i == token) {
25 CHECK(!found) << ": The same iterator is in the multimap twice??";
26 found = true;
27 }
28 ++i;
29 }
30 CHECK(found) << ": Trying to deschedule an event which is not scheduled";
31 }
32#endif
Alex Perrycb7da4b2019-08-28 19:35:56 -070033 events_list_.erase(token);
34}
35
Austin Schuh8bd96322020-02-13 21:18:22 -080036aos::monotonic_clock::time_point EventScheduler::OldestEvent() {
37 if (events_list_.empty()) {
38 return monotonic_clock::max_time;
Austin Schuh39788ff2019-12-01 18:22:57 -080039 }
Austin Schuh8bd96322020-02-13 21:18:22 -080040
41 return events_list_.begin()->first;
Alex Perrycb7da4b2019-08-28 19:35:56 -070042}
43
Austin Schuh8bd96322020-02-13 21:18:22 -080044void EventScheduler::CallOldestEvent() {
45 CHECK_GT(events_list_.size(), 0u);
46 auto iter = events_list_.begin();
Austin Schuhbe69cf32020-08-27 11:38:33 -070047 monotonic_now_ = iter->first;
48 monotonic_now_valid_ = true;
Austin Schuh8bd96322020-02-13 21:18:22 -080049
50 ::std::function<void()> callback = ::std::move(iter->second);
51 events_list_.erase(iter);
52 callback();
Austin Schuhbe69cf32020-08-27 11:38:33 -070053 monotonic_now_valid_ = false;
Austin Schuh8bd96322020-02-13 21:18:22 -080054}
55
56void EventScheduler::RunOnRun() {
Austin Schuh39788ff2019-12-01 18:22:57 -080057 for (std::function<void()> &on_run : on_run_) {
58 on_run();
59 }
60 on_run_.clear();
Alex Perrycb7da4b2019-08-28 19:35:56 -070061}
62
Austin Schuhac0771c2020-01-07 18:36:30 -080063std::ostream &operator<<(std::ostream &stream,
64 const aos::distributed_clock::time_point &now) {
65 // Print it the same way we print a monotonic time. Literally.
66 stream << monotonic_clock::time_point(now.time_since_epoch());
67 return stream;
68}
69
Austin Schuh8bd96322020-02-13 21:18:22 -080070void EventSchedulerScheduler::AddEventScheduler(EventScheduler *scheduler) {
71 CHECK(std::find(schedulers_.begin(), schedulers_.end(), scheduler) ==
72 schedulers_.end());
73 CHECK(scheduler->scheduler_scheduler_ == nullptr);
74
75 schedulers_.emplace_back(scheduler);
76 scheduler->scheduler_scheduler_ = this;
77}
78
79void EventSchedulerScheduler::RunFor(distributed_clock::duration duration) {
80 distributed_clock::time_point end_time = now_ + duration;
81 logging::ScopedLogRestorer prev_logger;
82 RunOnRun();
83
84 // Run all the sub-event-schedulers.
85 while (is_running_) {
86 std::tuple<distributed_clock::time_point, EventScheduler *> oldest_event =
87 OldestEvent();
88 // No events left, bail.
89 if (std::get<0>(oldest_event) == distributed_clock::max_time ||
90 std::get<0>(oldest_event) > end_time) {
91 is_running_ = false;
92 break;
93 }
94
95 // We get to pick our tradeoffs here. Either we assume that there are no
96 // backward step changes in our time function for each node, or we have to
Austin Schuh2f8fd752020-09-01 22:38:28 -070097 // let time go backwards. We currently only really see this happen when 2
98 // events are scheduled for "now", time changes, and there is a nanosecond
99 // or two of rounding due to integer math.
100 //
101 // //aos/events/logging:logger_test triggers this.
102 CHECK_LE(now_, std::get<0>(oldest_event) + std::chrono::nanoseconds(1))
Austin Schuh8bd96322020-02-13 21:18:22 -0800103 << ": Simulated time went backwards by too much. Please investigate.";
104 now_ = std::get<0>(oldest_event);
105
106 std::get<1>(oldest_event)->CallOldestEvent();
107 }
108
109 now_ = end_time;
110}
111
112void EventSchedulerScheduler::Run() {
113 logging::ScopedLogRestorer prev_logger;
114 RunOnRun();
115 // Run all the sub-event-schedulers.
116 while (is_running_) {
117 std::tuple<distributed_clock::time_point, EventScheduler *> oldest_event =
118 OldestEvent();
119 // No events left, bail.
120 if (std::get<0>(oldest_event) == distributed_clock::max_time) {
121 break;
122 }
123
124 // We get to pick our tradeoffs here. Either we assume that there are no
125 // backward step changes in our time function for each node, or we have to
Austin Schuh2f8fd752020-09-01 22:38:28 -0700126 // let time go backwards. We currently only really see this happen when 2
127 // events are scheduled for "now", time changes, and there is a nanosecond
128 // or two of rounding due to integer math.
129 //
130 // //aos/events/logging:logger_test triggers this.
131 CHECK_LE(now_, std::get<0>(oldest_event) + std::chrono::nanoseconds(1))
Austin Schuh8bd96322020-02-13 21:18:22 -0800132 << ": Simulated time went backwards by too much. Please investigate.";
133 now_ = std::get<0>(oldest_event);
134
135 std::get<1>(oldest_event)->CallOldestEvent();
136 }
137
138 is_running_ = false;
139}
140
141std::tuple<distributed_clock::time_point, EventScheduler *>
142EventSchedulerScheduler::OldestEvent() {
143 distributed_clock::time_point min_event_time = distributed_clock::max_time;
144 EventScheduler *min_scheduler = nullptr;
145
146 // TODO(austin): Don't linearly search... But for N=3, it is probably the
147 // fastest way to do this.
148 for (EventScheduler *scheduler : schedulers_) {
149 const monotonic_clock::time_point monotonic_event_time =
150 scheduler->OldestEvent();
151 if (monotonic_event_time != monotonic_clock::max_time) {
152 const distributed_clock::time_point event_time =
153 scheduler->ToDistributedClock(monotonic_event_time);
154 if (event_time < min_event_time) {
155 min_event_time = event_time;
156 min_scheduler = scheduler;
157 }
158 }
159 }
160
161 return std::make_tuple(min_event_time, min_scheduler);
162}
163
Alex Perrycb7da4b2019-08-28 19:35:56 -0700164} // namespace aos