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