blob: 7c234565ca3c9f69497e2982c33e2aaf6db31462 [file] [log] [blame]
Brian Silverman9809c5f2022-07-23 16:12:23 -07001#ifndef AOS_EVENTS_EVENT_LOOP_RUNTIME_H_
2#define AOS_EVENTS_EVENT_LOOP_RUNTIME_H_
3
4// Exposes the primitives to implement an async Rust runtime on top of an
5// EventLoop. This is not intended to be used directly, so the APIs are not
6// particularly ergonomic for C++. See the Rust wrapper for detailed
7// documentation.
8
Adam Snaidercc8c2f72023-06-25 20:56:13 -07009#include <chrono>
Brian Silverman9809c5f2022-07-23 16:12:23 -070010#include <memory>
11#include <optional>
12
13#include "aos/events/event_loop.h"
14#include "aos/for_rust.h"
15#include "cxx.h"
16
17namespace aos {
18
19// An alternative version of Context to feed autocxx, to work around
20// https://github.com/google/autocxx/issues/787.
21/// <div rustbindgen replaces="aos::Context"></div>
22struct RustContext {
23 int64_t monotonic_event_time;
24 int64_t realtime_event_time;
25
26 int64_t monotonic_remote_time;
27 int64_t realtime_remote_time;
28
29 uint32_t queue_index;
30 uint32_t remote_queue_index;
31
32 size_t size;
33 const void *data;
34
35 int buffer_index;
36
37 // Work around https://github.com/google/autocxx/issues/266.
38 uint8_t source_boot_uuid[16];
39};
40
41static_assert(sizeof(Context) == sizeof(RustContext));
42static_assert(alignof(Context) == alignof(RustContext));
43static_assert(offsetof(Context, monotonic_event_time) ==
44 offsetof(RustContext, monotonic_event_time));
45static_assert(offsetof(Context, realtime_event_time) ==
46 offsetof(RustContext, realtime_event_time));
47static_assert(offsetof(Context, monotonic_remote_time) ==
48 offsetof(RustContext, monotonic_remote_time));
49static_assert(offsetof(Context, realtime_remote_time) ==
50 offsetof(RustContext, realtime_remote_time));
51static_assert(offsetof(Context, queue_index) ==
52 offsetof(RustContext, queue_index));
53static_assert(offsetof(Context, remote_queue_index) ==
54 offsetof(RustContext, remote_queue_index));
55static_assert(offsetof(Context, size) == offsetof(RustContext, size));
56static_assert(offsetof(Context, data) == offsetof(RustContext, data));
57static_assert(offsetof(Context, buffer_index) ==
58 offsetof(RustContext, buffer_index));
59static_assert(offsetof(Context, source_boot_uuid) ==
60 offsetof(RustContext, source_boot_uuid));
61static_assert(sizeof(Context::source_boot_uuid) ==
62 sizeof(RustContext::source_boot_uuid));
63static_assert(sizeof(RustContext) == sizeof(Context),
64 "Update this when adding or removing fields");
65
66// Similar to Rust's `Future<Output = Never>`.
67class ApplicationFuture {
68 public:
69 ApplicationFuture() = default;
70 virtual ~ApplicationFuture() = default;
71
72 // Calls a Rust `Future::poll`, with a waker that will panic if used. Because
73 // our Future's Output is Never, the inner Rust implementation can only return
74 // Poll::Pending, which is equivalent to void.
Brian Silverman1431a772022-08-31 20:44:36 -070075 //
76 // Returns true if it succeeded, or false if the Rust code paniced.
77 virtual bool Poll() = 0;
Brian Silverman9809c5f2022-07-23 16:12:23 -070078};
79
80// Similar to Rust's `Stream<Item = const Option&>`.
81class WatcherForRust {
82 public:
83 WatcherForRust(std::unique_ptr<RawFetcher> fetcher)
84 : fetcher_(std::move(fetcher)) {}
85 ~WatcherForRust() = default;
86
87 const Context *PollNext() {
88 if (!fetcher_->FetchNext()) {
89 return nullptr;
90 }
91 return &fetcher_->context();
92 }
93
94 private:
95 const std::unique_ptr<RawFetcher> fetcher_;
96};
97
98class SenderForRust {
99 public:
100 SenderForRust(std::unique_ptr<RawSender> sender)
101 : sender_(std::move(sender)) {}
102 ~SenderForRust() = default;
103
104 uint8_t *data() { return reinterpret_cast<uint8_t *>(sender_->data()); }
105 size_t size() { return sender_->size(); }
106 RawSender::Error SendBuffer(size_t size) { return sender_->Send(size); }
107 RawSender::Error CopyAndSend(const uint8_t *data, size_t size) {
108 return sender_->Send(data, size);
109 }
110
111 private:
112 const std::unique_ptr<RawSender> sender_;
113};
114
115class FetcherForRust {
116 public:
117 FetcherForRust(std::unique_ptr<RawFetcher> fetcher)
118 : fetcher_(std::move(fetcher)) {}
119 ~FetcherForRust() = default;
120
121 bool FetchNext() { return fetcher_->FetchNext(); }
122 bool Fetch() { return fetcher_->Fetch(); }
123
124 const Context &context() const { return fetcher_->context(); }
125
126 private:
127 const std::unique_ptr<RawFetcher> fetcher_;
128};
129
Brian Silverman76f48362022-08-24 21:09:08 -0700130class EventLoopRuntime;
131
132class OnRunForRust {
133 public:
Adam Snaider48a54682023-09-28 21:50:42 -0700134 OnRunForRust(const EventLoopRuntime *runtime);
Brian Silverman76f48362022-08-24 21:09:08 -0700135 ~OnRunForRust();
136
137 bool is_running() const;
138
139 private:
Adam Snaider48a54682023-09-28 21:50:42 -0700140 const EventLoopRuntime *const runtime_;
Brian Silverman76f48362022-08-24 21:09:08 -0700141};
142
Adam Snaidercc8c2f72023-06-25 20:56:13 -0700143class TimerForRust {
144 public:
Adam Snaider48a54682023-09-28 21:50:42 -0700145 static std::unique_ptr<TimerForRust> Make(const EventLoopRuntime *runtime);
Adam Snaidercc8c2f72023-06-25 20:56:13 -0700146
147 TimerForRust(const TimerForRust &) = delete;
148 TimerForRust(TimerForRust &&) = delete;
149
150 TimerForRust &operator=(const TimerForRust &) = delete;
151 TimerForRust &operator=(TimerForRust &&) = delete;
152
153 ~TimerForRust() { timer_->Disable(); }
154
155 void Schedule(int64_t base, int64_t repeat_offset) {
156 timer_->Schedule(
157 monotonic_clock::time_point(std::chrono::nanoseconds(base)),
158 std::chrono::nanoseconds(repeat_offset));
159 }
160
161 void Disable() { timer_->Disable(); }
162
163 bool IsDisabled() const { return timer_->IsDisabled(); }
164
165 void set_name(rust::Str name) { timer_->set_name(RustStrToStringView(name)); }
166 rust::Str name() const { return StringViewToRustStr(timer_->name()); }
167
168 // If true, the timer is expired.
169 bool Poll();
170
171 private:
172 TimerForRust() = default;
173
174 TimerHandler *timer_;
175 bool expired_ = false;
176};
177
Brian Silverman9809c5f2022-07-23 16:12:23 -0700178class EventLoopRuntime {
179 public:
180 EventLoopRuntime(EventLoop *event_loop) : event_loop_(event_loop) {}
Brian Silverman76f48362022-08-24 21:09:08 -0700181 ~EventLoopRuntime() {
Brian Silverman1431a772022-08-31 20:44:36 -0700182 // Do this first, because it may hold child objects.
183 task_.reset();
Brian Silverman76f48362022-08-24 21:09:08 -0700184 CHECK_EQ(child_count_, 0)
185 << ": Some child objects were not destroyed first";
186 }
Brian Silverman9809c5f2022-07-23 16:12:23 -0700187
Adam Snaider48a54682023-09-28 21:50:42 -0700188 EventLoop *event_loop() const { return event_loop_; }
Brian Silverman9809c5f2022-07-23 16:12:23 -0700189
Adam Snaider48a54682023-09-28 21:50:42 -0700190 void Spawn(std::unique_ptr<ApplicationFuture> task) const {
Brian Silverman1431a772022-08-31 20:44:36 -0700191 CHECK(!task_) << ": May only call Spawn once";
Brian Silverman9809c5f2022-07-23 16:12:23 -0700192 task_ = std::move(task);
Brian Silverman76f48362022-08-24 21:09:08 -0700193 DoPoll();
194 // Just do this unconditionally, so we don't have to keep track of each
195 // OnRun to only do it once. If Rust doesn't use OnRun, it's harmless to do
196 // an extra poll.
Brian Silverman9809c5f2022-07-23 16:12:23 -0700197 event_loop_->OnRun([this] { DoPoll(); });
198 }
199
200 const Configuration *configuration() const {
201 return event_loop_->configuration();
202 }
203 const Node *node() const { return event_loop_->node(); }
204
Brian Silverman76f48362022-08-24 21:09:08 -0700205 bool is_running() const { return event_loop_->is_running(); }
206
Brian Silverman9809c5f2022-07-23 16:12:23 -0700207 // autocxx generates broken C++ code for `time_point`, see
208 // https://github.com/google/autocxx/issues/787.
209 int64_t monotonic_now() const {
210 return std::chrono::nanoseconds(
211 event_loop_->monotonic_now().time_since_epoch())
212 .count();
213 }
214 int64_t realtime_now() const {
215 return std::chrono::nanoseconds(
216 event_loop_->realtime_now().time_since_epoch())
217 .count();
218 }
219
220 rust::Str name() const { return StringViewToRustStr(event_loop_->name()); }
221
Adam Snaider48a54682023-09-28 21:50:42 -0700222 WatcherForRust MakeWatcher(const Channel *channel) const {
Brian Silverman9809c5f2022-07-23 16:12:23 -0700223 event_loop_->MakeRawNoArgWatcher(channel,
224 [this](const Context &) { DoPoll(); });
225 return WatcherForRust(event_loop_->MakeRawFetcher(channel));
226 }
227
Adam Snaider48a54682023-09-28 21:50:42 -0700228 SenderForRust MakeSender(const Channel *channel) const {
Brian Silverman9809c5f2022-07-23 16:12:23 -0700229 return SenderForRust(event_loop_->MakeRawSender(channel));
230 }
231
Adam Snaider48a54682023-09-28 21:50:42 -0700232 FetcherForRust MakeFetcher(const Channel *channel) const {
Brian Silverman9809c5f2022-07-23 16:12:23 -0700233 return FetcherForRust(event_loop_->MakeRawFetcher(channel));
234 }
235
Adam Snaider48a54682023-09-28 21:50:42 -0700236 OnRunForRust MakeOnRun() const { return OnRunForRust(this); }
Brian Silverman76f48362022-08-24 21:09:08 -0700237
Adam Snaider48a54682023-09-28 21:50:42 -0700238 std::unique_ptr<TimerForRust> AddTimer() const {
239 return TimerForRust::Make(this);
240 }
Adam Snaidercc8c2f72023-06-25 20:56:13 -0700241
Adam Snaidercf0dac72023-10-02 14:41:58 -0700242 void SetRuntimeRealtimePriority(int priority) const {
243 event_loop_->SetRuntimeRealtimePriority(priority);
244 }
245
246 void SetRuntimeAffinity(const cpu_set_t &cpuset) const {
247 event_loop_->SetRuntimeAffinity(cpuset);
248 }
249
Brian Silverman9809c5f2022-07-23 16:12:23 -0700250 private:
Brian Silverman76f48362022-08-24 21:09:08 -0700251 friend class OnRunForRust;
Adam Snaidercc8c2f72023-06-25 20:56:13 -0700252 friend class TimerForRust;
Brian Silverman76f48362022-08-24 21:09:08 -0700253
Brian Silverman9809c5f2022-07-23 16:12:23 -0700254 // Polls the top-level future once. This is what all the callbacks should do.
Adam Snaider48a54682023-09-28 21:50:42 -0700255 void DoPoll() const {
Brian Silverman9809c5f2022-07-23 16:12:23 -0700256 if (task_) {
Brian Silverman1431a772022-08-31 20:44:36 -0700257 CHECK(task_->Poll()) << ": Rust panic, aborting";
Brian Silverman9809c5f2022-07-23 16:12:23 -0700258 }
259 }
260
261 EventLoop *const event_loop_;
262
Adam Snaider48a54682023-09-28 21:50:42 -0700263 // For Rust's EventLoopRuntime to be semantically equivelant to C++'s event
264 // loop, we need the ability to have shared references (&EventLoopRuntime) on
265 // the Rust side. Without that, the API would be overly restrictive to be
266 // usable. In order for the generated code to use &self references on methods,
267 // they need to be marked `const` on the C++ side. We use the `mutable`
268 // keyword to allow mutation through `const` methods.
269 //
270 // SAFETY:
271 // * The event loop runtime must be `!Sync` in the Rust side (default).
272 // * We can't expose exclusive references (&mut) to either of the mutable
273 // fields on the Rust side from a shared reference (&).
274 mutable std::unique_ptr<ApplicationFuture> task_;
Brian Silverman76f48362022-08-24 21:09:08 -0700275
Adam Snaider48a54682023-09-28 21:50:42 -0700276 mutable int child_count_ = 0;
Brian Silverman9809c5f2022-07-23 16:12:23 -0700277};
278
279} // namespace aos
280
281#endif // AOS_EVENTS_EVENT_LOOP_RUNTIME_H_