blob: f985eb1d75183562b2f8d35b7853edc93129714e [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();
47 now_ = iter->first;
48
49 ::std::function<void()> callback = ::std::move(iter->second);
50 events_list_.erase(iter);
51 callback();
52}
53
54void EventScheduler::RunOnRun() {
Austin Schuh39788ff2019-12-01 18:22:57 -080055 for (std::function<void()> &on_run : on_run_) {
56 on_run();
57 }
58 on_run_.clear();
Alex Perrycb7da4b2019-08-28 19:35:56 -070059}
60
Austin Schuhac0771c2020-01-07 18:36:30 -080061std::ostream &operator<<(std::ostream &stream,
62 const aos::distributed_clock::time_point &now) {
63 // Print it the same way we print a monotonic time. Literally.
64 stream << monotonic_clock::time_point(now.time_since_epoch());
65 return stream;
66}
67
Austin Schuh8bd96322020-02-13 21:18:22 -080068void EventSchedulerScheduler::AddEventScheduler(EventScheduler *scheduler) {
69 CHECK(std::find(schedulers_.begin(), schedulers_.end(), scheduler) ==
70 schedulers_.end());
71 CHECK(scheduler->scheduler_scheduler_ == nullptr);
72
73 schedulers_.emplace_back(scheduler);
74 scheduler->scheduler_scheduler_ = this;
75}
76
77void EventSchedulerScheduler::RunFor(distributed_clock::duration duration) {
78 distributed_clock::time_point end_time = now_ + duration;
79 logging::ScopedLogRestorer prev_logger;
80 RunOnRun();
81
82 // Run all the sub-event-schedulers.
83 while (is_running_) {
84 std::tuple<distributed_clock::time_point, EventScheduler *> oldest_event =
85 OldestEvent();
86 // No events left, bail.
87 if (std::get<0>(oldest_event) == distributed_clock::max_time ||
88 std::get<0>(oldest_event) > end_time) {
89 is_running_ = false;
90 break;
91 }
92
93 // We get to pick our tradeoffs here. Either we assume that there are no
94 // backward step changes in our time function for each node, or we have to
95 // let time go backwards. This backwards time jump should be small, so we
96 // can check for it and bound it.
97 CHECK_LE(now_, std::get<0>(oldest_event) + std::chrono::milliseconds(100))
98 << ": Simulated time went backwards by too much. Please investigate.";
99 now_ = std::get<0>(oldest_event);
100
101 std::get<1>(oldest_event)->CallOldestEvent();
102 }
103
104 now_ = end_time;
105}
106
107void EventSchedulerScheduler::Run() {
108 logging::ScopedLogRestorer prev_logger;
109 RunOnRun();
110 // Run all the sub-event-schedulers.
111 while (is_running_) {
112 std::tuple<distributed_clock::time_point, EventScheduler *> oldest_event =
113 OldestEvent();
114 // No events left, bail.
115 if (std::get<0>(oldest_event) == distributed_clock::max_time) {
116 break;
117 }
118
119 // We get to pick our tradeoffs here. Either we assume that there are no
120 // backward step changes in our time function for each node, or we have to
121 // let time go backwards. This backwards time jump should be small, so we
122 // can check for it and bound it.
123 CHECK_LE(now_, std::get<0>(oldest_event) + std::chrono::milliseconds(100))
124 << ": Simulated time went backwards by too much. Please investigate.";
125 now_ = std::get<0>(oldest_event);
126
127 std::get<1>(oldest_event)->CallOldestEvent();
128 }
129
130 is_running_ = false;
131}
132
133std::tuple<distributed_clock::time_point, EventScheduler *>
134EventSchedulerScheduler::OldestEvent() {
135 distributed_clock::time_point min_event_time = distributed_clock::max_time;
136 EventScheduler *min_scheduler = nullptr;
137
138 // TODO(austin): Don't linearly search... But for N=3, it is probably the
139 // fastest way to do this.
140 for (EventScheduler *scheduler : schedulers_) {
141 const monotonic_clock::time_point monotonic_event_time =
142 scheduler->OldestEvent();
143 if (monotonic_event_time != monotonic_clock::max_time) {
144 const distributed_clock::time_point event_time =
145 scheduler->ToDistributedClock(monotonic_event_time);
146 if (event_time < min_event_time) {
147 min_event_time = event_time;
148 min_scheduler = scheduler;
149 }
150 }
151 }
152
153 return std::make_tuple(min_event_time, min_scheduler);
154}
155
Alex Perrycb7da4b2019-08-28 19:35:56 -0700156} // namespace aos