blob: bd589d60fe3a2de245c92090d8ada588e91b17bc [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;
Austin Schuh898f4972020-01-11 17:21:25 -080030namespace message_bridge {
31class SimulatedMessageBridge;
32}
Austin Schuhac0771c2020-01-07 18:36:30 -080033
34// There are 2 concepts needed to support multi-node simulations.
35// 1) The node. This is implemented with NodeEventLoopFactory.
36// 2) The "robot" which runs multiple nodes. This is implemented with
37// SimulatedEventLoopFactory.
38//
39// To make things easier, SimulatedEventLoopFactory takes an optional Node
40// argument if you want to make event loops without interacting with the
41// NodeEventLoopFactory object.
42//
43// The basic flow goes something like as follows:
44//
45// SimulatedEventLoopFactory factory(config);
46// const Node *pi1 = configuration::GetNode(factory.configuration(), "pi1");
47// std::unique_ptr<EventLoop> event_loop = factory.MakeEventLoop("ping", pi1);
48//
49// Or
50//
51// SimulatedEventLoopFactory factory(config);
52// const Node *pi1 = configuration::GetNode(factory.configuration(), "pi1");
53// NodeEventLoopFactory *pi1_factory = factory.GetNodeEventLoopFactory(pi1);
54// std::unique_ptr<EventLoop> event_loop = pi1_factory.MakeEventLoop("ping");
55//
56// The distributed_clock is used to be the base time. NodeEventLoopFactory has
57// all the information needed to adjust both the realtime and monotonic clocks
58// relative to the distributed_clock.
Alex Perrycb7da4b2019-08-28 19:35:56 -070059class SimulatedEventLoopFactory {
60 public:
61 // Constructs a SimulatedEventLoopFactory with the provided configuration.
62 // This configuration must remain in scope for the lifetime of the factory and
63 // all sub-objects.
64 SimulatedEventLoopFactory(const Configuration *configuration);
65 ~SimulatedEventLoopFactory();
66
Austin Schuh58646e22021-08-23 23:51:46 -070067 SimulatedEventLoopFactory(const SimulatedEventLoopFactory &) = delete;
68 SimulatedEventLoopFactory &operator=(const SimulatedEventLoopFactory &) =
69 delete;
70 SimulatedEventLoopFactory(SimulatedEventLoopFactory &&) = delete;
71 SimulatedEventLoopFactory &operator=(SimulatedEventLoopFactory &&) = delete;
72
Austin Schuhac0771c2020-01-07 18:36:30 -080073 // Creates an event loop. If running in a multi-node environment, node needs
74 // to point to the node to create this event loop on.
75 ::std::unique_ptr<EventLoop> MakeEventLoop(std::string_view name,
76 const Node *node = nullptr);
77
78 // Returns the NodeEventLoopFactory for the provided node. The returned
79 // NodeEventLoopFactory is owned by the SimulatedEventLoopFactory and has a
80 // lifetime identical to the factory.
81 NodeEventLoopFactory *GetNodeEventLoopFactory(const Node *node);
Austin Schuh057d29f2021-08-21 23:05:15 -070082 NodeEventLoopFactory *GetNodeEventLoopFactory(std::string_view node);
Alex Perrycb7da4b2019-08-28 19:35:56 -070083
Austin Schuh87dd3832021-01-01 23:07:31 -080084 // Sets the time converter for all nodes.
85 void SetTimeConverter(TimeConverter *time_converter);
86
Austin Schuh58646e22021-08-23 23:51:46 -070087 // Starts executing the event loops unconditionally until Exit is called or
88 // all the nodes have shut down.
Alex Perrycb7da4b2019-08-28 19:35:56 -070089 void Run();
90 // Executes the event loops for a duration.
Austin Schuhac0771c2020-01-07 18:36:30 -080091 void RunFor(distributed_clock::duration duration);
Alex Perrycb7da4b2019-08-28 19:35:56 -070092
93 // Stops executing all event loops. Meant to be called from within an event
94 // loop handler.
Austin Schuh8fb315a2020-11-19 22:33:58 -080095 void Exit();
Alex Perrycb7da4b2019-08-28 19:35:56 -070096
Austin Schuhac0771c2020-01-07 18:36:30 -080097 const std::vector<const Node *> &nodes() const { return nodes_; }
98
99 // Sets the simulated send delay for all messages sent within a single node.
Austin Schuh7d87b672019-12-01 20:23:49 -0800100 void set_send_delay(std::chrono::nanoseconds send_delay);
Austin Schuhac0771c2020-01-07 18:36:30 -0800101 std::chrono::nanoseconds send_delay() const { return send_delay_; }
102
103 // Sets the simulated network delay for messages forwarded between nodes.
Brian Silvermana7c62052020-04-28 16:52:27 -0700104 void set_network_delay(std::chrono::nanoseconds network_delay) {
105 network_delay_ = network_delay;
106 }
Austin Schuhac0771c2020-01-07 18:36:30 -0800107 std::chrono::nanoseconds network_delay() const { return network_delay_; }
108
109 // Returns the clock used to synchronize the nodes.
110 distributed_clock::time_point distributed_now() const {
Austin Schuh8bd96322020-02-13 21:18:22 -0800111 return scheduler_scheduler_.distributed_now();
Austin Schuhac0771c2020-01-07 18:36:30 -0800112 }
113
114 // Returns the configuration used for everything.
115 const Configuration *configuration() const { return configuration_; }
116
Austin Schuh6f3babe2020-01-26 20:34:50 -0800117 // Disables forwarding for this channel. This should be used very rarely only
118 // for things like the logger.
119 void DisableForwarding(const Channel *channel);
120
Austin Schuh4c3b9702020-08-30 11:34:55 -0700121 // Disables the messages sent by the simulated message gateway.
122 void DisableStatistics();
Austin Schuh48205e62021-11-12 14:13:18 -0800123 // Enables the messages sent by the simulated message gateway.
124 void EnableStatistics();
Austin Schuh4c3b9702020-08-30 11:34:55 -0700125
Austin Schuh2928ebe2021-02-07 22:10:27 -0800126 // Calls SkipTimingReport() on all EventLoops used as part of the
127 // infrastructure. This may improve the performance of long-simulated-duration
128 // tests.
129 void SkipTimingReport();
130
Austin Schuhac0771c2020-01-07 18:36:30 -0800131 private:
Austin Schuhc0b0f722020-12-12 18:36:06 -0800132 friend class NodeEventLoopFactory;
133
Austin Schuhac0771c2020-01-07 18:36:30 -0800134 const Configuration *const configuration_;
Austin Schuh8bd96322020-02-13 21:18:22 -0800135 EventSchedulerScheduler scheduler_scheduler_;
Austin Schuhac0771c2020-01-07 18:36:30 -0800136
137 std::chrono::nanoseconds send_delay_ = std::chrono::microseconds(50);
138 std::chrono::nanoseconds network_delay_ = std::chrono::microseconds(100);
139
Austin Schuh58646e22021-08-23 23:51:46 -0700140 std::unique_ptr<message_bridge::SimulatedMessageBridge> bridge_;
141
Austin Schuhac0771c2020-01-07 18:36:30 -0800142 std::vector<std::unique_ptr<NodeEventLoopFactory>> node_factories_;
143
144 std::vector<const Node *> nodes_;
145};
146
147// This class holds all the state required to be a single node.
148class NodeEventLoopFactory {
149 public:
Austin Schuh057d29f2021-08-21 23:05:15 -0700150 ~NodeEventLoopFactory();
151
152 std::unique_ptr<EventLoop> MakeEventLoop(std::string_view name);
Austin Schuh7d87b672019-12-01 20:23:49 -0800153
Austin Schuh217a9782019-12-21 23:02:50 -0800154 // Returns the node that this factory is running as, or nullptr if this is a
155 // single node setup.
156 const Node *node() const { return node_; }
157
Austin Schuh92547522019-12-28 14:33:43 -0800158 // Sets realtime clock to realtime_now for a given monotonic clock.
159 void SetRealtimeOffset(monotonic_clock::time_point monotonic_now,
160 realtime_clock::time_point realtime_now) {
Austin Schuhac0771c2020-01-07 18:36:30 -0800161 realtime_offset_ =
162 realtime_now.time_since_epoch() - monotonic_now.time_since_epoch();
Austin Schuh92547522019-12-28 14:33:43 -0800163 }
164
Austin Schuhac0771c2020-01-07 18:36:30 -0800165 // Returns the current time on both clocks.
166 inline monotonic_clock::time_point monotonic_now() const;
167 inline realtime_clock::time_point realtime_now() const;
Austin Schuh58646e22021-08-23 23:51:46 -0700168 inline distributed_clock::time_point distributed_now() const;
Austin Schuh39788ff2019-12-01 18:22:57 -0800169
Austin Schuhfaec5e12020-11-05 17:39:55 -0800170 const Configuration *configuration() const {
171 return factory_->configuration();
172 }
173
Austin Schuh58646e22021-08-23 23:51:46 -0700174 // Starts the node up by calling the OnStartup handlers. These get called
175 // every time a node is started.
176
177 // Called when a node has started. This is typically when a log file starts
178 // for a node.
179 void OnStartup(std::function<void()> &&fn);
180
181 // Called when a node shuts down. These get called every time a node is shut
182 // down. All applications are destroyed right after the last OnShutdown
183 // callback is called.
184 void OnShutdown(std::function<void()> &&fn);
185
186 // Starts an application if the configuration says it should be started on
187 // this node. name is the name of the application. args are the constructor
188 // args for the Main class. Returns a pointer to the class that was started
189 // if it was started, or nullptr.
190 template <class Main, class... Args>
191 Main *MaybeStart(std::string_view name, Args &&... args);
192
193 // Starts an application regardless of if the config says to or not. name is
194 // the name of the application, and args are the constructor args for the
195 // application. Returns a pointer to the class that was started.
196 template <class Main, class... Args>
197 Main *AlwaysStart(std::string_view name, Args &&... args);
198
Austin Schuh898f4972020-01-11 17:21:25 -0800199 // Returns the simulated network delay for messages forwarded between nodes.
200 std::chrono::nanoseconds network_delay() const {
201 return factory_->network_delay();
202 }
203 // Returns the simulated send delay for all messages sent within a single
204 // node.
205 std::chrono::nanoseconds send_delay() const { return factory_->send_delay(); }
206
Austin Schuh58646e22021-08-23 23:51:46 -0700207 size_t boot_count() const { return scheduler_.boot_count(); }
208
Austin Schuh8bd96322020-02-13 21:18:22 -0800209 // TODO(austin): Private for the following?
210
Austin Schuhac0771c2020-01-07 18:36:30 -0800211 // Converts a time to the distributed clock for scheduling and cross-node time
212 // measurement.
Austin Schuh87dd3832021-01-01 23:07:31 -0800213 // Note: converting time too far in the future can cause problems when
214 // replaying logs. Only convert times in the present or near past.
Austin Schuhac0771c2020-01-07 18:36:30 -0800215 inline distributed_clock::time_point ToDistributedClock(
216 monotonic_clock::time_point time) const;
Austin Schuh58646e22021-08-23 23:51:46 -0700217 inline logger::BootTimestamp FromDistributedClock(
Austin Schuhbe69cf32020-08-27 11:38:33 -0700218 distributed_clock::time_point time) const;
Austin Schuhac0771c2020-01-07 18:36:30 -0800219
Austin Schuh87dd3832021-01-01 23:07:31 -0800220 // Sets the class used to convert time. This pointer must out-live the
221 // SimulatedEventLoopFactory.
222 void SetTimeConverter(TimeConverter *time_converter) {
223 scheduler_.SetTimeConverter(
224 configuration::GetNodeIndex(factory_->configuration(), node_),
225 time_converter);
Austin Schuhcde938c2020-02-02 17:30:07 -0800226 }
227
Austin Schuh20ac95d2020-12-05 17:24:19 -0800228 // Returns the boot UUID for this node.
Austin Schuh58646e22021-08-23 23:51:46 -0700229 const UUID &boot_uuid() {
230 if (boot_uuid_ == UUID::Zero()) {
231 boot_uuid_ = scheduler_.boot_uuid();
232 }
233 return boot_uuid_;
234 }
Austin Schuh20ac95d2020-12-05 17:24:19 -0800235
Austin Schuhc0b0f722020-12-12 18:36:06 -0800236 // Stops forwarding messages to the other node, and reports disconnected in
237 // the ServerStatistics message for this node, and the ClientStatistics for
238 // the other node.
239 void Disconnect(const Node *other);
240 // Resumes forwarding messages.
241 void Connect(const Node *other);
242
Austin Schuh48205e62021-11-12 14:13:18 -0800243 // Disables the messages sent by the simulated message gateway.
244 void DisableStatistics();
245 // Enables the messages sent by the simulated message gateway.
246 void EnableStatistics();
247
Austin Schuhac0771c2020-01-07 18:36:30 -0800248 private:
249 friend class SimulatedEventLoopFactory;
Austin Schuh057d29f2021-08-21 23:05:15 -0700250 NodeEventLoopFactory(EventSchedulerScheduler *scheduler_scheduler,
251 SimulatedEventLoopFactory *factory, const Node *node);
Austin Schuhac0771c2020-01-07 18:36:30 -0800252
Austin Schuh48205e62021-11-12 14:13:18 -0800253 // Skips timing reports on all event loops on this node.
254 void SkipTimingReport();
255
Austin Schuh58646e22021-08-23 23:51:46 -0700256 // Helpers to restart.
257 void ScheduleStartup();
258 void Startup();
259 void Shutdown();
260
Austin Schuh8bd96322020-02-13 21:18:22 -0800261 EventScheduler scheduler_;
Austin Schuhac0771c2020-01-07 18:36:30 -0800262 SimulatedEventLoopFactory *const factory_;
Austin Schuh7d87b672019-12-01 20:23:49 -0800263
Austin Schuh58646e22021-08-23 23:51:46 -0700264 UUID boot_uuid_ = UUID::Zero();
Austin Schuh20ac95d2020-12-05 17:24:19 -0800265
Austin Schuh217a9782019-12-21 23:02:50 -0800266 const Node *const node_;
267
Austin Schuh48205e62021-11-12 14:13:18 -0800268 bool skip_timing_report_ = false;
269
Austin Schuh057d29f2021-08-21 23:05:15 -0700270 std::vector<SimulatedEventLoop *> event_loops_;
Austin Schuhac0771c2020-01-07 18:36:30 -0800271
Austin Schuhac0771c2020-01-07 18:36:30 -0800272 std::chrono::nanoseconds realtime_offset_ = std::chrono::seconds(0);
273
274 // Map from name, type to queue.
275 absl::btree_map<SimpleChannel, std::unique_ptr<SimulatedChannel>> channels_;
276
277 // pid so we get unique timing reports.
Austin Schuh39788ff2019-12-01 18:22:57 -0800278 pid_t tid_ = 0;
Austin Schuh58646e22021-08-23 23:51:46 -0700279
280 // True if we are started.
281 bool started_ = false;
282
283 std::vector<std::function<void()>> pending_on_startup_;
284 std::vector<std::function<void()>> on_startup_;
285 std::vector<std::function<void()>> on_shutdown_;
286
287 // Base class for an application to start. This shouldn't be used directly.
288 struct Application {
289 Application(NodeEventLoopFactory *node_factory, std::string_view name)
290 : event_loop(node_factory->MakeEventLoop(name)) {}
291 virtual ~Application() {}
292
293 std::unique_ptr<EventLoop> event_loop;
294 };
295
296 // Subclass to do type erasure for the base class. Holds an instance of a
297 // specific class. Use SimulationStarter instead.
298 template <typename Main>
299 struct TypedApplication : public Application {
300 // Constructs an Application by delegating the arguments used to construct
301 // the event loop to Application and the rest of the args to the actual
302 // application.
303 template <class... Args>
304 TypedApplication(NodeEventLoopFactory *node_factory, std::string_view name,
305 Args &&... args)
306 : Application(node_factory, name),
307 main(event_loop.get(), std::forward<Args>(args)...) {
308 VLOG(1) << node_factory->scheduler_.distributed_now() << " "
309 << (node_factory->node() == nullptr
310 ? ""
311 : node_factory->node()->name()->str() + " ")
312 << node_factory->monotonic_now() << " Starting Application \""
313 << name << "\"";
314 }
315 ~TypedApplication() override {}
316
317 Main main;
318 };
319
320 std::vector<std::unique_ptr<Application>> applications_;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700321};
322
Austin Schuh58646e22021-08-23 23:51:46 -0700323template <class Main, class... Args>
324Main *NodeEventLoopFactory::MaybeStart(std::string_view name, Args &&... args) {
325 const aos::Application *application =
326 configuration::GetApplication(configuration(), node(), name);
327
328 if (application != nullptr) {
329 return AlwaysStart<Main>(name, std::forward<Args>(args)...);
330 }
331 return nullptr;
332}
333
334template <class Main, class... Args>
335Main *NodeEventLoopFactory::AlwaysStart(std::string_view name,
336 Args &&... args) {
337 std::unique_ptr<TypedApplication<Main>> app =
338 std::make_unique<TypedApplication<Main>>(this, name,
339 std::forward<Args>(args)...);
340 Main *main_ptr = &app->main;
341 applications_.emplace_back(std::move(app));
342 return main_ptr;
343}
344
Austin Schuhac0771c2020-01-07 18:36:30 -0800345inline monotonic_clock::time_point NodeEventLoopFactory::monotonic_now() const {
Austin Schuh8bd96322020-02-13 21:18:22 -0800346 // TODO(austin): Confirm that time never goes backwards?
Austin Schuhbe69cf32020-08-27 11:38:33 -0700347 return scheduler_.monotonic_now();
Austin Schuhac0771c2020-01-07 18:36:30 -0800348}
349
350inline realtime_clock::time_point NodeEventLoopFactory::realtime_now() const {
351 return realtime_clock::time_point(monotonic_now().time_since_epoch() +
352 realtime_offset_);
353}
354
Austin Schuh58646e22021-08-23 23:51:46 -0700355inline distributed_clock::time_point NodeEventLoopFactory::distributed_now()
356 const {
357 return scheduler_.distributed_now();
358}
359
360inline logger::BootTimestamp NodeEventLoopFactory::FromDistributedClock(
Austin Schuhbe69cf32020-08-27 11:38:33 -0700361 distributed_clock::time_point time) const {
362 return scheduler_.FromDistributedClock(time);
363}
364
Austin Schuhac0771c2020-01-07 18:36:30 -0800365inline distributed_clock::time_point NodeEventLoopFactory::ToDistributedClock(
366 monotonic_clock::time_point time) const {
Austin Schuh8bd96322020-02-13 21:18:22 -0800367 return scheduler_.ToDistributedClock(time);
Austin Schuhac0771c2020-01-07 18:36:30 -0800368}
369
Alex Perrycb7da4b2019-08-28 19:35:56 -0700370} // namespace aos
371
372#endif // AOS_EVENTS_SIMULATED_EVENT_LOOP_H_