blob: df76d67c5eb9a033d66b7695502c418045f5157f [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
127class EventLoopRuntime {
128 public:
129 EventLoopRuntime(EventLoop *event_loop) : event_loop_(event_loop) {}
130 ~EventLoopRuntime() = default;
131
132 EventLoop *event_loop() { return event_loop_; }
133
134 void spawn(std::unique_ptr<ApplicationFuture> task) {
135 CHECK(!task_) << ": May only call spawn once";
136 task_ = std::move(task);
137 // TODO(Brian): Do this once we've got OnRun support.
138 // DoPoll();
139 // TODO(Brian): Once we have OnRun support, should this move there or stay
140 // here unconditionally?
141 event_loop_->OnRun([this] { DoPoll(); });
142 }
143
144 const Configuration *configuration() const {
145 return event_loop_->configuration();
146 }
147 const Node *node() const { return event_loop_->node(); }
148
149 // autocxx generates broken C++ code for `time_point`, see
150 // https://github.com/google/autocxx/issues/787.
151 int64_t monotonic_now() const {
152 return std::chrono::nanoseconds(
153 event_loop_->monotonic_now().time_since_epoch())
154 .count();
155 }
156 int64_t realtime_now() const {
157 return std::chrono::nanoseconds(
158 event_loop_->realtime_now().time_since_epoch())
159 .count();
160 }
161
162 rust::Str name() const { return StringViewToRustStr(event_loop_->name()); }
163
164 WatcherForRust MakeWatcher(const Channel *channel) {
165 event_loop_->MakeRawNoArgWatcher(channel,
166 [this](const Context &) { DoPoll(); });
167 return WatcherForRust(event_loop_->MakeRawFetcher(channel));
168 }
169
170 SenderForRust MakeSender(const Channel *channel) {
171 return SenderForRust(event_loop_->MakeRawSender(channel));
172 }
173
174 FetcherForRust MakeFetcher(const Channel *channel) {
175 return FetcherForRust(event_loop_->MakeRawFetcher(channel));
176 }
177
178 private:
179 // Polls the top-level future once. This is what all the callbacks should do.
180 void DoPoll() {
181 if (task_) {
182 task_->Poll();
183 }
184 }
185
186 EventLoop *const event_loop_;
187
188 std::unique_ptr<ApplicationFuture> task_;
189};
190
191} // namespace aos
192
193#endif // AOS_EVENTS_EVENT_LOOP_RUNTIME_H_