blob: 3e2148dd0d5e5c53027630ce0cc953ed09babb60 [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
73void EPoll::Run() {
Brian Silverman441591b2020-01-31 17:44:32 -080074 run_ = true;
James Kuszmaulc8e657e2020-12-14 23:49:45 -080075 // As long as run_ is true or we still have events to process, keep polling.
Austin Schuh6b6dfa52019-06-12 20:16:20 -070076 while (true) {
James Kuszmaulc8e657e2020-12-14 23:49:45 -080077 // If we ran out of events and Quit() was called, quit
78 if (!Poll(run_)) {
79 if (!run_) {
Austin Schuh6b6dfa52019-06-12 20:16:20 -070080 return;
81 }
82 }
Austin Schuh6b6dfa52019-06-12 20:16:20 -070083 }
84}
85
James Kuszmaulc8e657e2020-12-14 23:49:45 -080086bool EPoll::Poll(bool block) {
87 // Pull a single event out. Infinite timeout if we are supposed to be
88 // running, and 0 length timeout otherwise. This lets us flush the event
89 // queue before quitting.
90 struct epoll_event event;
91 int num_events = epoll_wait(epoll_fd_, &event, 1, block ? -1 : 0);
92 // Retry on EINTR and nothing else.
93 if (num_events == -1) {
94 if (errno == EINTR) {
95 return false;
96 }
97 PCHECK(num_events != -1);
98 }
99
100 if (num_events == 0) {
101 return false;
102 }
103
104 EventData *const event_data = static_cast<struct EventData *>(event.data.ptr);
105 if (event.events & kInEvents) {
106 CHECK(event_data->in_fn)
107 << ": No handler registered for input events on " << event_data->fd;
108 event_data->in_fn();
109 }
110 if (event.events & kOutEvents) {
111 CHECK(event_data->out_fn)
112 << ": No handler registered for output events on " << event_data->fd;
113 event_data->out_fn();
114 }
115 return true;
116}
117
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700118void EPoll::Quit() { PCHECK(write(quit_signal_fd_, "q", 1) == 1); }
119
120void EPoll::OnReadable(int fd, ::std::function<void()> function) {
Brian Silverman441591b2020-01-31 17:44:32 -0800121 EventData *event_data = GetEventData(fd);
122 if (event_data == nullptr) {
123 fns_.emplace_back(std::make_unique<EventData>(fd));
124 event_data = fns_.back().get();
125 } else {
126 CHECK(!event_data->in_fn) << ": Duplicate in functions for " << fd;
127 }
128 event_data->in_fn = ::std::move(function);
129 DoEpollCtl(event_data, event_data->events | kInEvents);
130}
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700131
Brian Silverman441591b2020-01-31 17:44:32 -0800132void EPoll::OnWriteable(int fd, ::std::function<void()> function) {
133 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->out_fn) << ": Duplicate out functions for " << fd;
139 }
140 event_data->out_fn = ::std::move(function);
141 DoEpollCtl(event_data, event_data->events | kOutEvents);
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700142}
143
144// Removes fd from the event loop.
145void EPoll::DeleteFd(int fd) {
146 auto element = fns_.begin();
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700147 while (fns_.end() != element) {
148 if (element->get()->fd == fd) {
149 fns_.erase(element);
Brian Silverman441591b2020-01-31 17:44:32 -0800150 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == 0);
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700151 return;
152 }
153 ++element;
154 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700155 LOG(FATAL) << "fd " << fd << " not found";
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700156}
157
Brian Silverman441591b2020-01-31 17:44:32 -0800158void EPoll::EnableEvents(int fd, uint32_t events) {
159 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
160 DoEpollCtl(event_data, event_data->events | events);
161}
162
163void EPoll::DisableEvents(int fd, uint32_t events) {
164 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
165 DoEpollCtl(event_data, event_data->events & ~events);
166}
167
168EPoll::EventData *EPoll::GetEventData(int fd) {
169 const auto iterator = std::find_if(
170 fns_.begin(), fns_.end(),
171 [fd](const std::unique_ptr<EventData> &data) { return data->fd == fd; });
172 if (iterator == fns_.end()) {
173 return nullptr;
174 }
175 return iterator->get();
176}
177
178void EPoll::DoEpollCtl(EventData *event_data, const uint32_t new_events) {
179 const uint32_t old_events = event_data->events;
180 event_data->events = new_events;
181 if (new_events == 0) {
182 if (old_events == 0) {
183 // Not added, and doesn't need to be. Nothing to do here.
184 return;
185 }
186 // It was added, but should now be removed.
187 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, event_data->fd, nullptr) == 0);
188 return;
189 }
190
191 int operation = EPOLL_CTL_MOD;
192 if (old_events == 0) {
193 // If it wasn't added before, then this is the first time it's being added.
194 operation = EPOLL_CTL_ADD;
195 }
196 struct epoll_event event;
197 event.events = event_data->events;
198 event.data.ptr = event_data;
199 PCHECK(epoll_ctl(epoll_fd_, operation, event_data->fd, &event) == 0)
200 << ": Failed to " << operation << " epoll fd: " << event_data->fd;
201}
202
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700203} // namespace internal
204} // namespace aos