blob: 8cb553b392ca74eb4d37f465546a968a43404b3f [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) {
27 struct itimerspec new_value;
28 new_value.it_interval = ::aos::time::to_timespec(interval);
29 new_value.it_value = ::aos::time::to_timespec(start);
30 PCHECK(timerfd_settime(fd_, TFD_TIMER_ABSTIME, &new_value, nullptr) == 0);
31}
32
Austin Schuhde8a8ff2019-11-30 15:25:36 -080033uint64_t TimerFd::Read() {
Austin Schuh6b6dfa52019-06-12 20:16:20 -070034 uint64_t buf;
35 ssize_t result = read(fd_, &buf, sizeof(buf));
Austin Schuhde8a8ff2019-11-30 15:25:36 -080036 if (result == -1) {
37 if (errno == EAGAIN) {
38 return 0;
39 }
40 }
Austin Schuh6b6dfa52019-06-12 20:16:20 -070041 PCHECK(result != -1);
42 CHECK_EQ(result, static_cast<int>(sizeof(buf)));
Austin Schuhde8a8ff2019-11-30 15:25:36 -080043
44 return buf;
Austin Schuh6b6dfa52019-06-12 20:16:20 -070045}
46
47EPoll::EPoll() : epoll_fd_(epoll_create1(EPOLL_CLOEXEC)) {
48 PCHECK(epoll_fd_ > 0);
49
50 // Create a pipe for the Quit function. We want to use a pipe to be async
51 // safe so this can be called from signal handlers.
52 int pipefd[2];
53 PCHECK(pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == 0);
54 quit_epoll_fd_ = pipefd[0];
55 quit_signal_fd_ = pipefd[1];
56 // Read the fd when data is sent and set run_ to false.
57 OnReadable(quit_epoll_fd_, [this]() {
58 run_ = false;
59 char buf[1];
60 PCHECK(read(quit_epoll_fd_, &buf[0], 1) == 1);
61 });
62}
63
64EPoll::~EPoll() {
65 // Clean up the quit pipe and epoll fd.
66 DeleteFd(quit_epoll_fd_);
67 close(quit_signal_fd_);
68 close(quit_epoll_fd_);
Austin Schuhf257f3c2019-10-27 21:00:43 -070069 CHECK_EQ(fns_.size(), 0u)
70 << ": Not all file descriptors were unregistered before shutting down.";
Austin Schuh6b6dfa52019-06-12 20:16:20 -070071 close(epoll_fd_);
72}
73
Austin Schuh219b7782020-12-21 12:01:40 -080074void EPoll::BeforeWait(std::function<void()> function) {
75 before_epoll_wait_functions_.emplace_back(std::move(function));
76}
77
Austin Schuh6b6dfa52019-06-12 20:16:20 -070078void EPoll::Run() {
Brian Silverman441591b2020-01-31 17:44:32 -080079 run_ = true;
James Kuszmaulc8e657e2020-12-14 23:49:45 -080080 // As long as run_ is true or we still have events to process, keep polling.
Austin Schuh6b6dfa52019-06-12 20:16:20 -070081 while (true) {
James Kuszmaulc8e657e2020-12-14 23:49:45 -080082 // If we ran out of events and Quit() was called, quit
83 if (!Poll(run_)) {
84 if (!run_) {
Austin Schuh6b6dfa52019-06-12 20:16:20 -070085 return;
86 }
87 }
Austin Schuh6b6dfa52019-06-12 20:16:20 -070088 }
89}
90
James Kuszmaulc8e657e2020-12-14 23:49:45 -080091bool EPoll::Poll(bool block) {
Austin Schuh219b7782020-12-21 12:01:40 -080092 for (const std::function<void()> &function : before_epoll_wait_functions_) {
93 function();
94 }
James Kuszmaulc8e657e2020-12-14 23:49:45 -080095 // Pull a single event out. Infinite timeout if we are supposed to be
96 // running, and 0 length timeout otherwise. This lets us flush the event
97 // queue before quitting.
98 struct epoll_event event;
99 int num_events = epoll_wait(epoll_fd_, &event, 1, block ? -1 : 0);
100 // Retry on EINTR and nothing else.
101 if (num_events == -1) {
102 if (errno == EINTR) {
103 return false;
104 }
105 PCHECK(num_events != -1);
106 }
107
108 if (num_events == 0) {
109 return false;
110 }
111
112 EventData *const event_data = static_cast<struct EventData *>(event.data.ptr);
Austin Schuh4c839682021-07-21 15:30:44 -0700113 event_data->DoCallbacks(event.events);
James Kuszmaulc8e657e2020-12-14 23:49:45 -0800114 return true;
115}
116
Austin Schuhf74daa62021-07-21 15:29:17 -0700117void EPoll::Quit() {
118 // Shortcut to break us out of infinite loops. We might write more than once
119 // to the pipe, but we'll stop once the first is read on the other end.
120 if (!run_) {
121 return;
122 }
123 PCHECK(write(quit_signal_fd_, "q", 1) == 1);
124}
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700125
126void EPoll::OnReadable(int fd, ::std::function<void()> function) {
Brian Silverman441591b2020-01-31 17:44:32 -0800127 EventData *event_data = GetEventData(fd);
128 if (event_data == nullptr) {
Austin Schuh4c839682021-07-21 15:30:44 -0700129 fns_.emplace_back(std::make_unique<InOutEventData>(fd));
Brian Silverman441591b2020-01-31 17:44:32 -0800130 event_data = fns_.back().get();
131 } else {
Austin Schuh4c839682021-07-21 15:30:44 -0700132 CHECK(!static_cast<InOutEventData *>(event_data)->in_fn)
133 << ": Duplicate in functions for " << fd;
Brian Silverman441591b2020-01-31 17:44:32 -0800134 }
Austin Schuh4c839682021-07-21 15:30:44 -0700135 static_cast<InOutEventData *>(event_data)->in_fn = ::std::move(function);
Brian Silverman441591b2020-01-31 17:44:32 -0800136 DoEpollCtl(event_data, event_data->events | kInEvents);
137}
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700138
Austin Schuh603419f2021-04-24 18:15:20 -0700139void EPoll::OnError(int fd, ::std::function<void()> function) {
140 EventData *event_data = GetEventData(fd);
141 if (event_data == nullptr) {
Austin Schuh4c839682021-07-21 15:30:44 -0700142 fns_.emplace_back(std::make_unique<InOutEventData>(fd));
Austin Schuh603419f2021-04-24 18:15:20 -0700143 event_data = fns_.back().get();
144 } else {
Austin Schuh4c839682021-07-21 15:30:44 -0700145 CHECK(!static_cast<InOutEventData *>(event_data)->err_fn)
146 << ": Duplicate error functions for " << fd;
Austin Schuh603419f2021-04-24 18:15:20 -0700147 }
Austin Schuh4c839682021-07-21 15:30:44 -0700148 static_cast<InOutEventData *>(event_data)->err_fn = ::std::move(function);
Austin Schuh603419f2021-04-24 18:15:20 -0700149 DoEpollCtl(event_data, event_data->events | kErrorEvents);
150}
151
Brian Silverman441591b2020-01-31 17:44:32 -0800152void EPoll::OnWriteable(int fd, ::std::function<void()> function) {
153 EventData *event_data = GetEventData(fd);
154 if (event_data == nullptr) {
Austin Schuh4c839682021-07-21 15:30:44 -0700155 fns_.emplace_back(std::make_unique<InOutEventData>(fd));
Brian Silverman441591b2020-01-31 17:44:32 -0800156 event_data = fns_.back().get();
157 } else {
Austin Schuh4c839682021-07-21 15:30:44 -0700158 CHECK(!static_cast<InOutEventData *>(event_data)->out_fn)
159 << ": Duplicate out functions for " << fd;
Brian Silverman441591b2020-01-31 17:44:32 -0800160 }
Austin Schuh4c839682021-07-21 15:30:44 -0700161 static_cast<InOutEventData *>(event_data)->out_fn = ::std::move(function);
Brian Silverman441591b2020-01-31 17:44:32 -0800162 DoEpollCtl(event_data, event_data->events | kOutEvents);
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700163}
164
Austin Schuh4c839682021-07-21 15:30:44 -0700165void EPoll::OnEvents(int fd, ::std::function<void(uint32_t)> function) {
166 if (GetEventData(fd) != nullptr) {
167 LOG(FATAL) << "May not replace OnEvents handlers";
168 }
169 fns_.emplace_back(std::make_unique<SingleEventData>(fd));
170 static_cast<SingleEventData *>(fns_.back().get())->fn = std::move(function);
171}
172
Austin Schuh219b7782020-12-21 12:01:40 -0800173void EPoll::ForgetClosedFd(int fd) {
174 auto element = fns_.begin();
175 while (fns_.end() != element) {
176 if (element->get()->fd == fd) {
177 fns_.erase(element);
178 return;
179 }
180 ++element;
181 }
182 LOG(FATAL) << "fd " << fd << " not found";
183}
184
Austin Schuh4c839682021-07-21 15:30:44 -0700185void EPoll::SetEvents(int fd, uint32_t events) {
186 DoEpollCtl(CHECK_NOTNULL(GetEventData(fd)), events);
187}
188
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700189// Removes fd from the event loop.
190void EPoll::DeleteFd(int fd) {
191 auto element = fns_.begin();
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700192 while (fns_.end() != element) {
193 if (element->get()->fd == fd) {
194 fns_.erase(element);
Austin Schuh219b7782020-12-21 12:01:40 -0800195 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == 0)
196 << "Failed to delete fd " << fd;
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700197 return;
198 }
199 ++element;
200 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700201 LOG(FATAL) << "fd " << fd << " not found";
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700202}
203
Austin Schuh4c839682021-07-21 15:30:44 -0700204void EPoll::InOutEventData::DoCallbacks(uint32_t events) {
205 if (events & kInEvents) {
206 CHECK(in_fn) << ": No handler registered for input events on " << fd;
207 in_fn();
208 }
209 if (events & kOutEvents) {
210 CHECK(out_fn) << ": No handler registered for output events on " << fd;
211 out_fn();
212 }
213 if (events & kErrorEvents) {
214 CHECK(err_fn) << ": No handler registered for error events on " << fd;
215 err_fn();
216 }
217}
218
Brian Silverman441591b2020-01-31 17:44:32 -0800219void EPoll::EnableEvents(int fd, uint32_t events) {
220 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
221 DoEpollCtl(event_data, event_data->events | events);
222}
223
224void EPoll::DisableEvents(int fd, uint32_t events) {
225 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
226 DoEpollCtl(event_data, event_data->events & ~events);
227}
228
229EPoll::EventData *EPoll::GetEventData(int fd) {
230 const auto iterator = std::find_if(
231 fns_.begin(), fns_.end(),
232 [fd](const std::unique_ptr<EventData> &data) { return data->fd == fd; });
233 if (iterator == fns_.end()) {
234 return nullptr;
235 }
236 return iterator->get();
237}
238
239void EPoll::DoEpollCtl(EventData *event_data, const uint32_t new_events) {
240 const uint32_t old_events = event_data->events;
Austin Schuh4c839682021-07-21 15:30:44 -0700241 if (old_events == new_events) {
242 // Shortcut without calling into the kernel. This happens often with
243 // external event loop integrations that are emulating poll, so make it
244 // fast.
245 return;
246 }
Brian Silverman441591b2020-01-31 17:44:32 -0800247 event_data->events = new_events;
248 if (new_events == 0) {
Brian Silverman441591b2020-01-31 17:44:32 -0800249 // It was added, but should now be removed.
250 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, event_data->fd, nullptr) == 0);
251 return;
252 }
253
254 int operation = EPOLL_CTL_MOD;
255 if (old_events == 0) {
256 // If it wasn't added before, then this is the first time it's being added.
257 operation = EPOLL_CTL_ADD;
258 }
259 struct epoll_event event;
260 event.events = event_data->events;
261 event.data.ptr = event_data;
262 PCHECK(epoll_ctl(epoll_fd_, operation, event_data->fd, &event) == 0)
263 << ": Failed to " << operation << " epoll fd: " << event_data->fd;
264}
265
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700266} // namespace internal
267} // namespace aos