blob: 325560f9a0217ce9882ec41eeb8d5267ab5416ce [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
9#include <memory>
10#include <optional>
11
12#include "aos/events/event_loop.h"
13#include "aos/for_rust.h"
14#include "cxx.h"
15
16namespace aos {
17
18// An alternative version of Context to feed autocxx, to work around
19// https://github.com/google/autocxx/issues/787.
20/// <div rustbindgen replaces="aos::Context"></div>
21struct RustContext {
22 int64_t monotonic_event_time;
23 int64_t realtime_event_time;
24
25 int64_t monotonic_remote_time;
26 int64_t realtime_remote_time;
27
28 uint32_t queue_index;
29 uint32_t remote_queue_index;
30
31 size_t size;
32 const void *data;
33
34 int buffer_index;
35
36 // Work around https://github.com/google/autocxx/issues/266.
37 uint8_t source_boot_uuid[16];
38};
39
40static_assert(sizeof(Context) == sizeof(RustContext));
41static_assert(alignof(Context) == alignof(RustContext));
42static_assert(offsetof(Context, monotonic_event_time) ==
43 offsetof(RustContext, monotonic_event_time));
44static_assert(offsetof(Context, realtime_event_time) ==
45 offsetof(RustContext, realtime_event_time));
46static_assert(offsetof(Context, monotonic_remote_time) ==
47 offsetof(RustContext, monotonic_remote_time));
48static_assert(offsetof(Context, realtime_remote_time) ==
49 offsetof(RustContext, realtime_remote_time));
50static_assert(offsetof(Context, queue_index) ==
51 offsetof(RustContext, queue_index));
52static_assert(offsetof(Context, remote_queue_index) ==
53 offsetof(RustContext, remote_queue_index));
54static_assert(offsetof(Context, size) == offsetof(RustContext, size));
55static_assert(offsetof(Context, data) == offsetof(RustContext, data));
56static_assert(offsetof(Context, buffer_index) ==
57 offsetof(RustContext, buffer_index));
58static_assert(offsetof(Context, source_boot_uuid) ==
59 offsetof(RustContext, source_boot_uuid));
60static_assert(sizeof(Context::source_boot_uuid) ==
61 sizeof(RustContext::source_boot_uuid));
62static_assert(sizeof(RustContext) == sizeof(Context),
63 "Update this when adding or removing fields");
64
65// Similar to Rust's `Future<Output = Never>`.
66class ApplicationFuture {
67 public:
68 ApplicationFuture() = default;
69 virtual ~ApplicationFuture() = default;
70
71 // Calls a Rust `Future::poll`, with a waker that will panic if used. Because
72 // our Future's Output is Never, the inner Rust implementation can only return
73 // Poll::Pending, which is equivalent to void.
Brian Silverman1431a772022-08-31 20:44:36 -070074 //
75 // Returns true if it succeeded, or false if the Rust code paniced.
76 virtual bool Poll() = 0;
Brian Silverman9809c5f2022-07-23 16:12:23 -070077};
78
79// Similar to Rust's `Stream<Item = const Option&>`.
80class WatcherForRust {
81 public:
82 WatcherForRust(std::unique_ptr<RawFetcher> fetcher)
83 : fetcher_(std::move(fetcher)) {}
84 ~WatcherForRust() = default;
85
86 const Context *PollNext() {
87 if (!fetcher_->FetchNext()) {
88 return nullptr;
89 }
90 return &fetcher_->context();
91 }
92
93 private:
94 const std::unique_ptr<RawFetcher> fetcher_;
95};
96
97class SenderForRust {
98 public:
99 SenderForRust(std::unique_ptr<RawSender> sender)
100 : sender_(std::move(sender)) {}
101 ~SenderForRust() = default;
102
103 uint8_t *data() { return reinterpret_cast<uint8_t *>(sender_->data()); }
104 size_t size() { return sender_->size(); }
105 RawSender::Error SendBuffer(size_t size) { return sender_->Send(size); }
106 RawSender::Error CopyAndSend(const uint8_t *data, size_t size) {
107 return sender_->Send(data, size);
108 }
109
110 private:
111 const std::unique_ptr<RawSender> sender_;
112};
113
114class FetcherForRust {
115 public:
116 FetcherForRust(std::unique_ptr<RawFetcher> fetcher)
117 : fetcher_(std::move(fetcher)) {}
118 ~FetcherForRust() = default;
119
120 bool FetchNext() { return fetcher_->FetchNext(); }
121 bool Fetch() { return fetcher_->Fetch(); }
122
123 const Context &context() const { return fetcher_->context(); }
124
125 private:
126 const std::unique_ptr<RawFetcher> fetcher_;
127};
128
Brian Silverman76f48362022-08-24 21:09:08 -0700129class EventLoopRuntime;
130
131class OnRunForRust {
132 public:
133 OnRunForRust(EventLoopRuntime *runtime);
134 ~OnRunForRust();
135
136 bool is_running() const;
137
138 private:
139 EventLoopRuntime *const runtime_;
140};
141
Brian Silverman9809c5f2022-07-23 16:12:23 -0700142class EventLoopRuntime {
143 public:
144 EventLoopRuntime(EventLoop *event_loop) : event_loop_(event_loop) {}
Brian Silverman76f48362022-08-24 21:09:08 -0700145 ~EventLoopRuntime() {
Brian Silverman1431a772022-08-31 20:44:36 -0700146 // Do this first, because it may hold child objects.
147 task_.reset();
Brian Silverman76f48362022-08-24 21:09:08 -0700148 CHECK_EQ(child_count_, 0)
149 << ": Some child objects were not destroyed first";
150 }
Brian Silverman9809c5f2022-07-23 16:12:23 -0700151
152 EventLoop *event_loop() { return event_loop_; }
153
Brian Silverman1431a772022-08-31 20:44:36 -0700154 void Spawn(std::unique_ptr<ApplicationFuture> task) {
155 CHECK(!task_) << ": May only call Spawn once";
Brian Silverman9809c5f2022-07-23 16:12:23 -0700156 task_ = std::move(task);
Brian Silverman76f48362022-08-24 21:09:08 -0700157 DoPoll();
158 // Just do this unconditionally, so we don't have to keep track of each
159 // OnRun to only do it once. If Rust doesn't use OnRun, it's harmless to do
160 // an extra poll.
Brian Silverman9809c5f2022-07-23 16:12:23 -0700161 event_loop_->OnRun([this] { DoPoll(); });
162 }
163
164 const Configuration *configuration() const {
165 return event_loop_->configuration();
166 }
167 const Node *node() const { return event_loop_->node(); }
168
Brian Silverman76f48362022-08-24 21:09:08 -0700169 bool is_running() const { return event_loop_->is_running(); }
170
Brian Silverman9809c5f2022-07-23 16:12:23 -0700171 // autocxx generates broken C++ code for `time_point`, see
172 // https://github.com/google/autocxx/issues/787.
173 int64_t monotonic_now() const {
174 return std::chrono::nanoseconds(
175 event_loop_->monotonic_now().time_since_epoch())
176 .count();
177 }
178 int64_t realtime_now() const {
179 return std::chrono::nanoseconds(
180 event_loop_->realtime_now().time_since_epoch())
181 .count();
182 }
183
184 rust::Str name() const { return StringViewToRustStr(event_loop_->name()); }
185
186 WatcherForRust MakeWatcher(const Channel *channel) {
187 event_loop_->MakeRawNoArgWatcher(channel,
188 [this](const Context &) { DoPoll(); });
189 return WatcherForRust(event_loop_->MakeRawFetcher(channel));
190 }
191
192 SenderForRust MakeSender(const Channel *channel) {
193 return SenderForRust(event_loop_->MakeRawSender(channel));
194 }
195
196 FetcherForRust MakeFetcher(const Channel *channel) {
197 return FetcherForRust(event_loop_->MakeRawFetcher(channel));
198 }
199
Brian Silverman76f48362022-08-24 21:09:08 -0700200 OnRunForRust MakeOnRun() { return OnRunForRust(this); }
201
Brian Silverman9809c5f2022-07-23 16:12:23 -0700202 private:
Brian Silverman76f48362022-08-24 21:09:08 -0700203 friend class OnRunForRust;
204
Brian Silverman9809c5f2022-07-23 16:12:23 -0700205 // Polls the top-level future once. This is what all the callbacks should do.
206 void DoPoll() {
207 if (task_) {
Brian Silverman1431a772022-08-31 20:44:36 -0700208 CHECK(task_->Poll()) << ": Rust panic, aborting";
Brian Silverman9809c5f2022-07-23 16:12:23 -0700209 }
210 }
211
212 EventLoop *const event_loop_;
213
214 std::unique_ptr<ApplicationFuture> task_;
Brian Silverman76f48362022-08-24 21:09:08 -0700215
216 int child_count_ = 0;
Brian Silverman9809c5f2022-07-23 16:12:23 -0700217};
218
219} // namespace aos
220
221#endif // AOS_EVENTS_EVENT_LOOP_RUNTIME_H_