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