blob: ae50917808ca4dba46f9254c36095d9d3c23a6cf [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
Austin Schuhef8f1ae2021-12-11 12:35:05 -080011EventScheduler::Token EventScheduler::Schedule(monotonic_clock::time_point time,
12 Event *callback) {
James Kuszmaul86e86c32022-07-21 17:39:47 -070013 CHECK_LE(monotonic_clock::epoch(), time);
Alex Perrycb7da4b2019-08-28 19:35:56 -070014 return events_list_.emplace(time, callback);
15}
16
17void EventScheduler::Deschedule(EventScheduler::Token token) {
Brian Silverman7026e2d2021-11-11 16:15:35 -080018 // We basically want to DCHECK some nontrivial logic. Guard it with NDEBUG to
19 // ensure the compiler realizes it's all unnecessary when not doing debug
20 // checks.
Brian Silvermanbd405c02020-06-23 16:25:23 -070021#ifndef NDEBUG
22 {
23 bool found = false;
24 auto i = events_list_.begin();
25 while (i != events_list_.end()) {
26 if (i == token) {
27 CHECK(!found) << ": The same iterator is in the multimap twice??";
28 found = true;
29 }
30 ++i;
31 }
32 CHECK(found) << ": Trying to deschedule an event which is not scheduled";
33 }
34#endif
Alex Perrycb7da4b2019-08-28 19:35:56 -070035 events_list_.erase(token);
36}
37
Austin Schuhe12b5eb2022-08-29 12:39:27 -070038std::pair<distributed_clock::time_point, monotonic_clock::time_point>
39EventScheduler::OldestEvent() {
James Kuszmaul86e86c32022-07-21 17:39:47 -070040 // If we haven't started yet, schedule a special event for the epoch to allow
41 // ourselves to boot.
42 if (!called_started_) {
Austin Schuhe12b5eb2022-08-29 12:39:27 -070043 if (!cached_epoch_) {
44 cached_epoch_ = ToDistributedClock(monotonic_clock::epoch());
45 }
46 return std::make_pair(*cached_epoch_, monotonic_clock::epoch());
James Kuszmaul86e86c32022-07-21 17:39:47 -070047 }
48
Austin Schuh8bd96322020-02-13 21:18:22 -080049 if (events_list_.empty()) {
Austin Schuhe12b5eb2022-08-29 12:39:27 -070050 return std::make_pair(distributed_clock::max_time,
51 monotonic_clock::max_time);
Austin Schuh39788ff2019-12-01 18:22:57 -080052 }
Austin Schuh8bd96322020-02-13 21:18:22 -080053
Philipp Schrader790cb542023-07-05 21:06:52 -070054 const monotonic_clock::time_point monotonic_time =
55 events_list_.begin()->first;
Austin Schuhe12b5eb2022-08-29 12:39:27 -070056 if (cached_event_list_monotonic_time_ != monotonic_time) {
57 cached_event_list_time_ = ToDistributedClock(monotonic_time);
58 cached_event_list_monotonic_time_ = monotonic_time;
59 }
60
61 return std::make_pair(cached_event_list_time_, monotonic_time);
Alex Perrycb7da4b2019-08-28 19:35:56 -070062}
63
James Kuszmaul86e86c32022-07-21 17:39:47 -070064void EventScheduler::Shutdown() {
65 CHECK(!is_running_);
66 on_shutdown_();
67}
Austin Schuh58646e22021-08-23 23:51:46 -070068
69void EventScheduler::Startup() {
70 ++boot_count_;
James Kuszmaul86e86c32022-07-21 17:39:47 -070071 CHECK(!is_running_);
72 MaybeRunOnStartup();
73 CHECK(called_started_);
Austin Schuh58646e22021-08-23 23:51:46 -070074}
75
Austin Schuh8bd96322020-02-13 21:18:22 -080076void EventScheduler::CallOldestEvent() {
James Kuszmaul86e86c32022-07-21 17:39:47 -070077 if (!called_started_) {
78 // If we haven't started, start.
79 MaybeRunOnStartup();
80 MaybeRunOnRun();
81 CHECK(called_started_);
82 return;
83 }
84 CHECK(is_running_);
Austin Schuh8bd96322020-02-13 21:18:22 -080085 CHECK_GT(events_list_.size(), 0u);
86 auto iter = events_list_.begin();
Austin Schuh58646e22021-08-23 23:51:46 -070087 const logger::BootTimestamp t =
88 FromDistributedClock(scheduler_scheduler_->distributed_now());
Austin Schuhc1ee1b62022-03-22 17:09:52 -070089 VLOG(2) << "Got time back " << t;
Austin Schuh58646e22021-08-23 23:51:46 -070090 CHECK_EQ(t.boot, boot_count_);
91 CHECK_EQ(t.time, iter->first) << ": Time is wrong on node " << node_index_;
Austin Schuh8bd96322020-02-13 21:18:22 -080092
Austin Schuhef8f1ae2021-12-11 12:35:05 -080093 Event *callback = iter->second;
Austin Schuh8bd96322020-02-13 21:18:22 -080094 events_list_.erase(iter);
Austin Schuhef8f1ae2021-12-11 12:35:05 -080095 callback->Handle();
Austin Schuhb7c8d2a2021-07-19 19:22:12 -070096
97 converter_->ObserveTimePassed(scheduler_scheduler_->distributed_now());
Austin Schuh8bd96322020-02-13 21:18:22 -080098}
99
100void EventScheduler::RunOnRun() {
James Kuszmaul86e86c32022-07-21 17:39:47 -0700101 CHECK(is_running_);
Austin Schuhe33c08d2022-02-03 18:15:21 -0800102 while (!on_run_.empty()) {
103 std::function<void()> fn = std::move(*on_run_.begin());
104 on_run_.erase(on_run_.begin());
105 fn();
Austin Schuh39788ff2019-12-01 18:22:57 -0800106 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700107}
108
Austin Schuhe33c08d2022-02-03 18:15:21 -0800109void EventScheduler::RunOnStartup() noexcept {
110 while (!on_startup_.empty()) {
James Kuszmaul86e86c32022-07-21 17:39:47 -0700111 CHECK(!is_running_);
Austin Schuhe33c08d2022-02-03 18:15:21 -0800112 std::function<void()> fn = std::move(*on_startup_.begin());
113 on_startup_.erase(on_startup_.begin());
114 fn();
Austin Schuh057d29f2021-08-21 23:05:15 -0700115 }
Austin Schuh057d29f2021-08-21 23:05:15 -0700116}
117
Austin Schuhe33c08d2022-02-03 18:15:21 -0800118void EventScheduler::RunStarted() {
James Kuszmaul86e86c32022-07-21 17:39:47 -0700119 CHECK(!is_running_);
Austin Schuhe33c08d2022-02-03 18:15:21 -0800120 if (started_) {
121 started_();
122 }
James Kuszmaul86e86c32022-07-21 17:39:47 -0700123 is_running_ = true;
Austin Schuhe33c08d2022-02-03 18:15:21 -0800124}
125
James Kuszmaul86e86c32022-07-21 17:39:47 -0700126void EventScheduler::MaybeRunStopped() {
127 CHECK(is_running_);
128 is_running_ = false;
129 if (called_started_) {
130 called_started_ = false;
131 if (stopped_) {
132 stopped_();
133 }
134 }
135}
136
137void EventScheduler::MaybeRunOnStartup() {
138 CHECK(!called_started_);
139 CHECK(!is_running_);
140 const logger::BootTimestamp t =
141 FromDistributedClock(scheduler_scheduler_->distributed_now());
142 if (t.boot == boot_count_ && t.time >= monotonic_clock::epoch()) {
143 called_started_ = true;
144 RunOnStartup();
145 }
146}
147
148void EventScheduler::MaybeRunOnRun() {
149 if (called_started_) {
150 RunStarted();
151 RunOnRun();
Austin Schuhe33c08d2022-02-03 18:15:21 -0800152 }
153}
Austin Schuh58646e22021-08-23 23:51:46 -0700154
Austin Schuhac0771c2020-01-07 18:36:30 -0800155std::ostream &operator<<(std::ostream &stream,
156 const aos::distributed_clock::time_point &now) {
157 // Print it the same way we print a monotonic time. Literally.
158 stream << monotonic_clock::time_point(now.time_since_epoch());
159 return stream;
160}
161
Austin Schuh8bd96322020-02-13 21:18:22 -0800162void EventSchedulerScheduler::AddEventScheduler(EventScheduler *scheduler) {
163 CHECK(std::find(schedulers_.begin(), schedulers_.end(), scheduler) ==
164 schedulers_.end());
165 CHECK(scheduler->scheduler_scheduler_ == nullptr);
Austin Schuh58646e22021-08-23 23:51:46 -0700166 CHECK_EQ(scheduler->node_index(), schedulers_.size());
Austin Schuh8bd96322020-02-13 21:18:22 -0800167
168 schedulers_.emplace_back(scheduler);
169 scheduler->scheduler_scheduler_ = this;
170}
171
James Kuszmaul86e86c32022-07-21 17:39:47 -0700172void EventSchedulerScheduler::MaybeRunStopped() {
173 CHECK(!is_running_);
174 for (EventScheduler *scheduler : schedulers_) {
175 if (scheduler->is_running()) {
176 scheduler->MaybeRunStopped();
177 }
178 }
179}
180
181bool EventSchedulerScheduler::RunUntil(
182 realtime_clock::time_point end_time, EventScheduler *scheduler,
183 std::function<std::chrono::nanoseconds()> fn_realtime_offset) {
184 logging::ScopedLogRestorer prev_logger;
185 MaybeRunOnStartup();
186
187 bool reached_end_time = false;
188
189 RunMaybeRealtimeLoop([this, scheduler, end_time, fn_realtime_offset,
190 &reached_end_time]() {
191 std::tuple<distributed_clock::time_point, EventScheduler *> oldest_event =
192 OldestEvent();
193 aos::distributed_clock::time_point oldest_event_time_distributed =
194 std::get<0>(oldest_event);
195 logger::BootTimestamp test_time_monotonic =
196 scheduler->FromDistributedClock(oldest_event_time_distributed);
197 realtime_clock::time_point oldest_event_realtime(
198 test_time_monotonic.time_since_epoch() + fn_realtime_offset());
199
200 if ((std::get<0>(oldest_event) == distributed_clock::max_time) ||
201 (oldest_event_realtime > end_time &&
202 (reboots_.empty() ||
203 std::get<0>(reboots_.front()) > oldest_event_time_distributed))) {
204 is_running_ = false;
205 reached_end_time = true;
206
207 // We have to nudge our time back to the distributed time
208 // corresponding to our desired realtime time.
Austin Schuhe12b5eb2022-08-29 12:39:27 -0700209 const monotonic_clock::time_point end_monotonic =
210 monotonic_clock::epoch() + end_time.time_since_epoch() -
James Kuszmaul86e86c32022-07-21 17:39:47 -0700211 fn_realtime_offset();
212 const aos::distributed_clock::time_point end_time_distributed =
213 scheduler->ToDistributedClock(end_monotonic);
214
215 now_ = end_time_distributed;
216
217 return;
218 }
219
220 if (!reboots_.empty() &&
221 std::get<0>(reboots_.front()) <= std::get<0>(oldest_event)) {
222 // Reboot is next.
223 CHECK_LE(now_,
224 std::get<0>(reboots_.front()) + std::chrono::nanoseconds(1))
225 << ": Simulated time went backwards by too much. Please "
226 "investigate.";
227 now_ = std::get<0>(reboots_.front());
228 Reboot();
229 reboots_.erase(reboots_.begin());
230 return;
231 }
232
233 // We get to pick our tradeoffs here. Either we assume that there are
234 // no backward step changes in our time function for each node, or we
235 // have to let time go backwards. We currently only really see this
236 // happen when 2 events are scheduled for "now", time changes, and
237 // there is a nanosecond or two of rounding due to integer math.
238 //
239 // //aos/events/logging:logger_test triggers this.
240 CHECK_LE(now_, std::get<0>(oldest_event) + std::chrono::nanoseconds(1))
241 << ": Simulated time went backwards by too much. Please "
242 "investigate.";
243
244 now_ = std::get<0>(oldest_event);
245
246 std::get<1>(oldest_event)->CallOldestEvent();
247 });
248
249 MaybeRunStopped();
250
251 return reached_end_time;
252}
253
Austin Schuh58646e22021-08-23 23:51:46 -0700254void EventSchedulerScheduler::Reboot() {
255 const std::vector<logger::BootTimestamp> &times =
256 std::get<1>(reboots_.front());
257 CHECK_EQ(times.size(), schedulers_.size());
258
259 VLOG(1) << "Rebooting at " << now_;
260 for (const auto &time : times) {
261 VLOG(1) << " " << time;
262 }
263
264 is_running_ = false;
265
266 // Shut everything down.
267 std::vector<size_t> rebooted;
268 for (size_t node_index = 0; node_index < schedulers_.size(); ++node_index) {
269 if (schedulers_[node_index]->boot_count() == times[node_index].boot) {
270 continue;
271 } else {
272 rebooted.emplace_back(node_index);
273 CHECK_EQ(schedulers_[node_index]->boot_count() + 1,
274 times[node_index].boot);
James Kuszmaul86e86c32022-07-21 17:39:47 -0700275 schedulers_[node_index]->MaybeRunStopped();
Austin Schuh58646e22021-08-23 23:51:46 -0700276 schedulers_[node_index]->Shutdown();
277 }
278 }
279
280 // And start it back up again to reboot. When something starts back up
281 // (especially message_bridge), it could try to send stuff out. We want
282 // to move everything over to the new boot before doing that.
283 for (const size_t node_index : rebooted) {
Austin Schuh58646e22021-08-23 23:51:46 -0700284 schedulers_[node_index]->Startup();
285 }
Austin Schuh58646e22021-08-23 23:51:46 -0700286 for (const size_t node_index : rebooted) {
James Kuszmaul86e86c32022-07-21 17:39:47 -0700287 schedulers_[node_index]->MaybeRunOnRun();
Austin Schuh58646e22021-08-23 23:51:46 -0700288 }
289 is_running_ = true;
290}
291
Austin Schuh8bd96322020-02-13 21:18:22 -0800292void EventSchedulerScheduler::RunFor(distributed_clock::duration duration) {
293 distributed_clock::time_point end_time = now_ + duration;
294 logging::ScopedLogRestorer prev_logger;
James Kuszmaul86e86c32022-07-21 17:39:47 -0700295 MaybeRunOnStartup();
Austin Schuh8bd96322020-02-13 21:18:22 -0800296
297 // Run all the sub-event-schedulers.
James Kuszmaulb67409b2022-06-20 16:25:03 -0700298 RunMaybeRealtimeLoop([this, end_time]() {
Austin Schuh8bd96322020-02-13 21:18:22 -0800299 std::tuple<distributed_clock::time_point, EventScheduler *> oldest_event =
300 OldestEvent();
Austin Schuh58646e22021-08-23 23:51:46 -0700301 if (!reboots_.empty() &&
302 std::get<0>(reboots_.front()) <= std::get<0>(oldest_event)) {
303 // Reboot is next.
304 if (std::get<0>(reboots_.front()) > end_time) {
305 // Reboot is after our end time, give up.
306 is_running_ = false;
James Kuszmaulb67409b2022-06-20 16:25:03 -0700307 return;
Austin Schuh58646e22021-08-23 23:51:46 -0700308 }
309
310 CHECK_LE(now_,
311 std::get<0>(reboots_.front()) + std::chrono::nanoseconds(1))
312 << ": Simulated time went backwards by too much. Please "
313 "investigate.";
314 now_ = std::get<0>(reboots_.front());
315 Reboot();
316 reboots_.erase(reboots_.begin());
James Kuszmaulb67409b2022-06-20 16:25:03 -0700317 return;
Austin Schuh58646e22021-08-23 23:51:46 -0700318 }
319
Austin Schuh8bd96322020-02-13 21:18:22 -0800320 // No events left, bail.
321 if (std::get<0>(oldest_event) == distributed_clock::max_time ||
322 std::get<0>(oldest_event) > end_time) {
323 is_running_ = false;
James Kuszmaulb67409b2022-06-20 16:25:03 -0700324 return;
Austin Schuh8bd96322020-02-13 21:18:22 -0800325 }
326
327 // We get to pick our tradeoffs here. Either we assume that there are no
328 // backward step changes in our time function for each node, or we have to
Austin Schuh2f8fd752020-09-01 22:38:28 -0700329 // let time go backwards. We currently only really see this happen when 2
330 // events are scheduled for "now", time changes, and there is a nanosecond
331 // or two of rounding due to integer math.
332 //
333 // //aos/events/logging:logger_test triggers this.
334 CHECK_LE(now_, std::get<0>(oldest_event) + std::chrono::nanoseconds(1))
Austin Schuh8bd96322020-02-13 21:18:22 -0800335 << ": Simulated time went backwards by too much. Please investigate.";
James Kuszmaul86e86c32022-07-21 17:39:47 -0700336 // push time forwards
Austin Schuh8bd96322020-02-13 21:18:22 -0800337 now_ = std::get<0>(oldest_event);
338
339 std::get<1>(oldest_event)->CallOldestEvent();
James Kuszmaulb67409b2022-06-20 16:25:03 -0700340 });
Austin Schuh8bd96322020-02-13 21:18:22 -0800341
342 now_ = end_time;
Austin Schuhe33c08d2022-02-03 18:15:21 -0800343
James Kuszmaul86e86c32022-07-21 17:39:47 -0700344 MaybeRunStopped();
Austin Schuh8bd96322020-02-13 21:18:22 -0800345}
346
347void EventSchedulerScheduler::Run() {
348 logging::ScopedLogRestorer prev_logger;
James Kuszmaul86e86c32022-07-21 17:39:47 -0700349 MaybeRunOnStartup();
350
351 // Run all the sub-event-schedulers.
James Kuszmaulb67409b2022-06-20 16:25:03 -0700352 RunMaybeRealtimeLoop([this]() {
Austin Schuh8bd96322020-02-13 21:18:22 -0800353 std::tuple<distributed_clock::time_point, EventScheduler *> oldest_event =
354 OldestEvent();
Austin Schuh58646e22021-08-23 23:51:46 -0700355 if (!reboots_.empty() &&
356 std::get<0>(reboots_.front()) <= std::get<0>(oldest_event)) {
357 // Reboot is next.
358 CHECK_LE(now_,
359 std::get<0>(reboots_.front()) + std::chrono::nanoseconds(1))
360 << ": Simulated time went backwards by too much. Please "
361 "investigate.";
362 now_ = std::get<0>(reboots_.front());
363 Reboot();
364 reboots_.erase(reboots_.begin());
James Kuszmaulb67409b2022-06-20 16:25:03 -0700365 return;
Austin Schuh58646e22021-08-23 23:51:46 -0700366 }
Austin Schuh8bd96322020-02-13 21:18:22 -0800367 // No events left, bail.
368 if (std::get<0>(oldest_event) == distributed_clock::max_time) {
James Kuszmaulb67409b2022-06-20 16:25:03 -0700369 is_running_ = false;
370 return;
Austin Schuh8bd96322020-02-13 21:18:22 -0800371 }
372
373 // We get to pick our tradeoffs here. Either we assume that there are no
374 // backward step changes in our time function for each node, or we have to
Austin Schuh2f8fd752020-09-01 22:38:28 -0700375 // let time go backwards. We currently only really see this happen when 2
376 // events are scheduled for "now", time changes, and there is a nanosecond
377 // or two of rounding due to integer math.
378 //
379 // //aos/events/logging:logger_test triggers this.
380 CHECK_LE(now_, std::get<0>(oldest_event) + std::chrono::nanoseconds(1))
Austin Schuh8bd96322020-02-13 21:18:22 -0800381 << ": Simulated time went backwards by too much. Please investigate.";
382 now_ = std::get<0>(oldest_event);
383
384 std::get<1>(oldest_event)->CallOldestEvent();
James Kuszmaulb67409b2022-06-20 16:25:03 -0700385 });
Austin Schuhe33c08d2022-02-03 18:15:21 -0800386
James Kuszmaul86e86c32022-07-21 17:39:47 -0700387 MaybeRunStopped();
Austin Schuh8bd96322020-02-13 21:18:22 -0800388}
389
James Kuszmaulb67409b2022-06-20 16:25:03 -0700390template <typename F>
391void EventSchedulerScheduler::RunMaybeRealtimeLoop(F loop_body) {
392 internal::TimerFd timerfd;
393 CHECK_LT(0.0, replay_rate_) << "Replay rate must be positive.";
394 distributed_clock::time_point last_distributed_clock =
395 std::get<0>(OldestEvent());
396 monotonic_clock::time_point last_monotonic_clock = monotonic_clock::now();
397 timerfd.SetTime(last_monotonic_clock, std::chrono::seconds(0));
398 epoll_.OnReadable(
399 timerfd.fd(), [this, &last_distributed_clock, &last_monotonic_clock,
400 &timerfd, loop_body]() {
401 const uint64_t read_result = timerfd.Read();
402 if (!is_running_) {
403 epoll_.Quit();
404 return;
405 }
406 CHECK_EQ(read_result, 1u);
407 // Call loop_body() at least once; if we are in infinite-speed replay,
408 // we don't actually want/need the context switches from the epoll
409 // setup, so just loop.
410 // Note: The performance impacts of this code have not been carefully
411 // inspected (e.g., how much does avoiding the context-switch help; does
412 // the timerfd_settime call matter).
413 // This is deliberately written to support the user changing replay
414 // rates dynamically.
415 do {
416 loop_body();
417 if (is_running_) {
418 const monotonic_clock::time_point next_trigger =
419 last_monotonic_clock +
420 std::chrono::duration_cast<std::chrono::nanoseconds>(
421 (now_ - last_distributed_clock) / replay_rate_);
422 timerfd.SetTime(next_trigger, std::chrono::seconds(0));
423 last_monotonic_clock = next_trigger;
424 last_distributed_clock = now_;
425 } else {
426 epoll_.Quit();
427 }
428 } while (replay_rate_ == std::numeric_limits<double>::infinity() &&
429 is_running_);
430 });
431
432 epoll_.Run();
433 epoll_.DeleteFd(timerfd.fd());
434}
435
Austin Schuh8bd96322020-02-13 21:18:22 -0800436std::tuple<distributed_clock::time_point, EventScheduler *>
437EventSchedulerScheduler::OldestEvent() {
438 distributed_clock::time_point min_event_time = distributed_clock::max_time;
439 EventScheduler *min_scheduler = nullptr;
440
441 // TODO(austin): Don't linearly search... But for N=3, it is probably the
442 // fastest way to do this.
443 for (EventScheduler *scheduler : schedulers_) {
Austin Schuhe12b5eb2022-08-29 12:39:27 -0700444 const std::pair<distributed_clock::time_point, monotonic_clock::time_point>
445 event_time = scheduler->OldestEvent();
446 if (event_time.second != monotonic_clock::max_time) {
447 if (event_time.first < min_event_time) {
448 min_event_time = event_time.first;
Austin Schuh8bd96322020-02-13 21:18:22 -0800449 min_scheduler = scheduler;
450 }
451 }
452 }
453
Austin Schuh87dd3832021-01-01 23:07:31 -0800454 if (min_scheduler) {
Austin Schuhc1ee1b62022-03-22 17:09:52 -0700455 VLOG(2) << "Oldest event " << min_event_time << " on scheduler "
Austin Schuh87dd3832021-01-01 23:07:31 -0800456 << min_scheduler->node_index_;
457 }
Austin Schuh8bd96322020-02-13 21:18:22 -0800458 return std::make_tuple(min_event_time, min_scheduler);
459}
460
Austin Schuhe33c08d2022-02-03 18:15:21 -0800461void EventSchedulerScheduler::TemporarilyStopAndRun(std::function<void()> fn) {
Austin Schuh3e31f912023-08-21 21:29:10 -0700462 if (in_on_run_) {
463 LOG(FATAL)
464 << "Can't call AllowApplicationCreationDuring from an OnRun callback.";
465 }
Austin Schuhe33c08d2022-02-03 18:15:21 -0800466 const bool was_running = is_running_;
467 if (is_running_) {
468 is_running_ = false;
James Kuszmaul86e86c32022-07-21 17:39:47 -0700469 MaybeRunStopped();
Austin Schuhe33c08d2022-02-03 18:15:21 -0800470 }
471 fn();
472 if (was_running) {
James Kuszmaul86e86c32022-07-21 17:39:47 -0700473 MaybeRunOnStartup();
474 }
475}
476
477void EventSchedulerScheduler::MaybeRunOnStartup() {
478 is_running_ = true;
479 for (EventScheduler *scheduler : schedulers_) {
480 scheduler->MaybeRunOnStartup();
481 }
Austin Schuh3e31f912023-08-21 21:29:10 -0700482 in_on_run_ = true;
James Kuszmaul86e86c32022-07-21 17:39:47 -0700483 // We must trigger all the OnRun's *after* all the OnStartup callbacks are
484 // triggered because that is the contract that we have stated.
485 for (EventScheduler *scheduler : schedulers_) {
486 scheduler->MaybeRunOnRun();
Austin Schuhe33c08d2022-02-03 18:15:21 -0800487 }
Austin Schuh3e31f912023-08-21 21:29:10 -0700488 in_on_run_ = false;
Austin Schuhe33c08d2022-02-03 18:15:21 -0800489}
490
Alex Perrycb7da4b2019-08-28 19:35:56 -0700491} // namespace aos