blob: 71e3c9b2a39466b401727ee0aa12faa474cdf654 [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 Schuh6b6dfa52019-06-12 20:16:20 -070011#include "aos/time/time.h"
Austin Schuhf257f3c2019-10-27 21:00:43 -070012#include "glog/logging.h"
Austin Schuh6b6dfa52019-06-12 20:16:20 -070013
14namespace aos {
15namespace internal {
16
17TimerFd::TimerFd()
18 : fd_(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)) {
19 PCHECK(fd_ != -1);
20 Disable();
21}
22
Austin Schuhf257f3c2019-10-27 21:00:43 -070023TimerFd::~TimerFd() { PCHECK(close(fd_) == 0); }
24
Austin Schuh6b6dfa52019-06-12 20:16:20 -070025void TimerFd::SetTime(monotonic_clock::time_point start,
26 monotonic_clock::duration interval) {
James Kuszmaul8866e642022-06-10 16:00:36 -070027 CHECK_GE(start, monotonic_clock::epoch());
Austin Schuh6b6dfa52019-06-12 20:16:20 -070028 struct itimerspec new_value;
29 new_value.it_interval = ::aos::time::to_timespec(interval);
30 new_value.it_value = ::aos::time::to_timespec(start);
31 PCHECK(timerfd_settime(fd_, TFD_TIMER_ABSTIME, &new_value, nullptr) == 0);
32}
33
Austin Schuhde8a8ff2019-11-30 15:25:36 -080034uint64_t TimerFd::Read() {
Austin Schuh6b6dfa52019-06-12 20:16:20 -070035 uint64_t buf;
36 ssize_t result = read(fd_, &buf, sizeof(buf));
Austin Schuhde8a8ff2019-11-30 15:25:36 -080037 if (result == -1) {
38 if (errno == EAGAIN) {
39 return 0;
40 }
41 }
Austin Schuh6b6dfa52019-06-12 20:16:20 -070042 PCHECK(result != -1);
43 CHECK_EQ(result, static_cast<int>(sizeof(buf)));
Austin Schuhde8a8ff2019-11-30 15:25:36 -080044
45 return buf;
Austin Schuh6b6dfa52019-06-12 20:16:20 -070046}
47
48EPoll::EPoll() : epoll_fd_(epoll_create1(EPOLL_CLOEXEC)) {
49 PCHECK(epoll_fd_ > 0);
50
51 // Create a pipe for the Quit function. We want to use a pipe to be async
52 // safe so this can be called from signal handlers.
53 int pipefd[2];
54 PCHECK(pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == 0);
55 quit_epoll_fd_ = pipefd[0];
56 quit_signal_fd_ = pipefd[1];
57 // Read the fd when data is sent and set run_ to false.
58 OnReadable(quit_epoll_fd_, [this]() {
59 run_ = false;
60 char buf[1];
61 PCHECK(read(quit_epoll_fd_, &buf[0], 1) == 1);
62 });
63}
64
65EPoll::~EPoll() {
66 // Clean up the quit pipe and epoll fd.
67 DeleteFd(quit_epoll_fd_);
68 close(quit_signal_fd_);
69 close(quit_epoll_fd_);
Austin Schuhf257f3c2019-10-27 21:00:43 -070070 CHECK_EQ(fns_.size(), 0u)
71 << ": Not all file descriptors were unregistered before shutting down.";
Austin Schuh6b6dfa52019-06-12 20:16:20 -070072 close(epoll_fd_);
73}
74
Austin Schuh219b7782020-12-21 12:01:40 -080075void EPoll::BeforeWait(std::function<void()> function) {
76 before_epoll_wait_functions_.emplace_back(std::move(function));
77}
78
Austin Schuh6b6dfa52019-06-12 20:16:20 -070079void EPoll::Run() {
Brian Silverman441591b2020-01-31 17:44:32 -080080 run_ = true;
James Kuszmaulc8e657e2020-12-14 23:49:45 -080081 // As long as run_ is true or we still have events to process, keep polling.
Austin Schuh6b6dfa52019-06-12 20:16:20 -070082 while (true) {
James Kuszmaulc8e657e2020-12-14 23:49:45 -080083 // If we ran out of events and Quit() was called, quit
84 if (!Poll(run_)) {
85 if (!run_) {
Austin Schuh6b6dfa52019-06-12 20:16:20 -070086 return;
87 }
88 }
Austin Schuh6b6dfa52019-06-12 20:16:20 -070089 }
90}
91
James Kuszmaulc8e657e2020-12-14 23:49:45 -080092bool EPoll::Poll(bool block) {
Austin Schuh219b7782020-12-21 12:01:40 -080093 for (const std::function<void()> &function : before_epoll_wait_functions_) {
94 function();
95 }
James Kuszmaulc8e657e2020-12-14 23:49:45 -080096 // Pull a single event out. Infinite timeout if we are supposed to be
97 // running, and 0 length timeout otherwise. This lets us flush the event
98 // queue before quitting.
99 struct epoll_event event;
100 int num_events = epoll_wait(epoll_fd_, &event, 1, block ? -1 : 0);
101 // Retry on EINTR and nothing else.
102 if (num_events == -1) {
103 if (errno == EINTR) {
104 return false;
105 }
106 PCHECK(num_events != -1);
107 }
108
109 if (num_events == 0) {
110 return false;
111 }
112
113 EventData *const event_data = static_cast<struct EventData *>(event.data.ptr);
Austin Schuh4c839682021-07-21 15:30:44 -0700114 event_data->DoCallbacks(event.events);
James Kuszmaulc8e657e2020-12-14 23:49:45 -0800115 return true;
116}
117
Austin Schuhf74daa62021-07-21 15:29:17 -0700118void EPoll::Quit() {
119 // Shortcut to break us out of infinite loops. We might write more than once
120 // to the pipe, but we'll stop once the first is read on the other end.
121 if (!run_) {
122 return;
123 }
124 PCHECK(write(quit_signal_fd_, "q", 1) == 1);
125}
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700126
127void EPoll::OnReadable(int fd, ::std::function<void()> function) {
Brian Silverman441591b2020-01-31 17:44:32 -0800128 EventData *event_data = GetEventData(fd);
129 if (event_data == nullptr) {
Austin Schuh4c839682021-07-21 15:30:44 -0700130 fns_.emplace_back(std::make_unique<InOutEventData>(fd));
Brian Silverman441591b2020-01-31 17:44:32 -0800131 event_data = fns_.back().get();
132 } else {
Austin Schuh4c839682021-07-21 15:30:44 -0700133 CHECK(!static_cast<InOutEventData *>(event_data)->in_fn)
134 << ": Duplicate in functions for " << fd;
Brian Silverman441591b2020-01-31 17:44:32 -0800135 }
Austin Schuh4c839682021-07-21 15:30:44 -0700136 static_cast<InOutEventData *>(event_data)->in_fn = ::std::move(function);
Brian Silverman441591b2020-01-31 17:44:32 -0800137 DoEpollCtl(event_data, event_data->events | kInEvents);
138}
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700139
Austin Schuh603419f2021-04-24 18:15:20 -0700140void EPoll::OnError(int fd, ::std::function<void()> function) {
141 EventData *event_data = GetEventData(fd);
142 if (event_data == nullptr) {
Austin Schuh4c839682021-07-21 15:30:44 -0700143 fns_.emplace_back(std::make_unique<InOutEventData>(fd));
Austin Schuh603419f2021-04-24 18:15:20 -0700144 event_data = fns_.back().get();
145 } else {
Austin Schuh4c839682021-07-21 15:30:44 -0700146 CHECK(!static_cast<InOutEventData *>(event_data)->err_fn)
147 << ": Duplicate error functions for " << fd;
Austin Schuh603419f2021-04-24 18:15:20 -0700148 }
Austin Schuh4c839682021-07-21 15:30:44 -0700149 static_cast<InOutEventData *>(event_data)->err_fn = ::std::move(function);
Austin Schuh603419f2021-04-24 18:15:20 -0700150 DoEpollCtl(event_data, event_data->events | kErrorEvents);
151}
152
Brian Silverman441591b2020-01-31 17:44:32 -0800153void EPoll::OnWriteable(int fd, ::std::function<void()> function) {
154 EventData *event_data = GetEventData(fd);
155 if (event_data == nullptr) {
Austin Schuh4c839682021-07-21 15:30:44 -0700156 fns_.emplace_back(std::make_unique<InOutEventData>(fd));
Brian Silverman441591b2020-01-31 17:44:32 -0800157 event_data = fns_.back().get();
158 } else {
Austin Schuh4c839682021-07-21 15:30:44 -0700159 CHECK(!static_cast<InOutEventData *>(event_data)->out_fn)
160 << ": Duplicate out functions for " << fd;
Brian Silverman441591b2020-01-31 17:44:32 -0800161 }
Austin Schuh4c839682021-07-21 15:30:44 -0700162 static_cast<InOutEventData *>(event_data)->out_fn = ::std::move(function);
Brian Silverman441591b2020-01-31 17:44:32 -0800163 DoEpollCtl(event_data, event_data->events | kOutEvents);
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700164}
165
Austin Schuh4c839682021-07-21 15:30:44 -0700166void EPoll::OnEvents(int fd, ::std::function<void(uint32_t)> function) {
167 if (GetEventData(fd) != nullptr) {
168 LOG(FATAL) << "May not replace OnEvents handlers";
169 }
170 fns_.emplace_back(std::make_unique<SingleEventData>(fd));
171 static_cast<SingleEventData *>(fns_.back().get())->fn = std::move(function);
172}
173
Austin Schuh219b7782020-12-21 12:01:40 -0800174void EPoll::ForgetClosedFd(int fd) {
175 auto element = fns_.begin();
176 while (fns_.end() != element) {
177 if (element->get()->fd == fd) {
178 fns_.erase(element);
179 return;
180 }
181 ++element;
182 }
183 LOG(FATAL) << "fd " << fd << " not found";
184}
185
Austin Schuh4c839682021-07-21 15:30:44 -0700186void EPoll::SetEvents(int fd, uint32_t events) {
187 DoEpollCtl(CHECK_NOTNULL(GetEventData(fd)), events);
188}
189
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700190// Removes fd from the event loop.
191void EPoll::DeleteFd(int fd) {
192 auto element = fns_.begin();
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700193 while (fns_.end() != element) {
194 if (element->get()->fd == fd) {
195 fns_.erase(element);
Austin Schuh219b7782020-12-21 12:01:40 -0800196 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == 0)
197 << "Failed to delete fd " << fd;
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700198 return;
199 }
200 ++element;
201 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700202 LOG(FATAL) << "fd " << fd << " not found";
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700203}
204
Austin Schuh4c839682021-07-21 15:30:44 -0700205void EPoll::InOutEventData::DoCallbacks(uint32_t events) {
206 if (events & kInEvents) {
207 CHECK(in_fn) << ": No handler registered for input events on " << fd;
208 in_fn();
209 }
210 if (events & kOutEvents) {
211 CHECK(out_fn) << ": No handler registered for output events on " << fd;
212 out_fn();
213 }
214 if (events & kErrorEvents) {
215 CHECK(err_fn) << ": No handler registered for error events on " << fd;
216 err_fn();
217 }
218}
219
Brian Silverman441591b2020-01-31 17:44:32 -0800220void EPoll::EnableEvents(int fd, uint32_t events) {
221 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
222 DoEpollCtl(event_data, event_data->events | events);
223}
224
225void EPoll::DisableEvents(int fd, uint32_t events) {
226 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
227 DoEpollCtl(event_data, event_data->events & ~events);
228}
229
230EPoll::EventData *EPoll::GetEventData(int fd) {
231 const auto iterator = std::find_if(
232 fns_.begin(), fns_.end(),
233 [fd](const std::unique_ptr<EventData> &data) { return data->fd == fd; });
234 if (iterator == fns_.end()) {
235 return nullptr;
236 }
237 return iterator->get();
238}
239
240void EPoll::DoEpollCtl(EventData *event_data, const uint32_t new_events) {
241 const uint32_t old_events = event_data->events;
Austin Schuh4c839682021-07-21 15:30:44 -0700242 if (old_events == new_events) {
243 // Shortcut without calling into the kernel. This happens often with
244 // external event loop integrations that are emulating poll, so make it
245 // fast.
246 return;
247 }
Brian Silverman441591b2020-01-31 17:44:32 -0800248 event_data->events = new_events;
249 if (new_events == 0) {
Brian Silverman441591b2020-01-31 17:44:32 -0800250 // It was added, but should now be removed.
251 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, event_data->fd, nullptr) == 0);
252 return;
253 }
254
255 int operation = EPOLL_CTL_MOD;
256 if (old_events == 0) {
257 // If it wasn't added before, then this is the first time it's being added.
258 operation = EPOLL_CTL_ADD;
259 }
260 struct epoll_event event;
261 event.events = event_data->events;
262 event.data.ptr = event_data;
263 PCHECK(epoll_ctl(epoll_fd_, operation, event_data->fd, &event) == 0)
264 << ": Failed to " << operation << " epoll fd: " << event_data->fd;
265}
266
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700267} // namespace internal
268} // namespace aos