blob: 8c5c3e13875adf86112bef7a7289a3ca12cb5699 [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>
Kiran Mohanfed1a7e2023-10-19 11:05:27 -07005#include <sys/socket.h>
6#include <sys/stat.h>
Austin Schuh6b6dfa52019-06-12 20:16:20 -07007#include <sys/timerfd.h>
8#include <unistd.h>
Austin Schuh4c839682021-07-21 15:30:44 -07009
Austin Schuh6b6dfa52019-06-12 20:16:20 -070010#include <atomic>
Kiran Mohanfed1a7e2023-10-19 11:05:27 -070011#include <cstring>
Austin Schuh6b6dfa52019-06-12 20:16:20 -070012#include <vector>
13
Austin Schuhf257f3c2019-10-27 21:00:43 -070014#include "glog/logging.h"
Austin Schuh6b6dfa52019-06-12 20:16:20 -070015
Philipp Schrader790cb542023-07-05 21:06:52 -070016#include "aos/time/time.h"
17
Stephan Pleinesf63bde82024-01-13 15:59:33 -080018namespace aos::internal {
Austin Schuh6b6dfa52019-06-12 20:16:20 -070019
20TimerFd::TimerFd()
21 : fd_(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)) {
22 PCHECK(fd_ != -1);
23 Disable();
24}
25
Austin Schuhf257f3c2019-10-27 21:00:43 -070026TimerFd::~TimerFd() { PCHECK(close(fd_) == 0); }
27
Austin Schuh6b6dfa52019-06-12 20:16:20 -070028void TimerFd::SetTime(monotonic_clock::time_point start,
29 monotonic_clock::duration interval) {
James Kuszmaul8866e642022-06-10 16:00:36 -070030 CHECK_GE(start, monotonic_clock::epoch());
Austin Schuh6b6dfa52019-06-12 20:16:20 -070031 struct itimerspec new_value;
32 new_value.it_interval = ::aos::time::to_timespec(interval);
33 new_value.it_value = ::aos::time::to_timespec(start);
34 PCHECK(timerfd_settime(fd_, TFD_TIMER_ABSTIME, &new_value, nullptr) == 0);
35}
36
Austin Schuhde8a8ff2019-11-30 15:25:36 -080037uint64_t TimerFd::Read() {
Austin Schuh6b6dfa52019-06-12 20:16:20 -070038 uint64_t buf;
39 ssize_t result = read(fd_, &buf, sizeof(buf));
Austin Schuhde8a8ff2019-11-30 15:25:36 -080040 if (result == -1) {
41 if (errno == EAGAIN) {
42 return 0;
43 }
44 }
Austin Schuh6b6dfa52019-06-12 20:16:20 -070045 PCHECK(result != -1);
46 CHECK_EQ(result, static_cast<int>(sizeof(buf)));
Austin Schuhde8a8ff2019-11-30 15:25:36 -080047
48 return buf;
Austin Schuh6b6dfa52019-06-12 20:16:20 -070049}
50
51EPoll::EPoll() : epoll_fd_(epoll_create1(EPOLL_CLOEXEC)) {
52 PCHECK(epoll_fd_ > 0);
53
54 // Create a pipe for the Quit function. We want to use a pipe to be async
55 // safe so this can be called from signal handlers.
56 int pipefd[2];
57 PCHECK(pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == 0);
58 quit_epoll_fd_ = pipefd[0];
59 quit_signal_fd_ = pipefd[1];
60 // Read the fd when data is sent and set run_ to false.
61 OnReadable(quit_epoll_fd_, [this]() {
62 run_ = false;
63 char buf[1];
64 PCHECK(read(quit_epoll_fd_, &buf[0], 1) == 1);
65 });
66}
67
68EPoll::~EPoll() {
69 // Clean up the quit pipe and epoll fd.
70 DeleteFd(quit_epoll_fd_);
71 close(quit_signal_fd_);
72 close(quit_epoll_fd_);
Austin Schuhf257f3c2019-10-27 21:00:43 -070073 CHECK_EQ(fns_.size(), 0u)
74 << ": Not all file descriptors were unregistered before shutting down.";
Austin Schuh6b6dfa52019-06-12 20:16:20 -070075 close(epoll_fd_);
76}
77
Austin Schuh219b7782020-12-21 12:01:40 -080078void EPoll::BeforeWait(std::function<void()> function) {
79 before_epoll_wait_functions_.emplace_back(std::move(function));
80}
81
Austin Schuh6b6dfa52019-06-12 20:16:20 -070082void EPoll::Run() {
Brian Silverman441591b2020-01-31 17:44:32 -080083 run_ = true;
James Kuszmaulc8e657e2020-12-14 23:49:45 -080084 // As long as run_ is true or we still have events to process, keep polling.
Austin Schuh6b6dfa52019-06-12 20:16:20 -070085 while (true) {
James Kuszmaulc8e657e2020-12-14 23:49:45 -080086 // If we ran out of events and Quit() was called, quit
87 if (!Poll(run_)) {
88 if (!run_) {
Austin Schuh6b6dfa52019-06-12 20:16:20 -070089 return;
90 }
91 }
Austin Schuh6b6dfa52019-06-12 20:16:20 -070092 }
93}
94
James Kuszmaulc8e657e2020-12-14 23:49:45 -080095bool EPoll::Poll(bool block) {
Austin Schuh219b7782020-12-21 12:01:40 -080096 for (const std::function<void()> &function : before_epoll_wait_functions_) {
97 function();
98 }
James Kuszmaulc8e657e2020-12-14 23:49:45 -080099 // Pull a single event out. Infinite timeout if we are supposed to be
100 // running, and 0 length timeout otherwise. This lets us flush the event
101 // queue before quitting.
102 struct epoll_event event;
103 int num_events = epoll_wait(epoll_fd_, &event, 1, block ? -1 : 0);
104 // Retry on EINTR and nothing else.
105 if (num_events == -1) {
106 if (errno == EINTR) {
107 return false;
108 }
109 PCHECK(num_events != -1);
110 }
111
112 if (num_events == 0) {
113 return false;
114 }
115
116 EventData *const event_data = static_cast<struct EventData *>(event.data.ptr);
Austin Schuh4c839682021-07-21 15:30:44 -0700117 event_data->DoCallbacks(event.events);
James Kuszmaulc8e657e2020-12-14 23:49:45 -0800118 return true;
119}
120
Austin Schuhf74daa62021-07-21 15:29:17 -0700121void EPoll::Quit() {
122 // Shortcut to break us out of infinite loops. We might write more than once
123 // to the pipe, but we'll stop once the first is read on the other end.
124 if (!run_) {
125 return;
126 }
127 PCHECK(write(quit_signal_fd_, "q", 1) == 1);
128}
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700129
130void EPoll::OnReadable(int fd, ::std::function<void()> function) {
Brian Silverman441591b2020-01-31 17:44:32 -0800131 EventData *event_data = GetEventData(fd);
132 if (event_data == nullptr) {
Austin Schuh4c839682021-07-21 15:30:44 -0700133 fns_.emplace_back(std::make_unique<InOutEventData>(fd));
Brian Silverman441591b2020-01-31 17:44:32 -0800134 event_data = fns_.back().get();
135 } else {
Austin Schuh4c839682021-07-21 15:30:44 -0700136 CHECK(!static_cast<InOutEventData *>(event_data)->in_fn)
137 << ": Duplicate in functions for " << fd;
Brian Silverman441591b2020-01-31 17:44:32 -0800138 }
Austin Schuh4c839682021-07-21 15:30:44 -0700139 static_cast<InOutEventData *>(event_data)->in_fn = ::std::move(function);
Brian Silverman441591b2020-01-31 17:44:32 -0800140 DoEpollCtl(event_data, event_data->events | kInEvents);
141}
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700142
Austin Schuh603419f2021-04-24 18:15:20 -0700143void EPoll::OnError(int fd, ::std::function<void()> function) {
144 EventData *event_data = GetEventData(fd);
145 if (event_data == nullptr) {
Austin Schuh4c839682021-07-21 15:30:44 -0700146 fns_.emplace_back(std::make_unique<InOutEventData>(fd));
Austin Schuh603419f2021-04-24 18:15:20 -0700147 event_data = fns_.back().get();
148 } else {
Austin Schuh4c839682021-07-21 15:30:44 -0700149 CHECK(!static_cast<InOutEventData *>(event_data)->err_fn)
150 << ": Duplicate error functions for " << fd;
Austin Schuh603419f2021-04-24 18:15:20 -0700151 }
Austin Schuh4c839682021-07-21 15:30:44 -0700152 static_cast<InOutEventData *>(event_data)->err_fn = ::std::move(function);
Austin Schuh603419f2021-04-24 18:15:20 -0700153 DoEpollCtl(event_data, event_data->events | kErrorEvents);
154}
155
Brian Silverman441591b2020-01-31 17:44:32 -0800156void EPoll::OnWriteable(int fd, ::std::function<void()> function) {
157 EventData *event_data = GetEventData(fd);
158 if (event_data == nullptr) {
Austin Schuh4c839682021-07-21 15:30:44 -0700159 fns_.emplace_back(std::make_unique<InOutEventData>(fd));
Brian Silverman441591b2020-01-31 17:44:32 -0800160 event_data = fns_.back().get();
161 } else {
Austin Schuh4c839682021-07-21 15:30:44 -0700162 CHECK(!static_cast<InOutEventData *>(event_data)->out_fn)
163 << ": Duplicate out functions for " << fd;
Brian Silverman441591b2020-01-31 17:44:32 -0800164 }
Austin Schuh4c839682021-07-21 15:30:44 -0700165 static_cast<InOutEventData *>(event_data)->out_fn = ::std::move(function);
Brian Silverman441591b2020-01-31 17:44:32 -0800166 DoEpollCtl(event_data, event_data->events | kOutEvents);
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700167}
168
Austin Schuh4c839682021-07-21 15:30:44 -0700169void EPoll::OnEvents(int fd, ::std::function<void(uint32_t)> function) {
170 if (GetEventData(fd) != nullptr) {
171 LOG(FATAL) << "May not replace OnEvents handlers";
172 }
173 fns_.emplace_back(std::make_unique<SingleEventData>(fd));
174 static_cast<SingleEventData *>(fns_.back().get())->fn = std::move(function);
175}
176
Austin Schuh219b7782020-12-21 12:01:40 -0800177void EPoll::ForgetClosedFd(int fd) {
178 auto element = fns_.begin();
179 while (fns_.end() != element) {
180 if (element->get()->fd == fd) {
181 fns_.erase(element);
182 return;
183 }
184 ++element;
185 }
186 LOG(FATAL) << "fd " << fd << " not found";
187}
188
Austin Schuh4c839682021-07-21 15:30:44 -0700189void EPoll::SetEvents(int fd, uint32_t events) {
190 DoEpollCtl(CHECK_NOTNULL(GetEventData(fd)), events);
191}
192
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700193// Removes fd from the event loop.
194void EPoll::DeleteFd(int fd) {
195 auto element = fns_.begin();
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700196 while (fns_.end() != element) {
197 if (element->get()->fd == fd) {
198 fns_.erase(element);
Austin Schuh219b7782020-12-21 12:01:40 -0800199 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == 0)
200 << "Failed to delete fd " << fd;
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700201 return;
202 }
203 ++element;
204 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700205 LOG(FATAL) << "fd " << fd << " not found";
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700206}
207
Kiran Mohanfed1a7e2023-10-19 11:05:27 -0700208namespace {
209bool IsSocket(int fd) {
210 struct stat st;
211 if (fstat(fd, &st) == -1) {
212 return false;
213 }
214 return static_cast<bool>(S_ISSOCK(st.st_mode));
215}
216
217::std::string GetSocketErrorStr(int fd) {
218 ::std::string error_str;
219 if (IsSocket(fd)) {
220 int error = 0;
221 socklen_t errlen = sizeof(error);
222 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&error, &errlen) == 0) {
223 if (error) {
224 error_str = "Socket error: " + ::std::string(strerror(error));
225 }
226 }
227 }
228 return error_str;
229}
230} // namespace
231
Austin Schuh4c839682021-07-21 15:30:44 -0700232void EPoll::InOutEventData::DoCallbacks(uint32_t events) {
233 if (events & kInEvents) {
Kiran Mohanfed1a7e2023-10-19 11:05:27 -0700234 CHECK(in_fn) << ": No handler registered for input events on descriptor "
235 << fd << ". Received events = 0x" << std::hex << events
236 << std::dec;
Austin Schuh4c839682021-07-21 15:30:44 -0700237 in_fn();
238 }
239 if (events & kOutEvents) {
Kiran Mohanfed1a7e2023-10-19 11:05:27 -0700240 CHECK(out_fn) << ": No handler registered for output events on descriptor "
241 << fd << ". Received events = 0x" << std::hex << events
242 << std::dec;
Austin Schuh4c839682021-07-21 15:30:44 -0700243 out_fn();
244 }
245 if (events & kErrorEvents) {
Kiran Mohanfed1a7e2023-10-19 11:05:27 -0700246 CHECK(err_fn) << ": No handler registered for error events on descriptor "
247 << fd << ". Received events = 0x" << std::hex << events
248 << std::dec << ". " << GetSocketErrorStr(fd);
Austin Schuh4c839682021-07-21 15:30:44 -0700249 err_fn();
250 }
251}
252
Brian Silverman441591b2020-01-31 17:44:32 -0800253void EPoll::EnableEvents(int fd, uint32_t events) {
254 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
255 DoEpollCtl(event_data, event_data->events | events);
256}
257
258void EPoll::DisableEvents(int fd, uint32_t events) {
259 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
260 DoEpollCtl(event_data, event_data->events & ~events);
261}
262
263EPoll::EventData *EPoll::GetEventData(int fd) {
264 const auto iterator = std::find_if(
265 fns_.begin(), fns_.end(),
266 [fd](const std::unique_ptr<EventData> &data) { return data->fd == fd; });
267 if (iterator == fns_.end()) {
268 return nullptr;
269 }
270 return iterator->get();
271}
272
273void EPoll::DoEpollCtl(EventData *event_data, const uint32_t new_events) {
274 const uint32_t old_events = event_data->events;
Austin Schuh4c839682021-07-21 15:30:44 -0700275 if (old_events == new_events) {
276 // Shortcut without calling into the kernel. This happens often with
277 // external event loop integrations that are emulating poll, so make it
278 // fast.
279 return;
280 }
Brian Silverman441591b2020-01-31 17:44:32 -0800281 event_data->events = new_events;
282 if (new_events == 0) {
Brian Silverman441591b2020-01-31 17:44:32 -0800283 // It was added, but should now be removed.
284 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, event_data->fd, nullptr) == 0);
285 return;
286 }
287
288 int operation = EPOLL_CTL_MOD;
289 if (old_events == 0) {
290 // If it wasn't added before, then this is the first time it's being added.
291 operation = EPOLL_CTL_ADD;
292 }
293 struct epoll_event event;
294 event.events = event_data->events;
295 event.data.ptr = event_data;
296 PCHECK(epoll_ctl(epoll_fd_, operation, event_data->fd, &event) == 0)
297 << ": Failed to " << operation << " epoll fd: " << event_data->fd;
298}
299
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800300} // namespace aos::internal