blob: 9dd3d1f7c53d3942d913c4ff57be7dc28c299f31 [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>
5#include <map>
6#include <memory>
Austin Schuh5f1cc5c2019-12-01 18:01:11 -08007#include <string_view>
Alex Perrycb7da4b2019-08-28 19:35:56 -07008#include <unordered_set>
9#include <utility>
10#include <vector>
11
12#include "absl/container/btree_map.h"
13#include "aos/events/event_loop.h"
14#include "aos/events/event_scheduler.h"
Austin Schuhe1dafe42020-01-06 21:12:03 -080015#include "aos/events/simple_channel.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070016#include "aos/flatbuffer_merge.h"
17#include "aos/flatbuffers.h"
18#include "aos/ipc_lib/index.h"
19#include "glog/logging.h"
20
21namespace aos {
22
23// Class for simulated fetchers.
24class SimulatedChannel;
25
Austin Schuhac0771c2020-01-07 18:36:30 -080026class NodeEventLoopFactory;
Austin Schuh898f4972020-01-11 17:21:25 -080027namespace message_bridge {
28class SimulatedMessageBridge;
29}
Austin Schuhac0771c2020-01-07 18:36:30 -080030
31// There are 2 concepts needed to support multi-node simulations.
32// 1) The node. This is implemented with NodeEventLoopFactory.
33// 2) The "robot" which runs multiple nodes. This is implemented with
34// SimulatedEventLoopFactory.
35//
36// To make things easier, SimulatedEventLoopFactory takes an optional Node
37// argument if you want to make event loops without interacting with the
38// NodeEventLoopFactory object.
39//
40// The basic flow goes something like as follows:
41//
42// SimulatedEventLoopFactory factory(config);
43// const Node *pi1 = configuration::GetNode(factory.configuration(), "pi1");
44// std::unique_ptr<EventLoop> event_loop = factory.MakeEventLoop("ping", pi1);
45//
46// Or
47//
48// SimulatedEventLoopFactory factory(config);
49// const Node *pi1 = configuration::GetNode(factory.configuration(), "pi1");
50// NodeEventLoopFactory *pi1_factory = factory.GetNodeEventLoopFactory(pi1);
51// std::unique_ptr<EventLoop> event_loop = pi1_factory.MakeEventLoop("ping");
52//
53// The distributed_clock is used to be the base time. NodeEventLoopFactory has
54// all the information needed to adjust both the realtime and monotonic clocks
55// relative to the distributed_clock.
Alex Perrycb7da4b2019-08-28 19:35:56 -070056class SimulatedEventLoopFactory {
57 public:
58 // Constructs a SimulatedEventLoopFactory with the provided configuration.
59 // This configuration must remain in scope for the lifetime of the factory and
60 // all sub-objects.
61 SimulatedEventLoopFactory(const Configuration *configuration);
62 ~SimulatedEventLoopFactory();
63
Austin Schuhac0771c2020-01-07 18:36:30 -080064 // Creates an event loop. If running in a multi-node environment, node needs
65 // to point to the node to create this event loop on.
66 ::std::unique_ptr<EventLoop> MakeEventLoop(std::string_view name,
67 const Node *node = nullptr);
68
69 // Returns the NodeEventLoopFactory for the provided node. The returned
70 // NodeEventLoopFactory is owned by the SimulatedEventLoopFactory and has a
71 // lifetime identical to the factory.
72 NodeEventLoopFactory *GetNodeEventLoopFactory(const Node *node);
Alex Perrycb7da4b2019-08-28 19:35:56 -070073
74 // Starts executing the event loops unconditionally.
75 void Run();
76 // Executes the event loops for a duration.
Austin Schuhac0771c2020-01-07 18:36:30 -080077 void RunFor(distributed_clock::duration duration);
Alex Perrycb7da4b2019-08-28 19:35:56 -070078
79 // Stops executing all event loops. Meant to be called from within an event
80 // loop handler.
81 void Exit() { scheduler_.Exit(); }
82
Austin Schuhac0771c2020-01-07 18:36:30 -080083 const std::vector<const Node *> &nodes() const { return nodes_; }
84
85 // Sets the simulated send delay for all messages sent within a single node.
Austin Schuh7d87b672019-12-01 20:23:49 -080086 void set_send_delay(std::chrono::nanoseconds send_delay);
Austin Schuhac0771c2020-01-07 18:36:30 -080087 std::chrono::nanoseconds send_delay() const { return send_delay_; }
88
89 // Sets the simulated network delay for messages forwarded between nodes.
90 void set_network_delay(std::chrono::nanoseconds network_delay);
91 std::chrono::nanoseconds network_delay() const { return network_delay_; }
92
93 // Returns the clock used to synchronize the nodes.
94 distributed_clock::time_point distributed_now() const {
95 return scheduler_.distributed_now();
96 }
97
98 // Returns the configuration used for everything.
99 const Configuration *configuration() const { return configuration_; }
100
Austin Schuh6f3babe2020-01-26 20:34:50 -0800101 // Disables forwarding for this channel. This should be used very rarely only
102 // for things like the logger.
103 void DisableForwarding(const Channel *channel);
104
Austin Schuhac0771c2020-01-07 18:36:30 -0800105 private:
106 const Configuration *const configuration_;
107 EventScheduler scheduler_;
108 // List of event loops to manage running and not running for.
109 // The function is a callback used to set and clear the running bool on each
110 // event loop.
111 std::vector<std::pair<EventLoop *, std::function<void(bool)>>>
112 raw_event_loops_;
113
114 std::chrono::nanoseconds send_delay_ = std::chrono::microseconds(50);
115 std::chrono::nanoseconds network_delay_ = std::chrono::microseconds(100);
116
117 std::vector<std::unique_ptr<NodeEventLoopFactory>> node_factories_;
118
119 std::vector<const Node *> nodes_;
Austin Schuh898f4972020-01-11 17:21:25 -0800120
121 std::unique_ptr<message_bridge::SimulatedMessageBridge> bridge_;
Austin Schuhac0771c2020-01-07 18:36:30 -0800122};
123
124// This class holds all the state required to be a single node.
125class NodeEventLoopFactory {
126 public:
127 ::std::unique_ptr<EventLoop> MakeEventLoop(std::string_view name);
Austin Schuh7d87b672019-12-01 20:23:49 -0800128
Austin Schuh217a9782019-12-21 23:02:50 -0800129 // Returns the node that this factory is running as, or nullptr if this is a
130 // single node setup.
131 const Node *node() const { return node_; }
132
Austin Schuh92547522019-12-28 14:33:43 -0800133 // Sets realtime clock to realtime_now for a given monotonic clock.
134 void SetRealtimeOffset(monotonic_clock::time_point monotonic_now,
135 realtime_clock::time_point realtime_now) {
Austin Schuhac0771c2020-01-07 18:36:30 -0800136 realtime_offset_ =
137 realtime_now.time_since_epoch() - monotonic_now.time_since_epoch();
Austin Schuh92547522019-12-28 14:33:43 -0800138 }
139
Austin Schuhac0771c2020-01-07 18:36:30 -0800140 // Returns the current time on both clocks.
141 inline monotonic_clock::time_point monotonic_now() const;
142 inline realtime_clock::time_point realtime_now() const;
Austin Schuh39788ff2019-12-01 18:22:57 -0800143
Austin Schuh898f4972020-01-11 17:21:25 -0800144 // Returns the simulated network delay for messages forwarded between nodes.
145 std::chrono::nanoseconds network_delay() const {
146 return factory_->network_delay();
147 }
148 // Returns the simulated send delay for all messages sent within a single
149 // node.
150 std::chrono::nanoseconds send_delay() const { return factory_->send_delay(); }
151
Austin Schuhac0771c2020-01-07 18:36:30 -0800152 // Converts a time to the distributed clock for scheduling and cross-node time
153 // measurement.
154 inline distributed_clock::time_point ToDistributedClock(
155 monotonic_clock::time_point time) const;
156
Austin Schuhcde938c2020-02-02 17:30:07 -0800157 // Note: use this very very carefully. It can cause massive problems. This
158 // needs to go away as we properly handle time drifting between nodes.
159 void SetMonotonicNow(monotonic_clock::time_point monotonic_now) {
160 monotonic_clock::duration offset = (monotonic_now - this->monotonic_now());
161 monotonic_offset_ += offset;
162 realtime_offset_ -= offset;
163 }
164
Austin Schuhac0771c2020-01-07 18:36:30 -0800165 private:
166 friend class SimulatedEventLoopFactory;
167 NodeEventLoopFactory(
168 EventScheduler *scheduler, SimulatedEventLoopFactory *factory,
169 const Node *node,
170 std::vector<std::pair<EventLoop *, std::function<void(bool)>>>
171 *raw_event_loops);
172
173 EventScheduler *const scheduler_;
174 SimulatedEventLoopFactory *const factory_;
Austin Schuh7d87b672019-12-01 20:23:49 -0800175
Austin Schuh217a9782019-12-21 23:02:50 -0800176 const Node *const node_;
177
Austin Schuhac0771c2020-01-07 18:36:30 -0800178 std::vector<std::pair<EventLoop *, std::function<void(bool)>>>
179 *const raw_event_loops_;
180
181 std::chrono::nanoseconds monotonic_offset_ = std::chrono::seconds(0);
182 std::chrono::nanoseconds realtime_offset_ = std::chrono::seconds(0);
183
184 // Map from name, type to queue.
185 absl::btree_map<SimpleChannel, std::unique_ptr<SimulatedChannel>> channels_;
186
187 // pid so we get unique timing reports.
Austin Schuh39788ff2019-12-01 18:22:57 -0800188 pid_t tid_ = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700189};
190
Austin Schuhac0771c2020-01-07 18:36:30 -0800191inline monotonic_clock::time_point NodeEventLoopFactory::monotonic_now() const {
192 return monotonic_clock::time_point(
193 factory_->distributed_now().time_since_epoch() + monotonic_offset_);
194}
195
196inline realtime_clock::time_point NodeEventLoopFactory::realtime_now() const {
197 return realtime_clock::time_point(monotonic_now().time_since_epoch() +
198 realtime_offset_);
199}
200
201inline distributed_clock::time_point NodeEventLoopFactory::ToDistributedClock(
202 monotonic_clock::time_point time) const {
203 return distributed_clock::time_point(time.time_since_epoch() -
204 monotonic_offset_);
205}
206
Alex Perrycb7da4b2019-08-28 19:35:56 -0700207} // namespace aos
208
209#endif // AOS_EVENTS_SIMULATED_EVENT_LOOP_H_