blob: 6bac9839e27384874478132e1f475cf7cad41eb5 [file] [log] [blame]
Brian Silverman74b92d52021-10-14 13:12:02 -07001#include "aos/events/glib_main_loop.h"
2
3#include "glog/logging.h"
4
5namespace aos {
6namespace {
7
8gint EpollToGio(uint32_t epoll) {
9 gint result = 0;
10 if (epoll & EPOLLIN) {
11 result |= G_IO_IN;
12 epoll &= ~EPOLLIN;
13 }
14 if (epoll & EPOLLOUT) {
15 result |= G_IO_OUT;
16 epoll &= ~EPOLLOUT;
17 }
18 if (epoll & (EPOLLRDHUP | EPOLLHUP)) {
19 result |= G_IO_HUP;
20 epoll &= ~(EPOLLRDHUP | EPOLLHUP);
21 }
22 if (epoll & EPOLLPRI) {
23 result |= G_IO_PRI;
24 epoll &= ~EPOLLPRI;
25 }
26 if (epoll & EPOLLERR) {
27 result |= G_IO_ERR;
28 epoll &= ~EPOLLERR;
29 }
30 CHECK_EQ(epoll, 0u) << ": Unhandled epoll bits";
31 return result;
32}
33
34uint32_t GioToEpoll(gint gio) {
35 uint32_t result = 0;
36 if (gio & G_IO_IN) {
37 result |= EPOLLIN;
38 gio &= ~G_IO_IN;
39 }
40 if (gio & G_IO_OUT) {
41 result |= EPOLLOUT;
42 gio &= ~G_IO_OUT;
43 }
44 if (gio & G_IO_HUP) {
45 result |= EPOLLHUP;
46 gio &= ~G_IO_HUP;
47 }
48 if (gio & G_IO_PRI) {
49 result |= EPOLLPRI;
50 gio &= ~G_IO_PRI;
51 }
52 if (gio & G_IO_ERR) {
53 result |= EPOLLERR;
54 gio &= ~G_IO_ERR;
55 }
56 CHECK_EQ(gio, 0) << ": Unhandled gio bits";
57 return result;
58}
59
60} // namespace
61
62GlibMainLoop::GlibMainLoop(ShmEventLoop *event_loop)
63 : event_loop_(event_loop),
64 timeout_timer_(event_loop->AddTimer([]() {
65 // Don't need to do anything, just need to get the event loop to break
66 // out of the kernel and call BeforeWait again.
67 })),
68 g_main_context_(g_main_context_ref(g_main_context_default())),
69 g_main_loop_(g_main_loop_new(g_main_context_, true)) {
70 event_loop_->OnRun([this]() {
71 CHECK(!acquired_context_);
72 CHECK(g_main_context_acquire(g_main_context_))
73 << ": The EventLoop thread must own the context";
74 acquired_context_ = true;
75 });
76 event_loop_->epoll()->BeforeWait([this]() { BeforeWait(); });
77}
78
79GlibMainLoop::~GlibMainLoop() {
80 CHECK_EQ(children_, 0) << ": Failed to destroy all children";
81 RemoveAllFds();
82 if (acquired_context_) {
83 g_main_context_release(g_main_context_);
84 }
85 g_main_loop_unref(g_main_loop_);
86 g_main_context_unref(g_main_context_);
87}
88
89void GlibMainLoop::RemoveAllFds() {
90 while (true) {
91 const auto to_remove = added_fds_.begin();
92 if (to_remove == added_fds_.end()) {
93 break;
94 }
95 event_loop_->epoll()->DeleteFd(*to_remove);
96 added_fds_.erase(to_remove);
97 }
98}
99
100void GlibMainLoop::BeforeWait() {
101 if (!g_main_loop_is_running(g_main_loop_)) {
102 // glib will never quiesce its FDs, so the best we can do is just skip it
103 // once it's done and shut down our event loop. We have to remove all of its
104 // FDs first so other event sources can quiesce.
105 VLOG(1) << "g_main_loop_is_running = false";
106 RemoveAllFds();
107 event_loop_->Exit();
108 return;
109 }
110 if (!event_loop_->epoll()->should_run()) {
111 // Give glib one more round of dispatching.
112 VLOG(1) << "EPoll::should_run = false";
113 g_main_loop_quit(g_main_loop_);
114 }
115
116 if (!gpoll_fds_.empty()) {
117 // Tell glib about any events we received on the FDs it asked about.
118 if (g_main_context_check(g_main_context_, last_query_max_priority_,
119 gpoll_fds_.data(), gpoll_fds_.size())) {
120 VLOG(1) << "g_main_context_dispatch";
121 // We have some glib events now, dispatch them now.
122 g_main_context_dispatch(g_main_context_);
123 }
124 }
125
126 // Call prepare to check for any other events that are ready to be dispatched.
127 // g_main_context_iterate ignores the return value, so we're going to do that
128 // too.
129 g_main_context_prepare(g_main_context_, &last_query_max_priority_);
130
131 gint timeout_ms;
132 while (true) {
133 const gint number_new_fds =
134 g_main_context_query(g_main_context_, last_query_max_priority_,
135 &timeout_ms, gpoll_fds_.data(), gpoll_fds_.size());
136 if (static_cast<size_t>(number_new_fds) <= gpoll_fds_.size()) {
137 // They all fit, resize to drop any stale entries and then we're done.
138 gpoll_fds_.resize(number_new_fds);
139 VLOG(1) << "glib gave " << number_new_fds;
140 break;
141 }
142 // Need more space, we know how much so try again.
143 gpoll_fds_.resize(number_new_fds);
144 }
145
146 for (GPollFD gpoll_fd : gpoll_fds_) {
147 // API docs are a bit unclear, but this shouldn't ever happen I think?
148 CHECK_EQ(gpoll_fd.revents, 0) << ": what does this mean?";
149
150 if (added_fds_.count(gpoll_fd.fd) == 0) {
151 VLOG(1) << "Add to ShmEventLoop: " << gpoll_fd.fd;
152 event_loop_->epoll()->OnEvents(
153 gpoll_fd.fd, [this, fd = gpoll_fd.fd](uint32_t events) {
154 VLOG(1) << "glib " << fd << " triggered: " << std::hex << events;
155 const auto iterator = std::find_if(
156 gpoll_fds_.begin(), gpoll_fds_.end(),
157 [fd](const GPollFD &candidate) { return candidate.fd == fd; });
158 CHECK(iterator != gpoll_fds_.end())
159 << ": Lost GPollFD for " << fd
160 << " but still registered with epoll";
161 iterator->revents |= EpollToGio(events);
162 });
163 added_fds_.insert(gpoll_fd.fd);
164 }
165 event_loop_->epoll()->SetEvents(gpoll_fd.fd, GioToEpoll(gpoll_fd.events));
166 }
167 for (int fd : added_fds_) {
168 const auto iterator = std::find_if(
169 gpoll_fds_.begin(), gpoll_fds_.end(),
170 [fd](const GPollFD &candidate) { return candidate.fd == fd; });
171 if (iterator == gpoll_fds_.end()) {
172 VLOG(1) << "Remove from ShmEventLoop: " << fd;
173 added_fds_.erase(fd);
174 }
175 }
176 CHECK_EQ(added_fds_.size(), gpoll_fds_.size());
177 VLOG(1) << "Timeout: " << timeout_ms;
178 if (timeout_ms == -1) {
179 timeout_timer_->Disable();
180 } else {
181 timeout_timer_->Setup(event_loop_->monotonic_now() +
182 std::chrono::milliseconds(timeout_ms));
183 }
184}
185
186} // namespace aos