blob: 978909eea790ac76427d18117ad73cc9cebaa20f [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 }
122 return true;
123}
124
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700125void EPoll::Quit() { PCHECK(write(quit_signal_fd_, "q", 1) == 1); }
126
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) {
130 fns_.emplace_back(std::make_unique<EventData>(fd));
131 event_data = fns_.back().get();
132 } else {
133 CHECK(!event_data->in_fn) << ": Duplicate in functions for " << fd;
134 }
135 event_data->in_fn = ::std::move(function);
136 DoEpollCtl(event_data, event_data->events | kInEvents);
137}
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700138
Brian Silverman441591b2020-01-31 17:44:32 -0800139void EPoll::OnWriteable(int fd, ::std::function<void()> function) {
140 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->out_fn) << ": Duplicate out functions for " << fd;
146 }
147 event_data->out_fn = ::std::move(function);
148 DoEpollCtl(event_data, event_data->events | kOutEvents);
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700149}
150
Austin Schuh219b7782020-12-21 12:01:40 -0800151void EPoll::ForgetClosedFd(int fd) {
152 auto element = fns_.begin();
153 while (fns_.end() != element) {
154 if (element->get()->fd == fd) {
155 fns_.erase(element);
156 return;
157 }
158 ++element;
159 }
160 LOG(FATAL) << "fd " << fd << " not found";
161}
162
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700163// Removes fd from the event loop.
164void EPoll::DeleteFd(int fd) {
165 auto element = fns_.begin();
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700166 while (fns_.end() != element) {
167 if (element->get()->fd == fd) {
168 fns_.erase(element);
Austin Schuh219b7782020-12-21 12:01:40 -0800169 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == 0)
170 << "Failed to delete fd " << fd;
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700171 return;
172 }
173 ++element;
174 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700175 LOG(FATAL) << "fd " << fd << " not found";
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700176}
177
Brian Silverman441591b2020-01-31 17:44:32 -0800178void EPoll::EnableEvents(int fd, uint32_t events) {
179 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
180 DoEpollCtl(event_data, event_data->events | events);
181}
182
183void EPoll::DisableEvents(int fd, uint32_t events) {
184 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
185 DoEpollCtl(event_data, event_data->events & ~events);
186}
187
188EPoll::EventData *EPoll::GetEventData(int fd) {
189 const auto iterator = std::find_if(
190 fns_.begin(), fns_.end(),
191 [fd](const std::unique_ptr<EventData> &data) { return data->fd == fd; });
192 if (iterator == fns_.end()) {
193 return nullptr;
194 }
195 return iterator->get();
196}
197
198void EPoll::DoEpollCtl(EventData *event_data, const uint32_t new_events) {
199 const uint32_t old_events = event_data->events;
200 event_data->events = new_events;
201 if (new_events == 0) {
202 if (old_events == 0) {
203 // Not added, and doesn't need to be. Nothing to do here.
204 return;
205 }
206 // It was added, but should now be removed.
207 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, event_data->fd, nullptr) == 0);
208 return;
209 }
210
211 int operation = EPOLL_CTL_MOD;
212 if (old_events == 0) {
213 // If it wasn't added before, then this is the first time it's being added.
214 operation = EPOLL_CTL_ADD;
215 }
216 struct epoll_event event;
217 event.events = event_data->events;
218 event.data.ptr = event_data;
219 PCHECK(epoll_ctl(epoll_fd_, operation, event_data->fd, &event) == 0)
220 << ": Failed to " << operation << " epoll fd: " << event_data->fd;
221}
222
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700223} // namespace internal
224} // namespace aos