blob: 1c4427b6396d7e2af556ea8067ec6a494e8605bb [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 Schuh6b6dfa52019-06-12 20:16:20 -0700130void EPoll::Quit() { PCHECK(write(quit_signal_fd_, "q", 1) == 1); }
131
132void EPoll::OnReadable(int fd, ::std::function<void()> function) {
Brian Silverman441591b2020-01-31 17:44:32 -0800133 EventData *event_data = GetEventData(fd);
134 if (event_data == nullptr) {
135 fns_.emplace_back(std::make_unique<EventData>(fd));
136 event_data = fns_.back().get();
137 } else {
138 CHECK(!event_data->in_fn) << ": Duplicate in functions for " << fd;
139 }
140 event_data->in_fn = ::std::move(function);
141 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) {
147 fns_.emplace_back(std::make_unique<EventData>(fd));
148 event_data = fns_.back().get();
149 } else {
150 CHECK(!event_data->err_fn) << ": Duplicate in functions for " << fd;
151 }
152 event_data->err_fn = ::std::move(function);
153 DoEpollCtl(event_data, event_data->events | kErrorEvents);
154}
155
Brian Silverman441591b2020-01-31 17:44:32 -0800156void EPoll::OnWriteable(int fd, ::std::function<void()> function) {
157 EventData *event_data = GetEventData(fd);
158 if (event_data == nullptr) {
159 fns_.emplace_back(std::make_unique<EventData>(fd));
160 event_data = fns_.back().get();
161 } else {
162 CHECK(!event_data->out_fn) << ": Duplicate out functions for " << fd;
163 }
164 event_data->out_fn = ::std::move(function);
165 DoEpollCtl(event_data, event_data->events | kOutEvents);
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700166}
167
Austin Schuh219b7782020-12-21 12:01:40 -0800168void EPoll::ForgetClosedFd(int fd) {
169 auto element = fns_.begin();
170 while (fns_.end() != element) {
171 if (element->get()->fd == fd) {
172 fns_.erase(element);
173 return;
174 }
175 ++element;
176 }
177 LOG(FATAL) << "fd " << fd << " not found";
178}
179
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700180// Removes fd from the event loop.
181void EPoll::DeleteFd(int fd) {
182 auto element = fns_.begin();
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700183 while (fns_.end() != element) {
184 if (element->get()->fd == fd) {
185 fns_.erase(element);
Austin Schuh219b7782020-12-21 12:01:40 -0800186 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == 0)
187 << "Failed to delete fd " << fd;
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700188 return;
189 }
190 ++element;
191 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700192 LOG(FATAL) << "fd " << fd << " not found";
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700193}
194
Brian Silverman441591b2020-01-31 17:44:32 -0800195void EPoll::EnableEvents(int fd, uint32_t events) {
196 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
197 DoEpollCtl(event_data, event_data->events | events);
198}
199
200void EPoll::DisableEvents(int fd, uint32_t events) {
201 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
202 DoEpollCtl(event_data, event_data->events & ~events);
203}
204
205EPoll::EventData *EPoll::GetEventData(int fd) {
206 const auto iterator = std::find_if(
207 fns_.begin(), fns_.end(),
208 [fd](const std::unique_ptr<EventData> &data) { return data->fd == fd; });
209 if (iterator == fns_.end()) {
210 return nullptr;
211 }
212 return iterator->get();
213}
214
215void EPoll::DoEpollCtl(EventData *event_data, const uint32_t new_events) {
216 const uint32_t old_events = event_data->events;
217 event_data->events = new_events;
218 if (new_events == 0) {
219 if (old_events == 0) {
220 // Not added, and doesn't need to be. Nothing to do here.
221 return;
222 }
223 // It was added, but should now be removed.
224 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, event_data->fd, nullptr) == 0);
225 return;
226 }
227
228 int operation = EPOLL_CTL_MOD;
229 if (old_events == 0) {
230 // If it wasn't added before, then this is the first time it's being added.
231 operation = EPOLL_CTL_ADD;
232 }
233 struct epoll_event event;
234 event.events = event_data->events;
235 event.data.ptr = event_data;
236 PCHECK(epoll_ctl(epoll_fd_, operation, event_data->fd, &event) == 0)
237 << ": Failed to " << operation << " epoll fd: " << event_data->fd;
238}
239
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700240} // namespace internal
241} // namespace aos