blob: 7fff6efc27b0a19a8426bd120fae3d180cda98cf [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>
7#include <atomic>
8#include <vector>
9
Austin Schuh6b6dfa52019-06-12 20:16:20 -070010#include "aos/time/time.h"
Austin Schuhf257f3c2019-10-27 21:00:43 -070011#include "glog/logging.h"
Austin Schuh6b6dfa52019-06-12 20:16:20 -070012
13namespace aos {
14namespace internal {
15
16TimerFd::TimerFd()
17 : fd_(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)) {
18 PCHECK(fd_ != -1);
19 Disable();
20}
21
Austin Schuhf257f3c2019-10-27 21:00:43 -070022TimerFd::~TimerFd() { PCHECK(close(fd_) == 0); }
23
Austin Schuh6b6dfa52019-06-12 20:16:20 -070024void TimerFd::SetTime(monotonic_clock::time_point start,
25 monotonic_clock::duration interval) {
26 struct itimerspec new_value;
27 new_value.it_interval = ::aos::time::to_timespec(interval);
28 new_value.it_value = ::aos::time::to_timespec(start);
29 PCHECK(timerfd_settime(fd_, TFD_TIMER_ABSTIME, &new_value, nullptr) == 0);
30}
31
Austin Schuhde8a8ff2019-11-30 15:25:36 -080032uint64_t TimerFd::Read() {
Austin Schuh6b6dfa52019-06-12 20:16:20 -070033 uint64_t buf;
34 ssize_t result = read(fd_, &buf, sizeof(buf));
Austin Schuhde8a8ff2019-11-30 15:25:36 -080035 if (result == -1) {
36 if (errno == EAGAIN) {
37 return 0;
38 }
39 }
Austin Schuh6b6dfa52019-06-12 20:16:20 -070040 PCHECK(result != -1);
41 CHECK_EQ(result, static_cast<int>(sizeof(buf)));
Austin Schuhde8a8ff2019-11-30 15:25:36 -080042
43 return buf;
Austin Schuh6b6dfa52019-06-12 20:16:20 -070044}
45
46EPoll::EPoll() : epoll_fd_(epoll_create1(EPOLL_CLOEXEC)) {
47 PCHECK(epoll_fd_ > 0);
48
49 // Create a pipe for the Quit function. We want to use a pipe to be async
50 // safe so this can be called from signal handlers.
51 int pipefd[2];
52 PCHECK(pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == 0);
53 quit_epoll_fd_ = pipefd[0];
54 quit_signal_fd_ = pipefd[1];
55 // Read the fd when data is sent and set run_ to false.
56 OnReadable(quit_epoll_fd_, [this]() {
57 run_ = false;
58 char buf[1];
59 PCHECK(read(quit_epoll_fd_, &buf[0], 1) == 1);
60 });
61}
62
63EPoll::~EPoll() {
64 // Clean up the quit pipe and epoll fd.
65 DeleteFd(quit_epoll_fd_);
66 close(quit_signal_fd_);
67 close(quit_epoll_fd_);
Austin Schuhf257f3c2019-10-27 21:00:43 -070068 CHECK_EQ(fns_.size(), 0u)
69 << ": Not all file descriptors were unregistered before shutting down.";
Austin Schuh6b6dfa52019-06-12 20:16:20 -070070 close(epoll_fd_);
71}
72
Austin Schuh219b7782020-12-21 12:01:40 -080073void EPoll::BeforeWait(std::function<void()> function) {
74 before_epoll_wait_functions_.emplace_back(std::move(function));
75}
76
Austin Schuh6b6dfa52019-06-12 20:16:20 -070077void EPoll::Run() {
Brian Silverman441591b2020-01-31 17:44:32 -080078 run_ = true;
James Kuszmaulc8e657e2020-12-14 23:49:45 -080079 // As long as run_ is true or we still have events to process, keep polling.
Austin Schuh6b6dfa52019-06-12 20:16:20 -070080 while (true) {
James Kuszmaulc8e657e2020-12-14 23:49:45 -080081 // If we ran out of events and Quit() was called, quit
82 if (!Poll(run_)) {
83 if (!run_) {
Austin Schuh6b6dfa52019-06-12 20:16:20 -070084 return;
85 }
86 }
Austin Schuh6b6dfa52019-06-12 20:16:20 -070087 }
88}
89
James Kuszmaulc8e657e2020-12-14 23:49:45 -080090bool EPoll::Poll(bool block) {
Austin Schuh219b7782020-12-21 12:01:40 -080091 for (const std::function<void()> &function : before_epoll_wait_functions_) {
92 function();
93 }
James Kuszmaulc8e657e2020-12-14 23:49:45 -080094 // Pull a single event out. Infinite timeout if we are supposed to be
95 // running, and 0 length timeout otherwise. This lets us flush the event
96 // queue before quitting.
97 struct epoll_event event;
98 int num_events = epoll_wait(epoll_fd_, &event, 1, block ? -1 : 0);
99 // Retry on EINTR and nothing else.
100 if (num_events == -1) {
101 if (errno == EINTR) {
102 return false;
103 }
104 PCHECK(num_events != -1);
105 }
106
107 if (num_events == 0) {
108 return false;
109 }
110
111 EventData *const event_data = static_cast<struct EventData *>(event.data.ptr);
112 if (event.events & kInEvents) {
113 CHECK(event_data->in_fn)
114 << ": No handler registered for input events on " << event_data->fd;
115 event_data->in_fn();
116 }
117 if (event.events & kOutEvents) {
118 CHECK(event_data->out_fn)
119 << ": No handler registered for output events on " << event_data->fd;
120 event_data->out_fn();
121 }
Austin Schuh603419f2021-04-24 18:15:20 -0700122 if (event.events & kErrorEvents) {
123 CHECK(event_data->err_fn)
124 << ": No handler registered for error events on " << event_data->fd;
125 event_data->err_fn();
126 }
James Kuszmaulc8e657e2020-12-14 23:49:45 -0800127 return true;
128}
129
Austin Schuhf74daa62021-07-21 15:29:17 -0700130void EPoll::Quit() {
131 // Shortcut to break us out of infinite loops. We might write more than once
132 // to the pipe, but we'll stop once the first is read on the other end.
133 if (!run_) {
134 return;
135 }
136 PCHECK(write(quit_signal_fd_, "q", 1) == 1);
137}
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700138
139void EPoll::OnReadable(int fd, ::std::function<void()> function) {
Brian Silverman441591b2020-01-31 17:44:32 -0800140 EventData *event_data = GetEventData(fd);
141 if (event_data == nullptr) {
142 fns_.emplace_back(std::make_unique<EventData>(fd));
143 event_data = fns_.back().get();
144 } else {
145 CHECK(!event_data->in_fn) << ": Duplicate in functions for " << fd;
146 }
147 event_data->in_fn = ::std::move(function);
148 DoEpollCtl(event_data, event_data->events | kInEvents);
149}
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700150
Austin Schuh603419f2021-04-24 18:15:20 -0700151void EPoll::OnError(int fd, ::std::function<void()> function) {
152 EventData *event_data = GetEventData(fd);
153 if (event_data == nullptr) {
154 fns_.emplace_back(std::make_unique<EventData>(fd));
155 event_data = fns_.back().get();
156 } else {
157 CHECK(!event_data->err_fn) << ": Duplicate in functions for " << fd;
158 }
159 event_data->err_fn = ::std::move(function);
160 DoEpollCtl(event_data, event_data->events | kErrorEvents);
161}
162
Brian Silverman441591b2020-01-31 17:44:32 -0800163void EPoll::OnWriteable(int fd, ::std::function<void()> function) {
164 EventData *event_data = GetEventData(fd);
165 if (event_data == nullptr) {
166 fns_.emplace_back(std::make_unique<EventData>(fd));
167 event_data = fns_.back().get();
168 } else {
169 CHECK(!event_data->out_fn) << ": Duplicate out functions for " << fd;
170 }
171 event_data->out_fn = ::std::move(function);
172 DoEpollCtl(event_data, event_data->events | kOutEvents);
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700173}
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 Schuh6b6dfa52019-06-12 20:16:20 -0700187// Removes fd from the event loop.
188void EPoll::DeleteFd(int fd) {
189 auto element = fns_.begin();
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700190 while (fns_.end() != element) {
191 if (element->get()->fd == fd) {
192 fns_.erase(element);
Austin Schuh219b7782020-12-21 12:01:40 -0800193 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == 0)
194 << "Failed to delete fd " << fd;
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700195 return;
196 }
197 ++element;
198 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700199 LOG(FATAL) << "fd " << fd << " not found";
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700200}
201
Brian Silverman441591b2020-01-31 17:44:32 -0800202void EPoll::EnableEvents(int fd, uint32_t events) {
203 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
204 DoEpollCtl(event_data, event_data->events | events);
205}
206
207void EPoll::DisableEvents(int fd, uint32_t events) {
208 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
209 DoEpollCtl(event_data, event_data->events & ~events);
210}
211
212EPoll::EventData *EPoll::GetEventData(int fd) {
213 const auto iterator = std::find_if(
214 fns_.begin(), fns_.end(),
215 [fd](const std::unique_ptr<EventData> &data) { return data->fd == fd; });
216 if (iterator == fns_.end()) {
217 return nullptr;
218 }
219 return iterator->get();
220}
221
222void EPoll::DoEpollCtl(EventData *event_data, const uint32_t new_events) {
223 const uint32_t old_events = event_data->events;
224 event_data->events = new_events;
225 if (new_events == 0) {
226 if (old_events == 0) {
227 // Not added, and doesn't need to be. Nothing to do here.
228 return;
229 }
230 // It was added, but should now be removed.
231 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, event_data->fd, nullptr) == 0);
232 return;
233 }
234
235 int operation = EPOLL_CTL_MOD;
236 if (old_events == 0) {
237 // If it wasn't added before, then this is the first time it's being added.
238 operation = EPOLL_CTL_ADD;
239 }
240 struct epoll_event event;
241 event.events = event_data->events;
242 event.data.ptr = event_data;
243 PCHECK(epoll_ctl(epoll_fd_, operation, event_data->fd, &event) == 0)
244 << ": Failed to " << operation << " epoll fd: " << event_data->fd;
245}
246
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700247} // namespace internal
248} // namespace aos