blob: 0505852a842393beae031282e5497e64d923c888 [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:
Adam Snaider014f88e2023-10-24 13:21:42 -0400180 EventLoopRuntime(const EventLoop *event_loop)
181 // SAFETY: A &EventLoop in Rust becomes a const EventLoop*. While
182 // that's generally a reasonable convention, they are semantically
183 // different. In Rust, a &mut EventLoop is very restrictive as it enforces
184 // uniqueness of the reference. Additionally, a &EventLoop doesn't convey
185 // const-ness in the C++ sense. So to make the FFI boundary more
186 // ergonomic, we allow a &EventLoop passed from rust to be translated into
187 // an EventLoop* in C++. This is safe so long as EventLoop is !Sync and no
188 // &mut EventLoop references are constructed in Rust.
189 : event_loop_(const_cast<EventLoop *>(event_loop)) {}
Brian Silverman76f48362022-08-24 21:09:08 -0700190 ~EventLoopRuntime() {
Brian Silverman1431a772022-08-31 20:44:36 -0700191 // Do this first, because it may hold child objects.
192 task_.reset();
Brian Silverman76f48362022-08-24 21:09:08 -0700193 CHECK_EQ(child_count_, 0)
194 << ": Some child objects were not destroyed first";
195 }
Brian Silverman9809c5f2022-07-23 16:12:23 -0700196
Adam Snaider48a54682023-09-28 21:50:42 -0700197 EventLoop *event_loop() const { return event_loop_; }
Brian Silverman9809c5f2022-07-23 16:12:23 -0700198
Adam Snaider48a54682023-09-28 21:50:42 -0700199 void Spawn(std::unique_ptr<ApplicationFuture> task) const {
Brian Silverman1431a772022-08-31 20:44:36 -0700200 CHECK(!task_) << ": May only call Spawn once";
Brian Silverman9809c5f2022-07-23 16:12:23 -0700201 task_ = std::move(task);
Brian Silverman76f48362022-08-24 21:09:08 -0700202 DoPoll();
203 // Just do this unconditionally, so we don't have to keep track of each
204 // OnRun to only do it once. If Rust doesn't use OnRun, it's harmless to do
205 // an extra poll.
Brian Silverman9809c5f2022-07-23 16:12:23 -0700206 event_loop_->OnRun([this] { DoPoll(); });
207 }
208
209 const Configuration *configuration() const {
210 return event_loop_->configuration();
211 }
212 const Node *node() const { return event_loop_->node(); }
213
Brian Silverman76f48362022-08-24 21:09:08 -0700214 bool is_running() const { return event_loop_->is_running(); }
215
Brian Silverman9809c5f2022-07-23 16:12:23 -0700216 // autocxx generates broken C++ code for `time_point`, see
217 // https://github.com/google/autocxx/issues/787.
218 int64_t monotonic_now() const {
219 return std::chrono::nanoseconds(
220 event_loop_->monotonic_now().time_since_epoch())
221 .count();
222 }
223 int64_t realtime_now() const {
224 return std::chrono::nanoseconds(
225 event_loop_->realtime_now().time_since_epoch())
226 .count();
227 }
228
229 rust::Str name() const { return StringViewToRustStr(event_loop_->name()); }
230
Adam Snaider48a54682023-09-28 21:50:42 -0700231 WatcherForRust MakeWatcher(const Channel *channel) const {
Brian Silverman9809c5f2022-07-23 16:12:23 -0700232 event_loop_->MakeRawNoArgWatcher(channel,
233 [this](const Context &) { DoPoll(); });
234 return WatcherForRust(event_loop_->MakeRawFetcher(channel));
235 }
236
Adam Snaider48a54682023-09-28 21:50:42 -0700237 SenderForRust MakeSender(const Channel *channel) const {
Brian Silverman9809c5f2022-07-23 16:12:23 -0700238 return SenderForRust(event_loop_->MakeRawSender(channel));
239 }
240
Adam Snaider48a54682023-09-28 21:50:42 -0700241 FetcherForRust MakeFetcher(const Channel *channel) const {
Brian Silverman9809c5f2022-07-23 16:12:23 -0700242 return FetcherForRust(event_loop_->MakeRawFetcher(channel));
243 }
244
Adam Snaider48a54682023-09-28 21:50:42 -0700245 OnRunForRust MakeOnRun() const { return OnRunForRust(this); }
Brian Silverman76f48362022-08-24 21:09:08 -0700246
Adam Snaider48a54682023-09-28 21:50:42 -0700247 std::unique_ptr<TimerForRust> AddTimer() const {
248 return TimerForRust::Make(this);
249 }
Adam Snaidercc8c2f72023-06-25 20:56:13 -0700250
Adam Snaidercf0dac72023-10-02 14:41:58 -0700251 void SetRuntimeRealtimePriority(int priority) const {
252 event_loop_->SetRuntimeRealtimePriority(priority);
253 }
254
255 void SetRuntimeAffinity(const cpu_set_t &cpuset) const {
256 event_loop_->SetRuntimeAffinity(cpuset);
257 }
258
Brian Silverman9809c5f2022-07-23 16:12:23 -0700259 private:
Brian Silverman76f48362022-08-24 21:09:08 -0700260 friend class OnRunForRust;
Adam Snaidercc8c2f72023-06-25 20:56:13 -0700261 friend class TimerForRust;
Brian Silverman76f48362022-08-24 21:09:08 -0700262
Brian Silverman9809c5f2022-07-23 16:12:23 -0700263 // Polls the top-level future once. This is what all the callbacks should do.
Adam Snaider48a54682023-09-28 21:50:42 -0700264 void DoPoll() const {
Brian Silverman9809c5f2022-07-23 16:12:23 -0700265 if (task_) {
Brian Silverman1431a772022-08-31 20:44:36 -0700266 CHECK(task_->Poll()) << ": Rust panic, aborting";
Brian Silverman9809c5f2022-07-23 16:12:23 -0700267 }
268 }
269
270 EventLoop *const event_loop_;
271
Adam Snaider48a54682023-09-28 21:50:42 -0700272 // For Rust's EventLoopRuntime to be semantically equivelant to C++'s event
273 // loop, we need the ability to have shared references (&EventLoopRuntime) on
274 // the Rust side. Without that, the API would be overly restrictive to be
275 // usable. In order for the generated code to use &self references on methods,
276 // they need to be marked `const` on the C++ side. We use the `mutable`
277 // keyword to allow mutation through `const` methods.
278 //
279 // SAFETY:
280 // * The event loop runtime must be `!Sync` in the Rust side (default).
281 // * We can't expose exclusive references (&mut) to either of the mutable
282 // fields on the Rust side from a shared reference (&).
283 mutable std::unique_ptr<ApplicationFuture> task_;
Brian Silverman76f48362022-08-24 21:09:08 -0700284
Adam Snaider48a54682023-09-28 21:50:42 -0700285 mutable int child_count_ = 0;
Brian Silverman9809c5f2022-07-23 16:12:23 -0700286};
287
288} // namespace aos
289
290#endif // AOS_EVENTS_EVENT_LOOP_RUNTIME_H_