blob: 7b6eaa703829a57e3621b9fadcddcb88fe1f4180 [file] [log] [blame]
Alex Perrycb7da4b2019-08-28 19:35:56 -07001#ifndef AOS_EVENTS_SIMULATED_EVENT_LOOP_H_
2#define AOS_EVENTS_SIMULATED_EVENT_LOOP_H_
3
4#include <algorithm>
Brian Silverman601b9722020-06-18 14:33:43 -07005#include <functional>
Alex Perrycb7da4b2019-08-28 19:35:56 -07006#include <map>
7#include <memory>
Austin Schuh5f1cc5c2019-12-01 18:01:11 -08008#include <string_view>
Alex Perrycb7da4b2019-08-28 19:35:56 -07009#include <unordered_set>
10#include <utility>
11#include <vector>
12
13#include "absl/container/btree_map.h"
14#include "aos/events/event_loop.h"
15#include "aos/events/event_scheduler.h"
Austin Schuhe1dafe42020-01-06 21:12:03 -080016#include "aos/events/simple_channel.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070017#include "aos/flatbuffer_merge.h"
18#include "aos/flatbuffers.h"
19#include "aos/ipc_lib/index.h"
Austin Schuh4385b142021-03-14 21:31:13 -070020#include "aos/uuid.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070021#include "glog/logging.h"
22
23namespace aos {
24
25// Class for simulated fetchers.
26class SimulatedChannel;
27
Austin Schuhac0771c2020-01-07 18:36:30 -080028class NodeEventLoopFactory;
Austin Schuh057d29f2021-08-21 23:05:15 -070029class SimulatedEventLoop;
Brian Silvermane1fe2512022-08-14 23:18:50 -070030class SimulatedFactoryExitHandle;
Austin Schuh898f4972020-01-11 17:21:25 -080031namespace message_bridge {
32class SimulatedMessageBridge;
33}
Austin Schuhac0771c2020-01-07 18:36:30 -080034
35// There are 2 concepts needed to support multi-node simulations.
36// 1) The node. This is implemented with NodeEventLoopFactory.
37// 2) The "robot" which runs multiple nodes. This is implemented with
38// SimulatedEventLoopFactory.
39//
40// To make things easier, SimulatedEventLoopFactory takes an optional Node
41// argument if you want to make event loops without interacting with the
42// NodeEventLoopFactory object.
43//
44// The basic flow goes something like as follows:
45//
46// SimulatedEventLoopFactory factory(config);
47// const Node *pi1 = configuration::GetNode(factory.configuration(), "pi1");
48// std::unique_ptr<EventLoop> event_loop = factory.MakeEventLoop("ping", pi1);
49//
50// Or
51//
52// SimulatedEventLoopFactory factory(config);
53// const Node *pi1 = configuration::GetNode(factory.configuration(), "pi1");
54// NodeEventLoopFactory *pi1_factory = factory.GetNodeEventLoopFactory(pi1);
55// std::unique_ptr<EventLoop> event_loop = pi1_factory.MakeEventLoop("ping");
56//
57// The distributed_clock is used to be the base time. NodeEventLoopFactory has
58// all the information needed to adjust both the realtime and monotonic clocks
59// relative to the distributed_clock.
Alex Perrycb7da4b2019-08-28 19:35:56 -070060class SimulatedEventLoopFactory {
61 public:
62 // Constructs a SimulatedEventLoopFactory with the provided configuration.
63 // This configuration must remain in scope for the lifetime of the factory and
64 // all sub-objects.
65 SimulatedEventLoopFactory(const Configuration *configuration);
66 ~SimulatedEventLoopFactory();
67
Austin Schuh58646e22021-08-23 23:51:46 -070068 SimulatedEventLoopFactory(const SimulatedEventLoopFactory &) = delete;
69 SimulatedEventLoopFactory &operator=(const SimulatedEventLoopFactory &) =
70 delete;
71 SimulatedEventLoopFactory(SimulatedEventLoopFactory &&) = delete;
72 SimulatedEventLoopFactory &operator=(SimulatedEventLoopFactory &&) = delete;
73
Austin Schuhac0771c2020-01-07 18:36:30 -080074 // Creates an event loop. If running in a multi-node environment, node needs
75 // to point to the node to create this event loop on.
76 ::std::unique_ptr<EventLoop> MakeEventLoop(std::string_view name,
77 const Node *node = nullptr);
78
79 // Returns the NodeEventLoopFactory for the provided node. The returned
80 // NodeEventLoopFactory is owned by the SimulatedEventLoopFactory and has a
81 // lifetime identical to the factory.
82 NodeEventLoopFactory *GetNodeEventLoopFactory(const Node *node);
Austin Schuh057d29f2021-08-21 23:05:15 -070083 NodeEventLoopFactory *GetNodeEventLoopFactory(std::string_view node);
Alex Perrycb7da4b2019-08-28 19:35:56 -070084
Austin Schuh87dd3832021-01-01 23:07:31 -080085 // Sets the time converter for all nodes.
86 void SetTimeConverter(TimeConverter *time_converter);
87
Austin Schuh58646e22021-08-23 23:51:46 -070088 // Starts executing the event loops unconditionally until Exit is called or
89 // all the nodes have shut down.
Alex Perrycb7da4b2019-08-28 19:35:56 -070090 void Run();
91 // Executes the event loops for a duration.
Austin Schuhac0771c2020-01-07 18:36:30 -080092 void RunFor(distributed_clock::duration duration);
Alex Perrycb7da4b2019-08-28 19:35:56 -070093
94 // Stops executing all event loops. Meant to be called from within an event
95 // loop handler.
Austin Schuh8fb315a2020-11-19 22:33:58 -080096 void Exit();
Alex Perrycb7da4b2019-08-28 19:35:56 -070097
Brian Silvermane1fe2512022-08-14 23:18:50 -070098 std::unique_ptr<ExitHandle> MakeExitHandle();
99
Austin Schuhac0771c2020-01-07 18:36:30 -0800100 const std::vector<const Node *> &nodes() const { return nodes_; }
101
102 // Sets the simulated send delay for all messages sent within a single node.
Austin Schuh7d87b672019-12-01 20:23:49 -0800103 void set_send_delay(std::chrono::nanoseconds send_delay);
Austin Schuhac0771c2020-01-07 18:36:30 -0800104 std::chrono::nanoseconds send_delay() const { return send_delay_; }
105
106 // Sets the simulated network delay for messages forwarded between nodes.
Brian Silvermana7c62052020-04-28 16:52:27 -0700107 void set_network_delay(std::chrono::nanoseconds network_delay) {
108 network_delay_ = network_delay;
109 }
Austin Schuhac0771c2020-01-07 18:36:30 -0800110 std::chrono::nanoseconds network_delay() const { return network_delay_; }
111
112 // Returns the clock used to synchronize the nodes.
113 distributed_clock::time_point distributed_now() const {
Austin Schuh8bd96322020-02-13 21:18:22 -0800114 return scheduler_scheduler_.distributed_now();
Austin Schuhac0771c2020-01-07 18:36:30 -0800115 }
116
117 // Returns the configuration used for everything.
118 const Configuration *configuration() const { return configuration_; }
119
Austin Schuh6f3babe2020-01-26 20:34:50 -0800120 // Disables forwarding for this channel. This should be used very rarely only
121 // for things like the logger.
122 void DisableForwarding(const Channel *channel);
123
Austin Schuh4c3b9702020-08-30 11:34:55 -0700124 // Disables the messages sent by the simulated message gateway.
125 void DisableStatistics();
Austin Schuh48205e62021-11-12 14:13:18 -0800126 // Enables the messages sent by the simulated message gateway.
127 void EnableStatistics();
Austin Schuh4c3b9702020-08-30 11:34:55 -0700128
Austin Schuh2928ebe2021-02-07 22:10:27 -0800129 // Calls SkipTimingReport() on all EventLoops used as part of the
130 // infrastructure. This may improve the performance of long-simulated-duration
131 // tests.
132 void SkipTimingReport();
133
Austin Schuhe33c08d2022-02-03 18:15:21 -0800134 // Re-enables application creation for the duration of fn. This is mostly to
135 // allow use cases like log reading to create applications after the node
136 // starts up without stopping execution.
137 void AllowApplicationCreationDuring(std::function<void()> fn);
138
Austin Schuhac0771c2020-01-07 18:36:30 -0800139 private:
Austin Schuhc0b0f722020-12-12 18:36:06 -0800140 friend class NodeEventLoopFactory;
Brian Silvermane1fe2512022-08-14 23:18:50 -0700141 friend class SimulatedFactoryExitHandle;
Austin Schuhc0b0f722020-12-12 18:36:06 -0800142
Austin Schuhac0771c2020-01-07 18:36:30 -0800143 const Configuration *const configuration_;
Austin Schuh8bd96322020-02-13 21:18:22 -0800144 EventSchedulerScheduler scheduler_scheduler_;
Austin Schuhac0771c2020-01-07 18:36:30 -0800145
146 std::chrono::nanoseconds send_delay_ = std::chrono::microseconds(50);
147 std::chrono::nanoseconds network_delay_ = std::chrono::microseconds(100);
148
Austin Schuh58646e22021-08-23 23:51:46 -0700149 std::unique_ptr<message_bridge::SimulatedMessageBridge> bridge_;
150
Austin Schuhac0771c2020-01-07 18:36:30 -0800151 std::vector<std::unique_ptr<NodeEventLoopFactory>> node_factories_;
152
153 std::vector<const Node *> nodes_;
Brian Silvermane1fe2512022-08-14 23:18:50 -0700154
155 int exit_handle_count_ = 0;
Austin Schuhac0771c2020-01-07 18:36:30 -0800156};
157
158// This class holds all the state required to be a single node.
159class NodeEventLoopFactory {
160 public:
Austin Schuh057d29f2021-08-21 23:05:15 -0700161 ~NodeEventLoopFactory();
162
James Kuszmaul890c2492022-04-06 14:59:31 -0700163 // Whether a given event loop should have its senders checked for messages
164 // being sent too fast. Should only be used by the LogReader or other highly
165 // specialized applications that need to be able to bypass normal behaviors.
166 enum class CheckSentTooFast { kNo, kYes };
167 // Whether the created EventLoop should be the only one allowed to send on all
168 // of its channels. Mostly useful for the LogReader, to allow us to confirm
169 // whether the LogReader is conflicting with the output of any applications
170 // being run in replay.
171 enum class ExclusiveSenders { kNo, kYes };
172 struct EventLoopOptions {
173 CheckSentTooFast check_sent_too_fast;
174 ExclusiveSenders exclusive_senders;
175 };
176
177 // Takes the name for the event loop and a struct of options for selecting
178 // what checks to run for the event loop in question.
179 std::unique_ptr<EventLoop> MakeEventLoop(
180 std::string_view name,
181 EventLoopOptions options = EventLoopOptions{CheckSentTooFast::kYes,
182 ExclusiveSenders::kNo});
Austin Schuh7d87b672019-12-01 20:23:49 -0800183
Austin Schuh217a9782019-12-21 23:02:50 -0800184 // Returns the node that this factory is running as, or nullptr if this is a
185 // single node setup.
186 const Node *node() const { return node_; }
187
Austin Schuh92547522019-12-28 14:33:43 -0800188 // Sets realtime clock to realtime_now for a given monotonic clock.
189 void SetRealtimeOffset(monotonic_clock::time_point monotonic_now,
190 realtime_clock::time_point realtime_now) {
Austin Schuhac0771c2020-01-07 18:36:30 -0800191 realtime_offset_ =
192 realtime_now.time_since_epoch() - monotonic_now.time_since_epoch();
Austin Schuh92547522019-12-28 14:33:43 -0800193 }
194
Austin Schuhac0771c2020-01-07 18:36:30 -0800195 // Returns the current time on both clocks.
196 inline monotonic_clock::time_point monotonic_now() const;
197 inline realtime_clock::time_point realtime_now() const;
Austin Schuh58646e22021-08-23 23:51:46 -0700198 inline distributed_clock::time_point distributed_now() const;
Austin Schuh39788ff2019-12-01 18:22:57 -0800199
Austin Schuhfaec5e12020-11-05 17:39:55 -0800200 const Configuration *configuration() const {
201 return factory_->configuration();
202 }
203
Austin Schuh58646e22021-08-23 23:51:46 -0700204 // Starts the node up by calling the OnStartup handlers. These get called
205 // every time a node is started.
206
207 // Called when a node has started. This is typically when a log file starts
208 // for a node.
209 void OnStartup(std::function<void()> &&fn);
210
211 // Called when a node shuts down. These get called every time a node is shut
212 // down. All applications are destroyed right after the last OnShutdown
213 // callback is called.
214 void OnShutdown(std::function<void()> &&fn);
215
216 // Starts an application if the configuration says it should be started on
217 // this node. name is the name of the application. args are the constructor
218 // args for the Main class. Returns a pointer to the class that was started
219 // if it was started, or nullptr.
220 template <class Main, class... Args>
Austin Schuh60e77942022-05-16 17:48:24 -0700221 Main *MaybeStart(std::string_view name, Args &&...args);
Austin Schuh58646e22021-08-23 23:51:46 -0700222
223 // Starts an application regardless of if the config says to or not. name is
224 // the name of the application, and args are the constructor args for the
225 // application. Returns a pointer to the class that was started.
226 template <class Main, class... Args>
Austin Schuh60e77942022-05-16 17:48:24 -0700227 Main *AlwaysStart(std::string_view name, Args &&...args);
Austin Schuh58646e22021-08-23 23:51:46 -0700228
Austin Schuh898f4972020-01-11 17:21:25 -0800229 // Returns the simulated network delay for messages forwarded between nodes.
230 std::chrono::nanoseconds network_delay() const {
231 return factory_->network_delay();
232 }
233 // Returns the simulated send delay for all messages sent within a single
234 // node.
235 std::chrono::nanoseconds send_delay() const { return factory_->send_delay(); }
236
Austin Schuh58646e22021-08-23 23:51:46 -0700237 size_t boot_count() const { return scheduler_.boot_count(); }
238
Austin Schuh8bd96322020-02-13 21:18:22 -0800239 // TODO(austin): Private for the following?
240
Austin Schuhac0771c2020-01-07 18:36:30 -0800241 // Converts a time to the distributed clock for scheduling and cross-node time
242 // measurement.
Austin Schuh87dd3832021-01-01 23:07:31 -0800243 // Note: converting time too far in the future can cause problems when
244 // replaying logs. Only convert times in the present or near past.
Austin Schuhac0771c2020-01-07 18:36:30 -0800245 inline distributed_clock::time_point ToDistributedClock(
246 monotonic_clock::time_point time) const;
Austin Schuh58646e22021-08-23 23:51:46 -0700247 inline logger::BootTimestamp FromDistributedClock(
Austin Schuhbe69cf32020-08-27 11:38:33 -0700248 distributed_clock::time_point time) const;
Austin Schuhac0771c2020-01-07 18:36:30 -0800249
Austin Schuh87dd3832021-01-01 23:07:31 -0800250 // Sets the class used to convert time. This pointer must out-live the
251 // SimulatedEventLoopFactory.
252 void SetTimeConverter(TimeConverter *time_converter) {
253 scheduler_.SetTimeConverter(
254 configuration::GetNodeIndex(factory_->configuration(), node_),
255 time_converter);
Austin Schuhcde938c2020-02-02 17:30:07 -0800256 }
257
Austin Schuh20ac95d2020-12-05 17:24:19 -0800258 // Returns the boot UUID for this node.
Austin Schuh58646e22021-08-23 23:51:46 -0700259 const UUID &boot_uuid() {
260 if (boot_uuid_ == UUID::Zero()) {
261 boot_uuid_ = scheduler_.boot_uuid();
262 }
263 return boot_uuid_;
264 }
Austin Schuh20ac95d2020-12-05 17:24:19 -0800265
Austin Schuhc0b0f722020-12-12 18:36:06 -0800266 // Stops forwarding messages to the other node, and reports disconnected in
267 // the ServerStatistics message for this node, and the ClientStatistics for
268 // the other node.
269 void Disconnect(const Node *other);
270 // Resumes forwarding messages.
271 void Connect(const Node *other);
272
Austin Schuh48205e62021-11-12 14:13:18 -0800273 // Disables the messages sent by the simulated message gateway.
274 void DisableStatistics();
275 // Enables the messages sent by the simulated message gateway.
276 void EnableStatistics();
277
Austin Schuhac0771c2020-01-07 18:36:30 -0800278 private:
279 friend class SimulatedEventLoopFactory;
Austin Schuh057d29f2021-08-21 23:05:15 -0700280 NodeEventLoopFactory(EventSchedulerScheduler *scheduler_scheduler,
281 SimulatedEventLoopFactory *factory, const Node *node);
Austin Schuhac0771c2020-01-07 18:36:30 -0800282
Austin Schuh48205e62021-11-12 14:13:18 -0800283 // Skips timing reports on all event loops on this node.
284 void SkipTimingReport();
285
Austin Schuh58646e22021-08-23 23:51:46 -0700286 // Helpers to restart.
287 void ScheduleStartup();
288 void Startup();
289 void Shutdown();
290
Austin Schuh8bd96322020-02-13 21:18:22 -0800291 EventScheduler scheduler_;
Austin Schuhac0771c2020-01-07 18:36:30 -0800292 SimulatedEventLoopFactory *const factory_;
Austin Schuh7d87b672019-12-01 20:23:49 -0800293
Austin Schuh58646e22021-08-23 23:51:46 -0700294 UUID boot_uuid_ = UUID::Zero();
Austin Schuh20ac95d2020-12-05 17:24:19 -0800295
Austin Schuh217a9782019-12-21 23:02:50 -0800296 const Node *const node_;
297
Austin Schuh48205e62021-11-12 14:13:18 -0800298 bool skip_timing_report_ = false;
299
Austin Schuh057d29f2021-08-21 23:05:15 -0700300 std::vector<SimulatedEventLoop *> event_loops_;
Austin Schuhac0771c2020-01-07 18:36:30 -0800301
Austin Schuhac0771c2020-01-07 18:36:30 -0800302 std::chrono::nanoseconds realtime_offset_ = std::chrono::seconds(0);
303
304 // Map from name, type to queue.
305 absl::btree_map<SimpleChannel, std::unique_ptr<SimulatedChannel>> channels_;
306
307 // pid so we get unique timing reports.
Austin Schuh39788ff2019-12-01 18:22:57 -0800308 pid_t tid_ = 0;
Austin Schuh58646e22021-08-23 23:51:46 -0700309
310 // True if we are started.
311 bool started_ = false;
312
313 std::vector<std::function<void()>> pending_on_startup_;
314 std::vector<std::function<void()>> on_startup_;
315 std::vector<std::function<void()>> on_shutdown_;
316
317 // Base class for an application to start. This shouldn't be used directly.
318 struct Application {
319 Application(NodeEventLoopFactory *node_factory, std::string_view name)
320 : event_loop(node_factory->MakeEventLoop(name)) {}
321 virtual ~Application() {}
322
323 std::unique_ptr<EventLoop> event_loop;
324 };
325
326 // Subclass to do type erasure for the base class. Holds an instance of a
327 // specific class. Use SimulationStarter instead.
328 template <typename Main>
329 struct TypedApplication : public Application {
330 // Constructs an Application by delegating the arguments used to construct
331 // the event loop to Application and the rest of the args to the actual
332 // application.
333 template <class... Args>
334 TypedApplication(NodeEventLoopFactory *node_factory, std::string_view name,
Austin Schuh60e77942022-05-16 17:48:24 -0700335 Args &&...args)
Austin Schuh58646e22021-08-23 23:51:46 -0700336 : Application(node_factory, name),
337 main(event_loop.get(), std::forward<Args>(args)...) {
338 VLOG(1) << node_factory->scheduler_.distributed_now() << " "
339 << (node_factory->node() == nullptr
340 ? ""
341 : node_factory->node()->name()->str() + " ")
342 << node_factory->monotonic_now() << " Starting Application \""
343 << name << "\"";
344 }
345 ~TypedApplication() override {}
346
347 Main main;
348 };
349
350 std::vector<std::unique_ptr<Application>> applications_;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700351};
352
Austin Schuh58646e22021-08-23 23:51:46 -0700353template <class Main, class... Args>
Austin Schuh60e77942022-05-16 17:48:24 -0700354Main *NodeEventLoopFactory::MaybeStart(std::string_view name, Args &&...args) {
Austin Schuh58646e22021-08-23 23:51:46 -0700355 const aos::Application *application =
356 configuration::GetApplication(configuration(), node(), name);
357
358 if (application != nullptr) {
359 return AlwaysStart<Main>(name, std::forward<Args>(args)...);
360 }
361 return nullptr;
362}
363
364template <class Main, class... Args>
Austin Schuh60e77942022-05-16 17:48:24 -0700365Main *NodeEventLoopFactory::AlwaysStart(std::string_view name, Args &&...args) {
Austin Schuh58646e22021-08-23 23:51:46 -0700366 std::unique_ptr<TypedApplication<Main>> app =
367 std::make_unique<TypedApplication<Main>>(this, name,
368 std::forward<Args>(args)...);
369 Main *main_ptr = &app->main;
370 applications_.emplace_back(std::move(app));
371 return main_ptr;
372}
373
Austin Schuhac0771c2020-01-07 18:36:30 -0800374inline monotonic_clock::time_point NodeEventLoopFactory::monotonic_now() const {
Austin Schuh8bd96322020-02-13 21:18:22 -0800375 // TODO(austin): Confirm that time never goes backwards?
Austin Schuhbe69cf32020-08-27 11:38:33 -0700376 return scheduler_.monotonic_now();
Austin Schuhac0771c2020-01-07 18:36:30 -0800377}
378
379inline realtime_clock::time_point NodeEventLoopFactory::realtime_now() const {
380 return realtime_clock::time_point(monotonic_now().time_since_epoch() +
381 realtime_offset_);
382}
383
Austin Schuh58646e22021-08-23 23:51:46 -0700384inline distributed_clock::time_point NodeEventLoopFactory::distributed_now()
385 const {
386 return scheduler_.distributed_now();
387}
388
389inline logger::BootTimestamp NodeEventLoopFactory::FromDistributedClock(
Austin Schuhbe69cf32020-08-27 11:38:33 -0700390 distributed_clock::time_point time) const {
391 return scheduler_.FromDistributedClock(time);
392}
393
Austin Schuhac0771c2020-01-07 18:36:30 -0800394inline distributed_clock::time_point NodeEventLoopFactory::ToDistributedClock(
395 monotonic_clock::time_point time) const {
Austin Schuh8bd96322020-02-13 21:18:22 -0800396 return scheduler_.ToDistributedClock(time);
Austin Schuhac0771c2020-01-07 18:36:30 -0800397}
398
Alex Perrycb7da4b2019-08-28 19:35:56 -0700399} // namespace aos
400
401#endif // AOS_EVENTS_SIMULATED_EVENT_LOOP_H_