blob: 9a11197301aa6d792d849babe57e1db799a23119 [file] [log] [blame]
Austin Schuh6b6dfa52019-06-12 20:16:20 -07001#include "aos/events/epoll.h"
2
3#include <fcntl.h>
4#include <sys/epoll.h>
5#include <sys/timerfd.h>
6#include <unistd.h>
Austin Schuh4c839682021-07-21 15:30:44 -07007
Austin Schuh6b6dfa52019-06-12 20:16:20 -07008#include <atomic>
9#include <vector>
10
Austin Schuhf257f3c2019-10-27 21:00:43 -070011#include "glog/logging.h"
Austin Schuh6b6dfa52019-06-12 20:16:20 -070012
Philipp Schrader790cb542023-07-05 21:06:52 -070013#include "aos/time/time.h"
14
Austin Schuh6b6dfa52019-06-12 20:16:20 -070015namespace aos {
16namespace internal {
17
18TimerFd::TimerFd()
19 : fd_(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)) {
20 PCHECK(fd_ != -1);
21 Disable();
22}
23
Austin Schuhf257f3c2019-10-27 21:00:43 -070024TimerFd::~TimerFd() { PCHECK(close(fd_) == 0); }
25
Austin Schuh6b6dfa52019-06-12 20:16:20 -070026void TimerFd::SetTime(monotonic_clock::time_point start,
27 monotonic_clock::duration interval) {
James Kuszmaul8866e642022-06-10 16:00:36 -070028 CHECK_GE(start, monotonic_clock::epoch());
Austin Schuh6b6dfa52019-06-12 20:16:20 -070029 struct itimerspec new_value;
30 new_value.it_interval = ::aos::time::to_timespec(interval);
31 new_value.it_value = ::aos::time::to_timespec(start);
32 PCHECK(timerfd_settime(fd_, TFD_TIMER_ABSTIME, &new_value, nullptr) == 0);
33}
34
Austin Schuhde8a8ff2019-11-30 15:25:36 -080035uint64_t TimerFd::Read() {
Austin Schuh6b6dfa52019-06-12 20:16:20 -070036 uint64_t buf;
37 ssize_t result = read(fd_, &buf, sizeof(buf));
Austin Schuhde8a8ff2019-11-30 15:25:36 -080038 if (result == -1) {
39 if (errno == EAGAIN) {
40 return 0;
41 }
42 }
Austin Schuh6b6dfa52019-06-12 20:16:20 -070043 PCHECK(result != -1);
44 CHECK_EQ(result, static_cast<int>(sizeof(buf)));
Austin Schuhde8a8ff2019-11-30 15:25:36 -080045
46 return buf;
Austin Schuh6b6dfa52019-06-12 20:16:20 -070047}
48
49EPoll::EPoll() : epoll_fd_(epoll_create1(EPOLL_CLOEXEC)) {
50 PCHECK(epoll_fd_ > 0);
51
52 // Create a pipe for the Quit function. We want to use a pipe to be async
53 // safe so this can be called from signal handlers.
54 int pipefd[2];
55 PCHECK(pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == 0);
56 quit_epoll_fd_ = pipefd[0];
57 quit_signal_fd_ = pipefd[1];
58 // Read the fd when data is sent and set run_ to false.
59 OnReadable(quit_epoll_fd_, [this]() {
60 run_ = false;
61 char buf[1];
62 PCHECK(read(quit_epoll_fd_, &buf[0], 1) == 1);
63 });
64}
65
66EPoll::~EPoll() {
67 // Clean up the quit pipe and epoll fd.
68 DeleteFd(quit_epoll_fd_);
69 close(quit_signal_fd_);
70 close(quit_epoll_fd_);
Austin Schuhf257f3c2019-10-27 21:00:43 -070071 CHECK_EQ(fns_.size(), 0u)
72 << ": Not all file descriptors were unregistered before shutting down.";
Austin Schuh6b6dfa52019-06-12 20:16:20 -070073 close(epoll_fd_);
74}
75
Austin Schuh219b7782020-12-21 12:01:40 -080076void EPoll::BeforeWait(std::function<void()> function) {
77 before_epoll_wait_functions_.emplace_back(std::move(function));
78}
79
Austin Schuh6b6dfa52019-06-12 20:16:20 -070080void EPoll::Run() {
Brian Silverman441591b2020-01-31 17:44:32 -080081 run_ = true;
James Kuszmaulc8e657e2020-12-14 23:49:45 -080082 // As long as run_ is true or we still have events to process, keep polling.
Austin Schuh6b6dfa52019-06-12 20:16:20 -070083 while (true) {
James Kuszmaulc8e657e2020-12-14 23:49:45 -080084 // If we ran out of events and Quit() was called, quit
85 if (!Poll(run_)) {
86 if (!run_) {
Austin Schuh6b6dfa52019-06-12 20:16:20 -070087 return;
88 }
89 }
Austin Schuh6b6dfa52019-06-12 20:16:20 -070090 }
91}
92
James Kuszmaulc8e657e2020-12-14 23:49:45 -080093bool EPoll::Poll(bool block) {
Austin Schuh219b7782020-12-21 12:01:40 -080094 for (const std::function<void()> &function : before_epoll_wait_functions_) {
95 function();
96 }
James Kuszmaulc8e657e2020-12-14 23:49:45 -080097 // Pull a single event out. Infinite timeout if we are supposed to be
98 // running, and 0 length timeout otherwise. This lets us flush the event
99 // queue before quitting.
100 struct epoll_event event;
101 int num_events = epoll_wait(epoll_fd_, &event, 1, block ? -1 : 0);
102 // Retry on EINTR and nothing else.
103 if (num_events == -1) {
104 if (errno == EINTR) {
105 return false;
106 }
107 PCHECK(num_events != -1);
108 }
109
110 if (num_events == 0) {
111 return false;
112 }
113
114 EventData *const event_data = static_cast<struct EventData *>(event.data.ptr);
Austin Schuh4c839682021-07-21 15:30:44 -0700115 event_data->DoCallbacks(event.events);
James Kuszmaulc8e657e2020-12-14 23:49:45 -0800116 return true;
117}
118
Austin Schuhf74daa62021-07-21 15:29:17 -0700119void EPoll::Quit() {
120 // Shortcut to break us out of infinite loops. We might write more than once
121 // to the pipe, but we'll stop once the first is read on the other end.
122 if (!run_) {
123 return;
124 }
125 PCHECK(write(quit_signal_fd_, "q", 1) == 1);
126}
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700127
128void EPoll::OnReadable(int fd, ::std::function<void()> function) {
Brian Silverman441591b2020-01-31 17:44:32 -0800129 EventData *event_data = GetEventData(fd);
130 if (event_data == nullptr) {
Austin Schuh4c839682021-07-21 15:30:44 -0700131 fns_.emplace_back(std::make_unique<InOutEventData>(fd));
Brian Silverman441591b2020-01-31 17:44:32 -0800132 event_data = fns_.back().get();
133 } else {
Austin Schuh4c839682021-07-21 15:30:44 -0700134 CHECK(!static_cast<InOutEventData *>(event_data)->in_fn)
135 << ": Duplicate in functions for " << fd;
Brian Silverman441591b2020-01-31 17:44:32 -0800136 }
Austin Schuh4c839682021-07-21 15:30:44 -0700137 static_cast<InOutEventData *>(event_data)->in_fn = ::std::move(function);
Brian Silverman441591b2020-01-31 17:44:32 -0800138 DoEpollCtl(event_data, event_data->events | kInEvents);
139}
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700140
Austin Schuh603419f2021-04-24 18:15:20 -0700141void EPoll::OnError(int fd, ::std::function<void()> function) {
142 EventData *event_data = GetEventData(fd);
143 if (event_data == nullptr) {
Austin Schuh4c839682021-07-21 15:30:44 -0700144 fns_.emplace_back(std::make_unique<InOutEventData>(fd));
Austin Schuh603419f2021-04-24 18:15:20 -0700145 event_data = fns_.back().get();
146 } else {
Austin Schuh4c839682021-07-21 15:30:44 -0700147 CHECK(!static_cast<InOutEventData *>(event_data)->err_fn)
148 << ": Duplicate error functions for " << fd;
Austin Schuh603419f2021-04-24 18:15:20 -0700149 }
Austin Schuh4c839682021-07-21 15:30:44 -0700150 static_cast<InOutEventData *>(event_data)->err_fn = ::std::move(function);
Austin Schuh603419f2021-04-24 18:15:20 -0700151 DoEpollCtl(event_data, event_data->events | kErrorEvents);
152}
153
Brian Silverman441591b2020-01-31 17:44:32 -0800154void EPoll::OnWriteable(int fd, ::std::function<void()> function) {
155 EventData *event_data = GetEventData(fd);
156 if (event_data == nullptr) {
Austin Schuh4c839682021-07-21 15:30:44 -0700157 fns_.emplace_back(std::make_unique<InOutEventData>(fd));
Brian Silverman441591b2020-01-31 17:44:32 -0800158 event_data = fns_.back().get();
159 } else {
Austin Schuh4c839682021-07-21 15:30:44 -0700160 CHECK(!static_cast<InOutEventData *>(event_data)->out_fn)
161 << ": Duplicate out functions for " << fd;
Brian Silverman441591b2020-01-31 17:44:32 -0800162 }
Austin Schuh4c839682021-07-21 15:30:44 -0700163 static_cast<InOutEventData *>(event_data)->out_fn = ::std::move(function);
Brian Silverman441591b2020-01-31 17:44:32 -0800164 DoEpollCtl(event_data, event_data->events | kOutEvents);
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700165}
166
Austin Schuh4c839682021-07-21 15:30:44 -0700167void EPoll::OnEvents(int fd, ::std::function<void(uint32_t)> function) {
168 if (GetEventData(fd) != nullptr) {
169 LOG(FATAL) << "May not replace OnEvents handlers";
170 }
171 fns_.emplace_back(std::make_unique<SingleEventData>(fd));
172 static_cast<SingleEventData *>(fns_.back().get())->fn = std::move(function);
173}
174
Austin Schuh219b7782020-12-21 12:01:40 -0800175void EPoll::ForgetClosedFd(int fd) {
176 auto element = fns_.begin();
177 while (fns_.end() != element) {
178 if (element->get()->fd == fd) {
179 fns_.erase(element);
180 return;
181 }
182 ++element;
183 }
184 LOG(FATAL) << "fd " << fd << " not found";
185}
186
Austin Schuh4c839682021-07-21 15:30:44 -0700187void EPoll::SetEvents(int fd, uint32_t events) {
188 DoEpollCtl(CHECK_NOTNULL(GetEventData(fd)), events);
189}
190
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700191// Removes fd from the event loop.
192void EPoll::DeleteFd(int fd) {
193 auto element = fns_.begin();
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700194 while (fns_.end() != element) {
195 if (element->get()->fd == fd) {
196 fns_.erase(element);
Austin Schuh219b7782020-12-21 12:01:40 -0800197 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == 0)
198 << "Failed to delete fd " << fd;
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700199 return;
200 }
201 ++element;
202 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700203 LOG(FATAL) << "fd " << fd << " not found";
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700204}
205
Austin Schuh4c839682021-07-21 15:30:44 -0700206void EPoll::InOutEventData::DoCallbacks(uint32_t events) {
207 if (events & kInEvents) {
208 CHECK(in_fn) << ": No handler registered for input events on " << fd;
209 in_fn();
210 }
211 if (events & kOutEvents) {
212 CHECK(out_fn) << ": No handler registered for output events on " << fd;
213 out_fn();
214 }
215 if (events & kErrorEvents) {
216 CHECK(err_fn) << ": No handler registered for error events on " << fd;
217 err_fn();
218 }
219}
220
Brian Silverman441591b2020-01-31 17:44:32 -0800221void EPoll::EnableEvents(int fd, uint32_t events) {
222 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
223 DoEpollCtl(event_data, event_data->events | events);
224}
225
226void EPoll::DisableEvents(int fd, uint32_t events) {
227 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
228 DoEpollCtl(event_data, event_data->events & ~events);
229}
230
231EPoll::EventData *EPoll::GetEventData(int fd) {
232 const auto iterator = std::find_if(
233 fns_.begin(), fns_.end(),
234 [fd](const std::unique_ptr<EventData> &data) { return data->fd == fd; });
235 if (iterator == fns_.end()) {
236 return nullptr;
237 }
238 return iterator->get();
239}
240
241void EPoll::DoEpollCtl(EventData *event_data, const uint32_t new_events) {
242 const uint32_t old_events = event_data->events;
Austin Schuh4c839682021-07-21 15:30:44 -0700243 if (old_events == new_events) {
244 // Shortcut without calling into the kernel. This happens often with
245 // external event loop integrations that are emulating poll, so make it
246 // fast.
247 return;
248 }
Brian Silverman441591b2020-01-31 17:44:32 -0800249 event_data->events = new_events;
250 if (new_events == 0) {
Brian Silverman441591b2020-01-31 17:44:32 -0800251 // It was added, but should now be removed.
252 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, event_data->fd, nullptr) == 0);
253 return;
254 }
255
256 int operation = EPOLL_CTL_MOD;
257 if (old_events == 0) {
258 // If it wasn't added before, then this is the first time it's being added.
259 operation = EPOLL_CTL_ADD;
260 }
261 struct epoll_event event;
262 event.events = event_data->events;
263 event.data.ptr = event_data;
264 PCHECK(epoll_ctl(epoll_fd_, operation, event_data->fd, &event) == 0)
265 << ": Failed to " << operation << " epoll fd: " << event_data->fd;
266}
267
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700268} // namespace internal
269} // namespace aos