blob: cc016a8944a6c2cc8bf24e6ac118d214c075fd6d [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 Schuhac0771c2020-01-07 18:36:30 -080067 // Creates an event loop. If running in a multi-node environment, node needs
68 // to point to the node to create this event loop on.
69 ::std::unique_ptr<EventLoop> MakeEventLoop(std::string_view name,
70 const Node *node = nullptr);
71
72 // Returns the NodeEventLoopFactory for the provided node. The returned
73 // NodeEventLoopFactory is owned by the SimulatedEventLoopFactory and has a
74 // lifetime identical to the factory.
75 NodeEventLoopFactory *GetNodeEventLoopFactory(const Node *node);
Austin Schuh057d29f2021-08-21 23:05:15 -070076 NodeEventLoopFactory *GetNodeEventLoopFactory(std::string_view node);
Alex Perrycb7da4b2019-08-28 19:35:56 -070077
Austin Schuh87dd3832021-01-01 23:07:31 -080078 // Sets the time converter for all nodes.
79 void SetTimeConverter(TimeConverter *time_converter);
80
Alex Perrycb7da4b2019-08-28 19:35:56 -070081 // Starts executing the event loops unconditionally.
82 void Run();
83 // Executes the event loops for a duration.
Austin Schuhac0771c2020-01-07 18:36:30 -080084 void RunFor(distributed_clock::duration duration);
Alex Perrycb7da4b2019-08-28 19:35:56 -070085
86 // Stops executing all event loops. Meant to be called from within an event
87 // loop handler.
Austin Schuh8fb315a2020-11-19 22:33:58 -080088 void Exit();
Alex Perrycb7da4b2019-08-28 19:35:56 -070089
Austin Schuhac0771c2020-01-07 18:36:30 -080090 const std::vector<const Node *> &nodes() const { return nodes_; }
91
92 // Sets the simulated send delay for all messages sent within a single node.
Austin Schuh7d87b672019-12-01 20:23:49 -080093 void set_send_delay(std::chrono::nanoseconds send_delay);
Austin Schuhac0771c2020-01-07 18:36:30 -080094 std::chrono::nanoseconds send_delay() const { return send_delay_; }
95
96 // Sets the simulated network delay for messages forwarded between nodes.
Brian Silvermana7c62052020-04-28 16:52:27 -070097 void set_network_delay(std::chrono::nanoseconds network_delay) {
98 network_delay_ = network_delay;
99 }
Austin Schuhac0771c2020-01-07 18:36:30 -0800100 std::chrono::nanoseconds network_delay() const { return network_delay_; }
101
102 // Returns the clock used to synchronize the nodes.
103 distributed_clock::time_point distributed_now() const {
Austin Schuh8bd96322020-02-13 21:18:22 -0800104 return scheduler_scheduler_.distributed_now();
Austin Schuhac0771c2020-01-07 18:36:30 -0800105 }
106
107 // Returns the configuration used for everything.
108 const Configuration *configuration() const { return configuration_; }
109
Austin Schuh6f3babe2020-01-26 20:34:50 -0800110 // Disables forwarding for this channel. This should be used very rarely only
111 // for things like the logger.
112 void DisableForwarding(const Channel *channel);
113
Austin Schuh4c3b9702020-08-30 11:34:55 -0700114 // Disables the messages sent by the simulated message gateway.
115 void DisableStatistics();
116
Austin Schuh2928ebe2021-02-07 22:10:27 -0800117 // Calls SkipTimingReport() on all EventLoops used as part of the
118 // infrastructure. This may improve the performance of long-simulated-duration
119 // tests.
120 void SkipTimingReport();
121
Austin Schuhac0771c2020-01-07 18:36:30 -0800122 private:
Austin Schuhc0b0f722020-12-12 18:36:06 -0800123 friend class NodeEventLoopFactory;
124
Austin Schuhac0771c2020-01-07 18:36:30 -0800125 const Configuration *const configuration_;
Austin Schuh8bd96322020-02-13 21:18:22 -0800126 EventSchedulerScheduler scheduler_scheduler_;
Austin Schuhac0771c2020-01-07 18:36:30 -0800127
128 std::chrono::nanoseconds send_delay_ = std::chrono::microseconds(50);
129 std::chrono::nanoseconds network_delay_ = std::chrono::microseconds(100);
130
131 std::vector<std::unique_ptr<NodeEventLoopFactory>> node_factories_;
132
133 std::vector<const Node *> nodes_;
Austin Schuh898f4972020-01-11 17:21:25 -0800134
135 std::unique_ptr<message_bridge::SimulatedMessageBridge> bridge_;
Austin Schuhac0771c2020-01-07 18:36:30 -0800136};
137
138// This class holds all the state required to be a single node.
139class NodeEventLoopFactory {
140 public:
Austin Schuh057d29f2021-08-21 23:05:15 -0700141 ~NodeEventLoopFactory();
142
143 std::unique_ptr<EventLoop> MakeEventLoop(std::string_view name);
Austin Schuh7d87b672019-12-01 20:23:49 -0800144
Austin Schuh217a9782019-12-21 23:02:50 -0800145 // Returns the node that this factory is running as, or nullptr if this is a
146 // single node setup.
147 const Node *node() const { return node_; }
148
Austin Schuh92547522019-12-28 14:33:43 -0800149 // Sets realtime clock to realtime_now for a given monotonic clock.
150 void SetRealtimeOffset(monotonic_clock::time_point monotonic_now,
151 realtime_clock::time_point realtime_now) {
Austin Schuhac0771c2020-01-07 18:36:30 -0800152 realtime_offset_ =
153 realtime_now.time_since_epoch() - monotonic_now.time_since_epoch();
Austin Schuh92547522019-12-28 14:33:43 -0800154 }
155
Austin Schuhac0771c2020-01-07 18:36:30 -0800156 // Returns the current time on both clocks.
157 inline monotonic_clock::time_point monotonic_now() const;
158 inline realtime_clock::time_point realtime_now() const;
Austin Schuh39788ff2019-12-01 18:22:57 -0800159
Austin Schuhfaec5e12020-11-05 17:39:55 -0800160 const Configuration *configuration() const {
161 return factory_->configuration();
162 }
163
Austin Schuh898f4972020-01-11 17:21:25 -0800164 // Returns the simulated network delay for messages forwarded between nodes.
165 std::chrono::nanoseconds network_delay() const {
166 return factory_->network_delay();
167 }
168 // Returns the simulated send delay for all messages sent within a single
169 // node.
170 std::chrono::nanoseconds send_delay() const { return factory_->send_delay(); }
171
Austin Schuh8bd96322020-02-13 21:18:22 -0800172 // TODO(austin): Private for the following?
173
Austin Schuhac0771c2020-01-07 18:36:30 -0800174 // Converts a time to the distributed clock for scheduling and cross-node time
175 // measurement.
Austin Schuh87dd3832021-01-01 23:07:31 -0800176 // Note: converting time too far in the future can cause problems when
177 // replaying logs. Only convert times in the present or near past.
Austin Schuhac0771c2020-01-07 18:36:30 -0800178 inline distributed_clock::time_point ToDistributedClock(
179 monotonic_clock::time_point time) const;
Austin Schuhbe69cf32020-08-27 11:38:33 -0700180 inline monotonic_clock::time_point FromDistributedClock(
181 distributed_clock::time_point time) const;
Austin Schuhac0771c2020-01-07 18:36:30 -0800182
Austin Schuh87dd3832021-01-01 23:07:31 -0800183 // Sets the class used to convert time. This pointer must out-live the
184 // SimulatedEventLoopFactory.
185 void SetTimeConverter(TimeConverter *time_converter) {
186 scheduler_.SetTimeConverter(
187 configuration::GetNodeIndex(factory_->configuration(), node_),
188 time_converter);
Austin Schuhcde938c2020-02-02 17:30:07 -0800189 }
190
James Kuszmaul4f106fb2021-01-05 20:53:02 -0800191 // Sets the boot UUID for this node. This typically should only be used by
192 // the log reader.
193 void set_boot_uuid(std::string_view uuid) {
194 boot_uuid_ = UUID::FromString(uuid);
195 }
Austin Schuh20ac95d2020-12-05 17:24:19 -0800196 // Returns the boot UUID for this node.
197 const UUID &boot_uuid() const { return boot_uuid_; }
198
199 // Reboots the node. This just resets the boot_uuid_, nothing else.
200 // TODO(austin): This is here for a test case or two, not for general
201 // consumption. The interactions with the rest of the system need to be
202 // worked out better. Don't use this for anything real yet.
203 void Reboot() { boot_uuid_ = UUID::Random(); }
204
Austin Schuhc0b0f722020-12-12 18:36:06 -0800205 // Stops forwarding messages to the other node, and reports disconnected in
206 // the ServerStatistics message for this node, and the ClientStatistics for
207 // the other node.
208 void Disconnect(const Node *other);
209 // Resumes forwarding messages.
210 void Connect(const Node *other);
211
Austin Schuhac0771c2020-01-07 18:36:30 -0800212 private:
213 friend class SimulatedEventLoopFactory;
Austin Schuh057d29f2021-08-21 23:05:15 -0700214 NodeEventLoopFactory(EventSchedulerScheduler *scheduler_scheduler,
215 SimulatedEventLoopFactory *factory, const Node *node);
Austin Schuhac0771c2020-01-07 18:36:30 -0800216
Austin Schuh8bd96322020-02-13 21:18:22 -0800217 EventScheduler scheduler_;
Austin Schuhac0771c2020-01-07 18:36:30 -0800218 SimulatedEventLoopFactory *const factory_;
Austin Schuh7d87b672019-12-01 20:23:49 -0800219
Austin Schuh20ac95d2020-12-05 17:24:19 -0800220 UUID boot_uuid_ = UUID::Random();
221
Austin Schuh217a9782019-12-21 23:02:50 -0800222 const Node *const node_;
223
Austin Schuh057d29f2021-08-21 23:05:15 -0700224 std::vector<SimulatedEventLoop *> event_loops_;
Austin Schuhac0771c2020-01-07 18:36:30 -0800225
Austin Schuhac0771c2020-01-07 18:36:30 -0800226 std::chrono::nanoseconds realtime_offset_ = std::chrono::seconds(0);
227
228 // Map from name, type to queue.
229 absl::btree_map<SimpleChannel, std::unique_ptr<SimulatedChannel>> channels_;
230
231 // pid so we get unique timing reports.
Austin Schuh39788ff2019-12-01 18:22:57 -0800232 pid_t tid_ = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700233};
234
Austin Schuhac0771c2020-01-07 18:36:30 -0800235inline monotonic_clock::time_point NodeEventLoopFactory::monotonic_now() const {
Austin Schuh8bd96322020-02-13 21:18:22 -0800236 // TODO(austin): Confirm that time never goes backwards?
Austin Schuhbe69cf32020-08-27 11:38:33 -0700237 return scheduler_.monotonic_now();
Austin Schuhac0771c2020-01-07 18:36:30 -0800238}
239
240inline realtime_clock::time_point NodeEventLoopFactory::realtime_now() const {
241 return realtime_clock::time_point(monotonic_now().time_since_epoch() +
242 realtime_offset_);
243}
244
Austin Schuhbe69cf32020-08-27 11:38:33 -0700245inline monotonic_clock::time_point NodeEventLoopFactory::FromDistributedClock(
246 distributed_clock::time_point time) const {
247 return scheduler_.FromDistributedClock(time);
248}
249
Austin Schuhac0771c2020-01-07 18:36:30 -0800250inline distributed_clock::time_point NodeEventLoopFactory::ToDistributedClock(
251 monotonic_clock::time_point time) const {
Austin Schuh8bd96322020-02-13 21:18:22 -0800252 return scheduler_.ToDistributedClock(time);
Austin Schuhac0771c2020-01-07 18:36:30 -0800253}
254
Alex Perrycb7da4b2019-08-28 19:35:56 -0700255} // namespace aos
256
257#endif // AOS_EVENTS_SIMULATED_EVENT_LOOP_H_