blob: faa5ad780e84488bfdc4c0ae341d5692b365585e [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.
74 virtual void Poll() = 0;
75};
76
77// Similar to Rust's `Stream<Item = const Option&>`.
78class WatcherForRust {
79 public:
80 WatcherForRust(std::unique_ptr<RawFetcher> fetcher)
81 : fetcher_(std::move(fetcher)) {}
82 ~WatcherForRust() = default;
83
84 const Context *PollNext() {
85 if (!fetcher_->FetchNext()) {
86 return nullptr;
87 }
88 return &fetcher_->context();
89 }
90
91 private:
92 const std::unique_ptr<RawFetcher> fetcher_;
93};
94
95class SenderForRust {
96 public:
97 SenderForRust(std::unique_ptr<RawSender> sender)
98 : sender_(std::move(sender)) {}
99 ~SenderForRust() = default;
100
101 uint8_t *data() { return reinterpret_cast<uint8_t *>(sender_->data()); }
102 size_t size() { return sender_->size(); }
103 RawSender::Error SendBuffer(size_t size) { return sender_->Send(size); }
104 RawSender::Error CopyAndSend(const uint8_t *data, size_t size) {
105 return sender_->Send(data, size);
106 }
107
108 private:
109 const std::unique_ptr<RawSender> sender_;
110};
111
112class FetcherForRust {
113 public:
114 FetcherForRust(std::unique_ptr<RawFetcher> fetcher)
115 : fetcher_(std::move(fetcher)) {}
116 ~FetcherForRust() = default;
117
118 bool FetchNext() { return fetcher_->FetchNext(); }
119 bool Fetch() { return fetcher_->Fetch(); }
120
121 const Context &context() const { return fetcher_->context(); }
122
123 private:
124 const std::unique_ptr<RawFetcher> fetcher_;
125};
126
Brian Silverman76f48362022-08-24 21:09:08 -0700127class EventLoopRuntime;
128
129class OnRunForRust {
130 public:
131 OnRunForRust(EventLoopRuntime *runtime);
132 ~OnRunForRust();
133
134 bool is_running() const;
135
136 private:
137 EventLoopRuntime *const runtime_;
138};
139
Brian Silverman9809c5f2022-07-23 16:12:23 -0700140class EventLoopRuntime {
141 public:
142 EventLoopRuntime(EventLoop *event_loop) : event_loop_(event_loop) {}
Brian Silverman76f48362022-08-24 21:09:08 -0700143 ~EventLoopRuntime() {
144 CHECK_EQ(child_count_, 0)
145 << ": Some child objects were not destroyed first";
146 }
Brian Silverman9809c5f2022-07-23 16:12:23 -0700147
148 EventLoop *event_loop() { return event_loop_; }
149
150 void spawn(std::unique_ptr<ApplicationFuture> task) {
151 CHECK(!task_) << ": May only call spawn once";
152 task_ = std::move(task);
Brian Silverman76f48362022-08-24 21:09:08 -0700153 DoPoll();
154 // Just do this unconditionally, so we don't have to keep track of each
155 // OnRun to only do it once. If Rust doesn't use OnRun, it's harmless to do
156 // an extra poll.
Brian Silverman9809c5f2022-07-23 16:12:23 -0700157 event_loop_->OnRun([this] { DoPoll(); });
158 }
159
160 const Configuration *configuration() const {
161 return event_loop_->configuration();
162 }
163 const Node *node() const { return event_loop_->node(); }
164
Brian Silverman76f48362022-08-24 21:09:08 -0700165 bool is_running() const { return event_loop_->is_running(); }
166
Brian Silverman9809c5f2022-07-23 16:12:23 -0700167 // autocxx generates broken C++ code for `time_point`, see
168 // https://github.com/google/autocxx/issues/787.
169 int64_t monotonic_now() const {
170 return std::chrono::nanoseconds(
171 event_loop_->monotonic_now().time_since_epoch())
172 .count();
173 }
174 int64_t realtime_now() const {
175 return std::chrono::nanoseconds(
176 event_loop_->realtime_now().time_since_epoch())
177 .count();
178 }
179
180 rust::Str name() const { return StringViewToRustStr(event_loop_->name()); }
181
182 WatcherForRust MakeWatcher(const Channel *channel) {
183 event_loop_->MakeRawNoArgWatcher(channel,
184 [this](const Context &) { DoPoll(); });
185 return WatcherForRust(event_loop_->MakeRawFetcher(channel));
186 }
187
188 SenderForRust MakeSender(const Channel *channel) {
189 return SenderForRust(event_loop_->MakeRawSender(channel));
190 }
191
192 FetcherForRust MakeFetcher(const Channel *channel) {
193 return FetcherForRust(event_loop_->MakeRawFetcher(channel));
194 }
195
Brian Silverman76f48362022-08-24 21:09:08 -0700196 OnRunForRust MakeOnRun() { return OnRunForRust(this); }
197
Brian Silverman9809c5f2022-07-23 16:12:23 -0700198 private:
Brian Silverman76f48362022-08-24 21:09:08 -0700199 friend class OnRunForRust;
200
Brian Silverman9809c5f2022-07-23 16:12:23 -0700201 // Polls the top-level future once. This is what all the callbacks should do.
202 void DoPoll() {
203 if (task_) {
204 task_->Poll();
205 }
206 }
207
208 EventLoop *const event_loop_;
209
210 std::unique_ptr<ApplicationFuture> task_;
Brian Silverman76f48362022-08-24 21:09:08 -0700211
212 int child_count_ = 0;
Brian Silverman9809c5f2022-07-23 16:12:23 -0700213};
214
215} // namespace aos
216
217#endif // AOS_EVENTS_EVENT_LOOP_RUNTIME_H_