blob: 340d0cd839fec8ab5c2dc04812bb1011fd74fa95 [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
Austin Schuh6b6dfa52019-06-12 20:16:20 -070018namespace aos {
19namespace internal {
20
21TimerFd::TimerFd()
22 : fd_(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)) {
23 PCHECK(fd_ != -1);
24 Disable();
25}
26
Austin Schuhf257f3c2019-10-27 21:00:43 -070027TimerFd::~TimerFd() { PCHECK(close(fd_) == 0); }
28
Austin Schuh6b6dfa52019-06-12 20:16:20 -070029void TimerFd::SetTime(monotonic_clock::time_point start,
30 monotonic_clock::duration interval) {
James Kuszmaul8866e642022-06-10 16:00:36 -070031 CHECK_GE(start, monotonic_clock::epoch());
Austin Schuh6b6dfa52019-06-12 20:16:20 -070032 struct itimerspec new_value;
33 new_value.it_interval = ::aos::time::to_timespec(interval);
34 new_value.it_value = ::aos::time::to_timespec(start);
35 PCHECK(timerfd_settime(fd_, TFD_TIMER_ABSTIME, &new_value, nullptr) == 0);
36}
37
Austin Schuhde8a8ff2019-11-30 15:25:36 -080038uint64_t TimerFd::Read() {
Austin Schuh6b6dfa52019-06-12 20:16:20 -070039 uint64_t buf;
40 ssize_t result = read(fd_, &buf, sizeof(buf));
Austin Schuhde8a8ff2019-11-30 15:25:36 -080041 if (result == -1) {
42 if (errno == EAGAIN) {
43 return 0;
44 }
45 }
Austin Schuh6b6dfa52019-06-12 20:16:20 -070046 PCHECK(result != -1);
47 CHECK_EQ(result, static_cast<int>(sizeof(buf)));
Austin Schuhde8a8ff2019-11-30 15:25:36 -080048
49 return buf;
Austin Schuh6b6dfa52019-06-12 20:16:20 -070050}
51
52EPoll::EPoll() : epoll_fd_(epoll_create1(EPOLL_CLOEXEC)) {
53 PCHECK(epoll_fd_ > 0);
54
55 // Create a pipe for the Quit function. We want to use a pipe to be async
56 // safe so this can be called from signal handlers.
57 int pipefd[2];
58 PCHECK(pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == 0);
59 quit_epoll_fd_ = pipefd[0];
60 quit_signal_fd_ = pipefd[1];
61 // Read the fd when data is sent and set run_ to false.
62 OnReadable(quit_epoll_fd_, [this]() {
63 run_ = false;
64 char buf[1];
65 PCHECK(read(quit_epoll_fd_, &buf[0], 1) == 1);
66 });
67}
68
69EPoll::~EPoll() {
70 // Clean up the quit pipe and epoll fd.
71 DeleteFd(quit_epoll_fd_);
72 close(quit_signal_fd_);
73 close(quit_epoll_fd_);
Austin Schuhf257f3c2019-10-27 21:00:43 -070074 CHECK_EQ(fns_.size(), 0u)
75 << ": Not all file descriptors were unregistered before shutting down.";
Austin Schuh6b6dfa52019-06-12 20:16:20 -070076 close(epoll_fd_);
77}
78
Austin Schuh219b7782020-12-21 12:01:40 -080079void EPoll::BeforeWait(std::function<void()> function) {
80 before_epoll_wait_functions_.emplace_back(std::move(function));
81}
82
Austin Schuh6b6dfa52019-06-12 20:16:20 -070083void EPoll::Run() {
Brian Silverman441591b2020-01-31 17:44:32 -080084 run_ = true;
James Kuszmaulc8e657e2020-12-14 23:49:45 -080085 // As long as run_ is true or we still have events to process, keep polling.
Austin Schuh6b6dfa52019-06-12 20:16:20 -070086 while (true) {
James Kuszmaulc8e657e2020-12-14 23:49:45 -080087 // If we ran out of events and Quit() was called, quit
88 if (!Poll(run_)) {
89 if (!run_) {
Austin Schuh6b6dfa52019-06-12 20:16:20 -070090 return;
91 }
92 }
Austin Schuh6b6dfa52019-06-12 20:16:20 -070093 }
94}
95
James Kuszmaulc8e657e2020-12-14 23:49:45 -080096bool EPoll::Poll(bool block) {
Austin Schuh219b7782020-12-21 12:01:40 -080097 for (const std::function<void()> &function : before_epoll_wait_functions_) {
98 function();
99 }
James Kuszmaulc8e657e2020-12-14 23:49:45 -0800100 // Pull a single event out. Infinite timeout if we are supposed to be
101 // running, and 0 length timeout otherwise. This lets us flush the event
102 // queue before quitting.
103 struct epoll_event event;
104 int num_events = epoll_wait(epoll_fd_, &event, 1, block ? -1 : 0);
105 // Retry on EINTR and nothing else.
106 if (num_events == -1) {
107 if (errno == EINTR) {
108 return false;
109 }
110 PCHECK(num_events != -1);
111 }
112
113 if (num_events == 0) {
114 return false;
115 }
116
117 EventData *const event_data = static_cast<struct EventData *>(event.data.ptr);
Austin Schuh4c839682021-07-21 15:30:44 -0700118 event_data->DoCallbacks(event.events);
James Kuszmaulc8e657e2020-12-14 23:49:45 -0800119 return true;
120}
121
Austin Schuhf74daa62021-07-21 15:29:17 -0700122void EPoll::Quit() {
123 // Shortcut to break us out of infinite loops. We might write more than once
124 // to the pipe, but we'll stop once the first is read on the other end.
125 if (!run_) {
126 return;
127 }
128 PCHECK(write(quit_signal_fd_, "q", 1) == 1);
129}
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700130
131void EPoll::OnReadable(int fd, ::std::function<void()> function) {
Brian Silverman441591b2020-01-31 17:44:32 -0800132 EventData *event_data = GetEventData(fd);
133 if (event_data == nullptr) {
Austin Schuh4c839682021-07-21 15:30:44 -0700134 fns_.emplace_back(std::make_unique<InOutEventData>(fd));
Brian Silverman441591b2020-01-31 17:44:32 -0800135 event_data = fns_.back().get();
136 } else {
Austin Schuh4c839682021-07-21 15:30:44 -0700137 CHECK(!static_cast<InOutEventData *>(event_data)->in_fn)
138 << ": Duplicate in functions for " << fd;
Brian Silverman441591b2020-01-31 17:44:32 -0800139 }
Austin Schuh4c839682021-07-21 15:30:44 -0700140 static_cast<InOutEventData *>(event_data)->in_fn = ::std::move(function);
Brian Silverman441591b2020-01-31 17:44:32 -0800141 DoEpollCtl(event_data, event_data->events | kInEvents);
142}
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700143
Austin Schuh603419f2021-04-24 18:15:20 -0700144void EPoll::OnError(int fd, ::std::function<void()> function) {
145 EventData *event_data = GetEventData(fd);
146 if (event_data == nullptr) {
Austin Schuh4c839682021-07-21 15:30:44 -0700147 fns_.emplace_back(std::make_unique<InOutEventData>(fd));
Austin Schuh603419f2021-04-24 18:15:20 -0700148 event_data = fns_.back().get();
149 } else {
Austin Schuh4c839682021-07-21 15:30:44 -0700150 CHECK(!static_cast<InOutEventData *>(event_data)->err_fn)
151 << ": Duplicate error functions for " << fd;
Austin Schuh603419f2021-04-24 18:15:20 -0700152 }
Austin Schuh4c839682021-07-21 15:30:44 -0700153 static_cast<InOutEventData *>(event_data)->err_fn = ::std::move(function);
Austin Schuh603419f2021-04-24 18:15:20 -0700154 DoEpollCtl(event_data, event_data->events | kErrorEvents);
155}
156
Brian Silverman441591b2020-01-31 17:44:32 -0800157void EPoll::OnWriteable(int fd, ::std::function<void()> function) {
158 EventData *event_data = GetEventData(fd);
159 if (event_data == nullptr) {
Austin Schuh4c839682021-07-21 15:30:44 -0700160 fns_.emplace_back(std::make_unique<InOutEventData>(fd));
Brian Silverman441591b2020-01-31 17:44:32 -0800161 event_data = fns_.back().get();
162 } else {
Austin Schuh4c839682021-07-21 15:30:44 -0700163 CHECK(!static_cast<InOutEventData *>(event_data)->out_fn)
164 << ": Duplicate out functions for " << fd;
Brian Silverman441591b2020-01-31 17:44:32 -0800165 }
Austin Schuh4c839682021-07-21 15:30:44 -0700166 static_cast<InOutEventData *>(event_data)->out_fn = ::std::move(function);
Brian Silverman441591b2020-01-31 17:44:32 -0800167 DoEpollCtl(event_data, event_data->events | kOutEvents);
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700168}
169
Austin Schuh4c839682021-07-21 15:30:44 -0700170void EPoll::OnEvents(int fd, ::std::function<void(uint32_t)> function) {
171 if (GetEventData(fd) != nullptr) {
172 LOG(FATAL) << "May not replace OnEvents handlers";
173 }
174 fns_.emplace_back(std::make_unique<SingleEventData>(fd));
175 static_cast<SingleEventData *>(fns_.back().get())->fn = std::move(function);
176}
177
Austin Schuh219b7782020-12-21 12:01:40 -0800178void EPoll::ForgetClosedFd(int fd) {
179 auto element = fns_.begin();
180 while (fns_.end() != element) {
181 if (element->get()->fd == fd) {
182 fns_.erase(element);
183 return;
184 }
185 ++element;
186 }
187 LOG(FATAL) << "fd " << fd << " not found";
188}
189
Austin Schuh4c839682021-07-21 15:30:44 -0700190void EPoll::SetEvents(int fd, uint32_t events) {
191 DoEpollCtl(CHECK_NOTNULL(GetEventData(fd)), events);
192}
193
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700194// Removes fd from the event loop.
195void EPoll::DeleteFd(int fd) {
196 auto element = fns_.begin();
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700197 while (fns_.end() != element) {
198 if (element->get()->fd == fd) {
199 fns_.erase(element);
Austin Schuh219b7782020-12-21 12:01:40 -0800200 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == 0)
201 << "Failed to delete fd " << fd;
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700202 return;
203 }
204 ++element;
205 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700206 LOG(FATAL) << "fd " << fd << " not found";
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700207}
208
Kiran Mohanfed1a7e2023-10-19 11:05:27 -0700209namespace {
210bool IsSocket(int fd) {
211 struct stat st;
212 if (fstat(fd, &st) == -1) {
213 return false;
214 }
215 return static_cast<bool>(S_ISSOCK(st.st_mode));
216}
217
218::std::string GetSocketErrorStr(int fd) {
219 ::std::string error_str;
220 if (IsSocket(fd)) {
221 int error = 0;
222 socklen_t errlen = sizeof(error);
223 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&error, &errlen) == 0) {
224 if (error) {
225 error_str = "Socket error: " + ::std::string(strerror(error));
226 }
227 }
228 }
229 return error_str;
230}
231} // namespace
232
Austin Schuh4c839682021-07-21 15:30:44 -0700233void EPoll::InOutEventData::DoCallbacks(uint32_t events) {
234 if (events & kInEvents) {
Kiran Mohanfed1a7e2023-10-19 11:05:27 -0700235 CHECK(in_fn) << ": No handler registered for input events on descriptor "
236 << fd << ". Received events = 0x" << std::hex << events
237 << std::dec;
Austin Schuh4c839682021-07-21 15:30:44 -0700238 in_fn();
239 }
240 if (events & kOutEvents) {
Kiran Mohanfed1a7e2023-10-19 11:05:27 -0700241 CHECK(out_fn) << ": No handler registered for output events on descriptor "
242 << fd << ". Received events = 0x" << std::hex << events
243 << std::dec;
Austin Schuh4c839682021-07-21 15:30:44 -0700244 out_fn();
245 }
246 if (events & kErrorEvents) {
Kiran Mohanfed1a7e2023-10-19 11:05:27 -0700247 CHECK(err_fn) << ": No handler registered for error events on descriptor "
248 << fd << ". Received events = 0x" << std::hex << events
249 << std::dec << ". " << GetSocketErrorStr(fd);
Austin Schuh4c839682021-07-21 15:30:44 -0700250 err_fn();
251 }
252}
253
Brian Silverman441591b2020-01-31 17:44:32 -0800254void EPoll::EnableEvents(int fd, uint32_t events) {
255 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
256 DoEpollCtl(event_data, event_data->events | events);
257}
258
259void EPoll::DisableEvents(int fd, uint32_t events) {
260 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
261 DoEpollCtl(event_data, event_data->events & ~events);
262}
263
264EPoll::EventData *EPoll::GetEventData(int fd) {
265 const auto iterator = std::find_if(
266 fns_.begin(), fns_.end(),
267 [fd](const std::unique_ptr<EventData> &data) { return data->fd == fd; });
268 if (iterator == fns_.end()) {
269 return nullptr;
270 }
271 return iterator->get();
272}
273
274void EPoll::DoEpollCtl(EventData *event_data, const uint32_t new_events) {
275 const uint32_t old_events = event_data->events;
Austin Schuh4c839682021-07-21 15:30:44 -0700276 if (old_events == new_events) {
277 // Shortcut without calling into the kernel. This happens often with
278 // external event loop integrations that are emulating poll, so make it
279 // fast.
280 return;
281 }
Brian Silverman441591b2020-01-31 17:44:32 -0800282 event_data->events = new_events;
283 if (new_events == 0) {
Brian Silverman441591b2020-01-31 17:44:32 -0800284 // It was added, but should now be removed.
285 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, event_data->fd, nullptr) == 0);
286 return;
287 }
288
289 int operation = EPOLL_CTL_MOD;
290 if (old_events == 0) {
291 // If it wasn't added before, then this is the first time it's being added.
292 operation = EPOLL_CTL_ADD;
293 }
294 struct epoll_event event;
295 event.events = event_data->events;
296 event.data.ptr = event_data;
297 PCHECK(epoll_ctl(epoll_fd_, operation, event_data->fd, &event) == 0)
298 << ": Failed to " << operation << " epoll fd: " << event_data->fd;
299}
300
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700301} // namespace internal
302} // namespace aos