blob: 03b6b5aac731885615ab00b6f001210291304f22 [file] [log] [blame]
Neil Balchc8f41ed2018-01-20 22:06:53 -08001#ifndef _AOS_EVENTS_SIMULATED_EVENT_LOOP_H_
2#define _AOS_EVENTS_SIMULATED_EVENT_LOOP_H_
3
Austin Schuh44019f92019-05-19 19:58:27 -07004#include <algorithm>
Neil Balchc8f41ed2018-01-20 22:06:53 -08005#include <map>
6#include <memory>
7#include <unordered_set>
8#include <utility>
9#include <vector>
10
11#include "aos/events/event-loop.h"
12
13namespace aos {
14
James Kuszmaulcd1db352019-05-26 16:42:29 -070015// This class manages allocation of queue messages for simulation.
16// Unfortunately, because the current interfaces all assume that we pass around
17// raw pointers to messages we can't use a std::shared_ptr or the such, and
18// because aos::Message's themselves to not have any sort of built-in support
19// for this, we need to manage memory for the Messages in some custom fashion.
20// In this case, we do so by allocating a ref-counter in the bytes immediately
21// preceding the aos::Message. We then provide a constructor that takes just a
22// pointer to an existing message and we assume that it was allocated using this
23// class, and can decrement the counter if the RefCountedBuffer we constructed
24// goes out of scope. There are currently no checks to ensure that pointers
25// passed into this class were actually allocated using this class.
Neil Balchc8f41ed2018-01-20 22:06:53 -080026class RefCountedBuffer {
27 public:
28 RefCountedBuffer() {}
29 ~RefCountedBuffer() { clear(); }
30
James Kuszmaulcd1db352019-05-26 16:42:29 -070031 // Create a RefCountedBuffer for some Message that was already allocated using
32 // a RefCountedBuffer class. This, or some function like it, is required to
33 // allow us to let users of the simulated event loops work with raw pointers
34 // to messages.
Neil Balchc8f41ed2018-01-20 22:06:53 -080035 explicit RefCountedBuffer(aos::Message *data) : data_(data) {}
36
James Kuszmaulcd1db352019-05-26 16:42:29 -070037 // Allocates memory for a new message of a given size. Does not initialize the
38 // memory or call any constructors.
Neil Balchc8f41ed2018-01-20 22:06:53 -080039 explicit RefCountedBuffer(size_t size) {
40 data_ = reinterpret_cast<uint8_t *>(malloc(kRefCountSize + size)) +
41 kRefCountSize;
42 // Initialize the allocated memory with an integer
43 *GetRefCount() = 1;
44 }
45
46 RefCountedBuffer(const RefCountedBuffer &other) {
47 data_ = other.data_;
48 ++*GetRefCount();
49 }
50
51 RefCountedBuffer(RefCountedBuffer &&other) { std::swap(data_, other.data_); }
52
53 RefCountedBuffer &operator=(const RefCountedBuffer &other) {
54 if (this == &other) return *this;
55 clear();
56 data_ = other.data_;
57 ++*GetRefCount();
58 return *this;
59 }
60
61 RefCountedBuffer &operator=(RefCountedBuffer &&other) {
62 if (this == &other) return *this;
63 std::swap(data_, other.data_);
64 return *this;
65 }
66
67 aos::Message *get() const { return static_cast<aos::Message *>(data_); }
68
69 aos::Message *release() {
70 auto tmp = get();
71 data_ = nullptr;
72 return tmp;
73 }
74
75 void clear() {
76 if (data_ != nullptr) {
77 if (--*GetRefCount() == 0) {
78 // Free memory block from the start of the allocated block
79 free(GetRefCount());
80 }
81 data_ = nullptr;
82 }
83 }
84
85 private:
86 void *data_ = nullptr;
87 // Qty. memory to be allocated to the ref counter
88 static constexpr size_t kRefCountSize = sizeof(int64_t);
89
90 int64_t *GetRefCount() {
91 // Need to cast the void* to an 8 bit long object (size of void* is
92 // technically 0)
93 return reinterpret_cast<int64_t *>(static_cast<void *>(
94 reinterpret_cast<uint8_t *>(data_) - kRefCountSize));
95 }
96};
97
98class EventScheduler {
99 public:
100 using QueueType = ::std::multimap<::aos::monotonic_clock::time_point,
101 ::std::function<void()>>;
102 using Token = QueueType::iterator;
103
104 // Schedule an event with a callback function
105 // Returns an iterator to the event
106 Token Schedule(::aos::monotonic_clock::time_point time,
107 ::std::function<void()> callback);
108
109 // Deschedule an event by its iterator
110 void Deschedule(Token token);
111
112 void Run();
Austin Schuh44019f92019-05-19 19:58:27 -0700113 void RunFor(::aos::monotonic_clock::duration duration);
Neil Balchc8f41ed2018-01-20 22:06:53 -0800114
Austin Schuh44019f92019-05-19 19:58:27 -0700115 void Exit() {
116 is_running_ = false;
117 }
118
119 void AddRawEventLoop(RawEventLoop *event_loop) {
120 raw_event_loops_.push_back(event_loop);
121 }
122 void RemoveRawEventLoop(RawEventLoop *event_loop) {
123 raw_event_loops_.erase(::std::find(raw_event_loops_.begin(),
124 raw_event_loops_.end(), event_loop));
125 }
Neil Balchc8f41ed2018-01-20 22:06:53 -0800126
Austin Schuh7267c532019-05-19 19:55:53 -0700127 ::aos::monotonic_clock::time_point monotonic_now() const { return now_; }
Neil Balchc8f41ed2018-01-20 22:06:53 -0800128
129 private:
130 ::aos::monotonic_clock::time_point now_ = ::aos::monotonic_clock::epoch();
131 QueueType events_list_;
132 bool is_running_ = false;
Austin Schuh44019f92019-05-19 19:58:27 -0700133 ::std::vector<RawEventLoop *> raw_event_loops_;
Neil Balchc8f41ed2018-01-20 22:06:53 -0800134};
135
136class SimulatedQueue {
137 public:
Austin Schuhd681bbd2019-02-02 12:03:32 -0800138 explicit SimulatedQueue(const QueueTypeInfo &type, const ::std::string &name,
139 EventScheduler *scheduler)
140 : type_(type), name_(name), scheduler_(scheduler){};
Neil Balchc8f41ed2018-01-20 22:06:53 -0800141
Austin Schuh7267c532019-05-19 19:55:53 -0700142 ::std::unique_ptr<RawSender> MakeRawSender(EventLoop *event_loop);
Neil Balchc8f41ed2018-01-20 22:06:53 -0800143
Austin Schuh7267c532019-05-19 19:55:53 -0700144 ::std::unique_ptr<RawFetcher> MakeRawFetcher();
Neil Balchc8f41ed2018-01-20 22:06:53 -0800145
Austin Schuh7267c532019-05-19 19:55:53 -0700146 void MakeRawWatcher(
147 ::std::function<void(const ::aos::Message *message)> watcher);
Neil Balchc8f41ed2018-01-20 22:06:53 -0800148
149 void Send(RefCountedBuffer message) {
150 index_++;
151 latest_message_ = message;
152 for (auto &watcher : watchers_) {
Austin Schuh7267c532019-05-19 19:55:53 -0700153 scheduler_->Schedule(scheduler_->monotonic_now(),
Neil Balchc8f41ed2018-01-20 22:06:53 -0800154 [watcher, message]() { watcher(message.get()); });
155 }
156 }
157
158 const RefCountedBuffer &latest_message() { return latest_message_; }
159
160 int64_t index() { return index_; }
161
162 size_t size() { return type_.size; }
163
Austin Schuhd681bbd2019-02-02 12:03:32 -0800164 const char *name() const { return name_.c_str(); }
165
Neil Balchc8f41ed2018-01-20 22:06:53 -0800166 private:
167 int64_t index_ = -1;
168 QueueTypeInfo type_;
Austin Schuhd681bbd2019-02-02 12:03:32 -0800169 const ::std::string name_;
Neil Balchc8f41ed2018-01-20 22:06:53 -0800170 ::std::vector<std::function<void(const aos::Message *message)>> watchers_;
171 RefCountedBuffer latest_message_;
172 EventScheduler *scheduler_;
173};
174
Neil Balchc8f41ed2018-01-20 22:06:53 -0800175class SimulatedEventLoopFactory {
176 public:
Austin Schuh7267c532019-05-19 19:55:53 -0700177 ::std::unique_ptr<EventLoop> MakeEventLoop();
178
179 void Run() { scheduler_.Run(); }
Austin Schuh44019f92019-05-19 19:58:27 -0700180 void RunFor(monotonic_clock::duration duration) {
181 scheduler_.RunFor(duration);
182 }
Austin Schuh7267c532019-05-19 19:55:53 -0700183
184 monotonic_clock::time_point monotonic_now() const {
185 return scheduler_.monotonic_now();
Neil Balchc8f41ed2018-01-20 22:06:53 -0800186 }
187
188 private:
189 EventScheduler scheduler_;
190 ::std::map<::std::pair<::std::string, QueueTypeInfo>, SimulatedQueue> queues_;
191};
Austin Schuh7267c532019-05-19 19:55:53 -0700192
Neil Balchc8f41ed2018-01-20 22:06:53 -0800193} // namespace aos
Austin Schuh7267c532019-05-19 19:55:53 -0700194
Neil Balchc8f41ed2018-01-20 22:06:53 -0800195#endif //_AOS_EVENTS_TEST_EVENT_LOOP_H_