blob: afc96e28b9fd9f06bb8a31e79190a5be66c603fd [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;
Austin Schuhac6d89e2024-03-27 14:56:09 -070028 int64_t monotonic_remote_transmit_time;
Brian Silverman9809c5f2022-07-23 16:12:23 -070029
30 uint32_t queue_index;
31 uint32_t remote_queue_index;
32
33 size_t size;
34 const void *data;
35
36 int buffer_index;
37
38 // Work around https://github.com/google/autocxx/issues/266.
39 uint8_t source_boot_uuid[16];
40};
41
42static_assert(sizeof(Context) == sizeof(RustContext));
43static_assert(alignof(Context) == alignof(RustContext));
44static_assert(offsetof(Context, monotonic_event_time) ==
45 offsetof(RustContext, monotonic_event_time));
46static_assert(offsetof(Context, realtime_event_time) ==
47 offsetof(RustContext, realtime_event_time));
48static_assert(offsetof(Context, monotonic_remote_time) ==
49 offsetof(RustContext, monotonic_remote_time));
50static_assert(offsetof(Context, realtime_remote_time) ==
51 offsetof(RustContext, realtime_remote_time));
Austin Schuhac6d89e2024-03-27 14:56:09 -070052static_assert(offsetof(Context, monotonic_remote_transmit_time) ==
53 offsetof(RustContext, monotonic_remote_transmit_time));
Brian Silverman9809c5f2022-07-23 16:12:23 -070054static_assert(offsetof(Context, queue_index) ==
55 offsetof(RustContext, queue_index));
56static_assert(offsetof(Context, remote_queue_index) ==
57 offsetof(RustContext, remote_queue_index));
58static_assert(offsetof(Context, size) == offsetof(RustContext, size));
59static_assert(offsetof(Context, data) == offsetof(RustContext, data));
60static_assert(offsetof(Context, buffer_index) ==
61 offsetof(RustContext, buffer_index));
62static_assert(offsetof(Context, source_boot_uuid) ==
63 offsetof(RustContext, source_boot_uuid));
64static_assert(sizeof(Context::source_boot_uuid) ==
65 sizeof(RustContext::source_boot_uuid));
66static_assert(sizeof(RustContext) == sizeof(Context),
67 "Update this when adding or removing fields");
68
69// Similar to Rust's `Future<Output = Never>`.
70class ApplicationFuture {
71 public:
72 ApplicationFuture() = default;
73 virtual ~ApplicationFuture() = default;
74
75 // Calls a Rust `Future::poll`, with a waker that will panic if used. Because
76 // our Future's Output is Never, the inner Rust implementation can only return
77 // Poll::Pending, which is equivalent to void.
Brian Silverman1431a772022-08-31 20:44:36 -070078 //
79 // Returns true if it succeeded, or false if the Rust code paniced.
80 virtual bool Poll() = 0;
Brian Silverman9809c5f2022-07-23 16:12:23 -070081};
82
83// Similar to Rust's `Stream<Item = const Option&>`.
84class WatcherForRust {
85 public:
86 WatcherForRust(std::unique_ptr<RawFetcher> fetcher)
87 : fetcher_(std::move(fetcher)) {}
88 ~WatcherForRust() = default;
89
90 const Context *PollNext() {
91 if (!fetcher_->FetchNext()) {
92 return nullptr;
93 }
94 return &fetcher_->context();
95 }
96
97 private:
98 const std::unique_ptr<RawFetcher> fetcher_;
99};
100
101class SenderForRust {
102 public:
103 SenderForRust(std::unique_ptr<RawSender> sender)
104 : sender_(std::move(sender)) {}
105 ~SenderForRust() = default;
106
107 uint8_t *data() { return reinterpret_cast<uint8_t *>(sender_->data()); }
108 size_t size() { return sender_->size(); }
109 RawSender::Error SendBuffer(size_t size) { return sender_->Send(size); }
110 RawSender::Error CopyAndSend(const uint8_t *data, size_t size) {
111 return sender_->Send(data, size);
112 }
113
114 private:
115 const std::unique_ptr<RawSender> sender_;
116};
117
118class FetcherForRust {
119 public:
120 FetcherForRust(std::unique_ptr<RawFetcher> fetcher)
121 : fetcher_(std::move(fetcher)) {}
122 ~FetcherForRust() = default;
123
124 bool FetchNext() { return fetcher_->FetchNext(); }
125 bool Fetch() { return fetcher_->Fetch(); }
126
127 const Context &context() const { return fetcher_->context(); }
128
129 private:
130 const std::unique_ptr<RawFetcher> fetcher_;
131};
132
Brian Silverman76f48362022-08-24 21:09:08 -0700133class EventLoopRuntime;
134
135class OnRunForRust {
136 public:
Adam Snaider48a54682023-09-28 21:50:42 -0700137 OnRunForRust(const EventLoopRuntime *runtime);
Brian Silverman76f48362022-08-24 21:09:08 -0700138 ~OnRunForRust();
139
140 bool is_running() const;
141
142 private:
Adam Snaider48a54682023-09-28 21:50:42 -0700143 const EventLoopRuntime *const runtime_;
Brian Silverman76f48362022-08-24 21:09:08 -0700144};
145
Adam Snaidercc8c2f72023-06-25 20:56:13 -0700146class TimerForRust {
147 public:
Adam Snaider48a54682023-09-28 21:50:42 -0700148 static std::unique_ptr<TimerForRust> Make(const EventLoopRuntime *runtime);
Adam Snaidercc8c2f72023-06-25 20:56:13 -0700149
150 TimerForRust(const TimerForRust &) = delete;
151 TimerForRust(TimerForRust &&) = delete;
152
153 TimerForRust &operator=(const TimerForRust &) = delete;
154 TimerForRust &operator=(TimerForRust &&) = delete;
155
156 ~TimerForRust() { timer_->Disable(); }
157
158 void Schedule(int64_t base, int64_t repeat_offset) {
159 timer_->Schedule(
160 monotonic_clock::time_point(std::chrono::nanoseconds(base)),
161 std::chrono::nanoseconds(repeat_offset));
162 }
163
164 void Disable() { timer_->Disable(); }
165
166 bool IsDisabled() const { return timer_->IsDisabled(); }
167
168 void set_name(rust::Str name) { timer_->set_name(RustStrToStringView(name)); }
169 rust::Str name() const { return StringViewToRustStr(timer_->name()); }
170
171 // If true, the timer is expired.
172 bool Poll();
173
174 private:
175 TimerForRust() = default;
176
177 TimerHandler *timer_;
178 bool expired_ = false;
179};
180
Brian Silverman9809c5f2022-07-23 16:12:23 -0700181class EventLoopRuntime {
182 public:
Adam Snaider014f88e2023-10-24 13:21:42 -0400183 EventLoopRuntime(const EventLoop *event_loop)
184 // SAFETY: A &EventLoop in Rust becomes a const EventLoop*. While
185 // that's generally a reasonable convention, they are semantically
186 // different. In Rust, a &mut EventLoop is very restrictive as it enforces
187 // uniqueness of the reference. Additionally, a &EventLoop doesn't convey
188 // const-ness in the C++ sense. So to make the FFI boundary more
189 // ergonomic, we allow a &EventLoop passed from rust to be translated into
190 // an EventLoop* in C++. This is safe so long as EventLoop is !Sync and no
191 // &mut EventLoop references are constructed in Rust.
192 : event_loop_(const_cast<EventLoop *>(event_loop)) {}
Brian Silverman76f48362022-08-24 21:09:08 -0700193 ~EventLoopRuntime() {
Brian Silverman1431a772022-08-31 20:44:36 -0700194 // Do this first, because it may hold child objects.
195 task_.reset();
Brian Silverman76f48362022-08-24 21:09:08 -0700196 CHECK_EQ(child_count_, 0)
197 << ": Some child objects were not destroyed first";
198 }
Brian Silverman9809c5f2022-07-23 16:12:23 -0700199
Adam Snaider48a54682023-09-28 21:50:42 -0700200 EventLoop *event_loop() const { return event_loop_; }
Brian Silverman9809c5f2022-07-23 16:12:23 -0700201
Adam Snaider48a54682023-09-28 21:50:42 -0700202 void Spawn(std::unique_ptr<ApplicationFuture> task) const {
Brian Silverman1431a772022-08-31 20:44:36 -0700203 CHECK(!task_) << ": May only call Spawn once";
Brian Silverman9809c5f2022-07-23 16:12:23 -0700204 task_ = std::move(task);
Brian Silverman76f48362022-08-24 21:09:08 -0700205 DoPoll();
206 // Just do this unconditionally, so we don't have to keep track of each
207 // OnRun to only do it once. If Rust doesn't use OnRun, it's harmless to do
208 // an extra poll.
Brian Silverman9809c5f2022-07-23 16:12:23 -0700209 event_loop_->OnRun([this] { DoPoll(); });
210 }
211
212 const Configuration *configuration() const {
213 return event_loop_->configuration();
214 }
215 const Node *node() const { return event_loop_->node(); }
216
Brian Silverman76f48362022-08-24 21:09:08 -0700217 bool is_running() const { return event_loop_->is_running(); }
218
Brian Silverman9809c5f2022-07-23 16:12:23 -0700219 // autocxx generates broken C++ code for `time_point`, see
220 // https://github.com/google/autocxx/issues/787.
221 int64_t monotonic_now() const {
222 return std::chrono::nanoseconds(
223 event_loop_->monotonic_now().time_since_epoch())
224 .count();
225 }
226 int64_t realtime_now() const {
227 return std::chrono::nanoseconds(
228 event_loop_->realtime_now().time_since_epoch())
229 .count();
230 }
231
232 rust::Str name() const { return StringViewToRustStr(event_loop_->name()); }
233
Adam Snaider48a54682023-09-28 21:50:42 -0700234 WatcherForRust MakeWatcher(const Channel *channel) const {
Brian Silverman9809c5f2022-07-23 16:12:23 -0700235 event_loop_->MakeRawNoArgWatcher(channel,
236 [this](const Context &) { DoPoll(); });
237 return WatcherForRust(event_loop_->MakeRawFetcher(channel));
238 }
239
Adam Snaider48a54682023-09-28 21:50:42 -0700240 SenderForRust MakeSender(const Channel *channel) const {
Brian Silverman9809c5f2022-07-23 16:12:23 -0700241 return SenderForRust(event_loop_->MakeRawSender(channel));
242 }
243
Adam Snaider48a54682023-09-28 21:50:42 -0700244 FetcherForRust MakeFetcher(const Channel *channel) const {
Brian Silverman9809c5f2022-07-23 16:12:23 -0700245 return FetcherForRust(event_loop_->MakeRawFetcher(channel));
246 }
247
Adam Snaider48a54682023-09-28 21:50:42 -0700248 OnRunForRust MakeOnRun() const { return OnRunForRust(this); }
Brian Silverman76f48362022-08-24 21:09:08 -0700249
Adam Snaider48a54682023-09-28 21:50:42 -0700250 std::unique_ptr<TimerForRust> AddTimer() const {
251 return TimerForRust::Make(this);
252 }
Adam Snaidercc8c2f72023-06-25 20:56:13 -0700253
Adam Snaidercf0dac72023-10-02 14:41:58 -0700254 void SetRuntimeRealtimePriority(int priority) const {
255 event_loop_->SetRuntimeRealtimePriority(priority);
256 }
257
258 void SetRuntimeAffinity(const cpu_set_t &cpuset) const {
259 event_loop_->SetRuntimeAffinity(cpuset);
260 }
261
Brian Silverman9809c5f2022-07-23 16:12:23 -0700262 private:
Brian Silverman76f48362022-08-24 21:09:08 -0700263 friend class OnRunForRust;
Adam Snaidercc8c2f72023-06-25 20:56:13 -0700264 friend class TimerForRust;
Brian Silverman76f48362022-08-24 21:09:08 -0700265
Brian Silverman9809c5f2022-07-23 16:12:23 -0700266 // Polls the top-level future once. This is what all the callbacks should do.
Adam Snaider48a54682023-09-28 21:50:42 -0700267 void DoPoll() const {
Brian Silverman9809c5f2022-07-23 16:12:23 -0700268 if (task_) {
Brian Silverman1431a772022-08-31 20:44:36 -0700269 CHECK(task_->Poll()) << ": Rust panic, aborting";
Brian Silverman9809c5f2022-07-23 16:12:23 -0700270 }
271 }
272
273 EventLoop *const event_loop_;
274
Adam Snaider48a54682023-09-28 21:50:42 -0700275 // For Rust's EventLoopRuntime to be semantically equivelant to C++'s event
276 // loop, we need the ability to have shared references (&EventLoopRuntime) on
277 // the Rust side. Without that, the API would be overly restrictive to be
278 // usable. In order for the generated code to use &self references on methods,
279 // they need to be marked `const` on the C++ side. We use the `mutable`
280 // keyword to allow mutation through `const` methods.
281 //
282 // SAFETY:
283 // * The event loop runtime must be `!Sync` in the Rust side (default).
284 // * We can't expose exclusive references (&mut) to either of the mutable
285 // fields on the Rust side from a shared reference (&).
286 mutable std::unique_ptr<ApplicationFuture> task_;
Brian Silverman76f48362022-08-24 21:09:08 -0700287
Adam Snaider48a54682023-09-28 21:50:42 -0700288 mutable int child_count_ = 0;
Brian Silverman9809c5f2022-07-23 16:12:23 -0700289};
290
291} // namespace aos
292
293#endif // AOS_EVENTS_EVENT_LOOP_RUNTIME_H_