blob: b9e7edbb5e2f0fc0b1dcd44e0a5a52465129cc77 [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;
Austin Schuh6b6dfa52019-06-12 20:16:20 -070075 while (true) {
76 // Pull a single event out. Infinite timeout if we are supposed to be
77 // running, and 0 length timeout otherwise. This lets us flush the event
78 // queue before quitting.
79 struct epoll_event event;
80 int num_events = epoll_wait(epoll_fd_, &event, 1, run_ ? -1 : 0);
81 // Retry on EINTR and nothing else.
82 if (num_events == -1) {
83 if (errno == EINTR) {
84 continue;
85 }
86 PCHECK(num_events != -1);
87 }
88 if (!run_) {
89 // If we ran out of events, quit.
90 if (num_events == 0) {
91 return;
92 }
93 }
Brian Silverman441591b2020-01-31 17:44:32 -080094 EventData *const event_data = static_cast<struct EventData *>(event.data.ptr);
95 if (event.events & kInEvents) {
96 CHECK(event_data->in_fn)
97 << ": No handler registered for input events on " << event_data->fd;
Austin Schuh6b6dfa52019-06-12 20:16:20 -070098 event_data->in_fn();
99 }
Brian Silverman441591b2020-01-31 17:44:32 -0800100 if (event.events & kOutEvents) {
101 CHECK(event_data->out_fn)
102 << ": No handler registered for output events on " << event_data->fd;
103 event_data->out_fn();
104 }
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700105 }
106}
107
108void EPoll::Quit() { PCHECK(write(quit_signal_fd_, "q", 1) == 1); }
109
110void EPoll::OnReadable(int fd, ::std::function<void()> function) {
Brian Silverman441591b2020-01-31 17:44:32 -0800111 EventData *event_data = GetEventData(fd);
112 if (event_data == nullptr) {
113 fns_.emplace_back(std::make_unique<EventData>(fd));
114 event_data = fns_.back().get();
115 } else {
116 CHECK(!event_data->in_fn) << ": Duplicate in functions for " << fd;
117 }
118 event_data->in_fn = ::std::move(function);
119 DoEpollCtl(event_data, event_data->events | kInEvents);
120}
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700121
Brian Silverman441591b2020-01-31 17:44:32 -0800122void EPoll::OnWriteable(int fd, ::std::function<void()> function) {
123 EventData *event_data = GetEventData(fd);
124 if (event_data == nullptr) {
125 fns_.emplace_back(std::make_unique<EventData>(fd));
126 event_data = fns_.back().get();
127 } else {
128 CHECK(!event_data->out_fn) << ": Duplicate out functions for " << fd;
129 }
130 event_data->out_fn = ::std::move(function);
131 DoEpollCtl(event_data, event_data->events | kOutEvents);
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700132}
133
134// Removes fd from the event loop.
135void EPoll::DeleteFd(int fd) {
136 auto element = fns_.begin();
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700137 while (fns_.end() != element) {
138 if (element->get()->fd == fd) {
139 fns_.erase(element);
Brian Silverman441591b2020-01-31 17:44:32 -0800140 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == 0);
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700141 return;
142 }
143 ++element;
144 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700145 LOG(FATAL) << "fd " << fd << " not found";
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700146}
147
Brian Silverman441591b2020-01-31 17:44:32 -0800148void EPoll::EnableEvents(int fd, uint32_t events) {
149 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
150 DoEpollCtl(event_data, event_data->events | events);
151}
152
153void EPoll::DisableEvents(int fd, uint32_t events) {
154 EventData *const event_data = CHECK_NOTNULL(GetEventData(fd));
155 DoEpollCtl(event_data, event_data->events & ~events);
156}
157
158EPoll::EventData *EPoll::GetEventData(int fd) {
159 const auto iterator = std::find_if(
160 fns_.begin(), fns_.end(),
161 [fd](const std::unique_ptr<EventData> &data) { return data->fd == fd; });
162 if (iterator == fns_.end()) {
163 return nullptr;
164 }
165 return iterator->get();
166}
167
168void EPoll::DoEpollCtl(EventData *event_data, const uint32_t new_events) {
169 const uint32_t old_events = event_data->events;
170 event_data->events = new_events;
171 if (new_events == 0) {
172 if (old_events == 0) {
173 // Not added, and doesn't need to be. Nothing to do here.
174 return;
175 }
176 // It was added, but should now be removed.
177 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, event_data->fd, nullptr) == 0);
178 return;
179 }
180
181 int operation = EPOLL_CTL_MOD;
182 if (old_events == 0) {
183 // If it wasn't added before, then this is the first time it's being added.
184 operation = EPOLL_CTL_ADD;
185 }
186 struct epoll_event event;
187 event.events = event_data->events;
188 event.data.ptr = event_data;
189 PCHECK(epoll_ctl(epoll_fd_, operation, event_data->fd, &event) == 0)
190 << ": Failed to " << operation << " epoll fd: " << event_data->fd;
191}
192
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700193} // namespace internal
194} // namespace aos