blob: 49b170d4a9c9d3ed36da2dc3039ca8c1b6bfea2f [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 Schuh87dd3832021-01-01 23:07:31 -080047 CHECK_EQ(monotonic_now(), iter->first)
48 << ": Time is wrong on node " << node_index_;
Austin Schuh8bd96322020-02-13 21:18:22 -080049
50 ::std::function<void()> callback = ::std::move(iter->second);
51 events_list_.erase(iter);
52 callback();
53}
54
55void EventScheduler::RunOnRun() {
Austin Schuh39788ff2019-12-01 18:22:57 -080056 for (std::function<void()> &on_run : on_run_) {
57 on_run();
58 }
59 on_run_.clear();
Alex Perrycb7da4b2019-08-28 19:35:56 -070060}
61
Austin Schuhac0771c2020-01-07 18:36:30 -080062std::ostream &operator<<(std::ostream &stream,
63 const aos::distributed_clock::time_point &now) {
64 // Print it the same way we print a monotonic time. Literally.
65 stream << monotonic_clock::time_point(now.time_since_epoch());
66 return stream;
67}
68
Austin Schuh8bd96322020-02-13 21:18:22 -080069void EventSchedulerScheduler::AddEventScheduler(EventScheduler *scheduler) {
70 CHECK(std::find(schedulers_.begin(), schedulers_.end(), scheduler) ==
71 schedulers_.end());
72 CHECK(scheduler->scheduler_scheduler_ == nullptr);
73
74 schedulers_.emplace_back(scheduler);
75 scheduler->scheduler_scheduler_ = this;
76}
77
78void EventSchedulerScheduler::RunFor(distributed_clock::duration duration) {
79 distributed_clock::time_point end_time = now_ + duration;
80 logging::ScopedLogRestorer prev_logger;
81 RunOnRun();
82
83 // Run all the sub-event-schedulers.
84 while (is_running_) {
85 std::tuple<distributed_clock::time_point, EventScheduler *> oldest_event =
86 OldestEvent();
87 // No events left, bail.
88 if (std::get<0>(oldest_event) == distributed_clock::max_time ||
89 std::get<0>(oldest_event) > end_time) {
90 is_running_ = false;
91 break;
92 }
93
94 // We get to pick our tradeoffs here. Either we assume that there are no
95 // backward step changes in our time function for each node, or we have to
Austin Schuh2f8fd752020-09-01 22:38:28 -070096 // let time go backwards. We currently only really see this happen when 2
97 // events are scheduled for "now", time changes, and there is a nanosecond
98 // or two of rounding due to integer math.
99 //
100 // //aos/events/logging:logger_test triggers this.
101 CHECK_LE(now_, std::get<0>(oldest_event) + std::chrono::nanoseconds(1))
Austin Schuh8bd96322020-02-13 21:18:22 -0800102 << ": Simulated time went backwards by too much. Please investigate.";
103 now_ = std::get<0>(oldest_event);
104
105 std::get<1>(oldest_event)->CallOldestEvent();
106 }
107
108 now_ = end_time;
109}
110
111void EventSchedulerScheduler::Run() {
112 logging::ScopedLogRestorer prev_logger;
113 RunOnRun();
114 // Run all the sub-event-schedulers.
115 while (is_running_) {
116 std::tuple<distributed_clock::time_point, EventScheduler *> oldest_event =
117 OldestEvent();
118 // No events left, bail.
119 if (std::get<0>(oldest_event) == distributed_clock::max_time) {
120 break;
121 }
122
123 // We get to pick our tradeoffs here. Either we assume that there are no
124 // backward step changes in our time function for each node, or we have to
Austin Schuh2f8fd752020-09-01 22:38:28 -0700125 // let time go backwards. We currently only really see this happen when 2
126 // events are scheduled for "now", time changes, and there is a nanosecond
127 // or two of rounding due to integer math.
128 //
129 // //aos/events/logging:logger_test triggers this.
130 CHECK_LE(now_, std::get<0>(oldest_event) + std::chrono::nanoseconds(1))
Austin Schuh8bd96322020-02-13 21:18:22 -0800131 << ": Simulated time went backwards by too much. Please investigate.";
132 now_ = std::get<0>(oldest_event);
133
134 std::get<1>(oldest_event)->CallOldestEvent();
135 }
136
137 is_running_ = false;
138}
139
140std::tuple<distributed_clock::time_point, EventScheduler *>
141EventSchedulerScheduler::OldestEvent() {
142 distributed_clock::time_point min_event_time = distributed_clock::max_time;
143 EventScheduler *min_scheduler = nullptr;
144
145 // TODO(austin): Don't linearly search... But for N=3, it is probably the
146 // fastest way to do this.
147 for (EventScheduler *scheduler : schedulers_) {
148 const monotonic_clock::time_point monotonic_event_time =
149 scheduler->OldestEvent();
150 if (monotonic_event_time != monotonic_clock::max_time) {
151 const distributed_clock::time_point event_time =
152 scheduler->ToDistributedClock(monotonic_event_time);
153 if (event_time < min_event_time) {
154 min_event_time = event_time;
155 min_scheduler = scheduler;
156 }
157 }
158 }
159
Austin Schuh87dd3832021-01-01 23:07:31 -0800160 if (min_scheduler) {
161 VLOG(1) << "Oldest event " << min_event_time << " on scheduler "
162 << min_scheduler->node_index_;
163 }
Austin Schuh8bd96322020-02-13 21:18:22 -0800164 return std::make_tuple(min_event_time, min_scheduler);
165}
166
Alex Perrycb7da4b2019-08-28 19:35:56 -0700167} // namespace aos