blob: 3da04cf8ebff7bef61ec9dd996b82ea10ff55367 [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
32void TimerFd::Read() {
33 uint64_t buf;
34 ssize_t result = read(fd_, &buf, sizeof(buf));
35 PCHECK(result != -1);
36 CHECK_EQ(result, static_cast<int>(sizeof(buf)));
37}
38
39EPoll::EPoll() : epoll_fd_(epoll_create1(EPOLL_CLOEXEC)) {
40 PCHECK(epoll_fd_ > 0);
41
42 // Create a pipe for the Quit function. We want to use a pipe to be async
43 // safe so this can be called from signal handlers.
44 int pipefd[2];
45 PCHECK(pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == 0);
46 quit_epoll_fd_ = pipefd[0];
47 quit_signal_fd_ = pipefd[1];
48 // Read the fd when data is sent and set run_ to false.
49 OnReadable(quit_epoll_fd_, [this]() {
50 run_ = false;
51 char buf[1];
52 PCHECK(read(quit_epoll_fd_, &buf[0], 1) == 1);
53 });
54}
55
56EPoll::~EPoll() {
57 // Clean up the quit pipe and epoll fd.
58 DeleteFd(quit_epoll_fd_);
59 close(quit_signal_fd_);
60 close(quit_epoll_fd_);
Austin Schuhf257f3c2019-10-27 21:00:43 -070061 CHECK_EQ(fns_.size(), 0u)
62 << ": Not all file descriptors were unregistered before shutting down.";
Austin Schuh6b6dfa52019-06-12 20:16:20 -070063 close(epoll_fd_);
64}
65
66void EPoll::Run() {
67 while (true) {
68 // Pull a single event out. Infinite timeout if we are supposed to be
69 // running, and 0 length timeout otherwise. This lets us flush the event
70 // queue before quitting.
71 struct epoll_event event;
72 int num_events = epoll_wait(epoll_fd_, &event, 1, run_ ? -1 : 0);
73 // Retry on EINTR and nothing else.
74 if (num_events == -1) {
75 if (errno == EINTR) {
76 continue;
77 }
78 PCHECK(num_events != -1);
79 }
80 if (!run_) {
81 // If we ran out of events, quit.
82 if (num_events == 0) {
83 return;
84 }
85 }
86 EventData *event_data = static_cast<struct EventData *>(event.data.ptr);
87 if (event.events & (EPOLLIN | EPOLLPRI)) {
88 event_data->in_fn();
89 }
90 }
91}
92
93void EPoll::Quit() { PCHECK(write(quit_signal_fd_, "q", 1) == 1); }
94
95void EPoll::OnReadable(int fd, ::std::function<void()> function) {
96 ::std::unique_ptr<EventData> event_data(
97 new EventData{fd, ::std::move(function)});
98
99 struct epoll_event event;
100 event.events = EPOLLIN | EPOLLPRI;
101 event.data.ptr = event_data.get();
Austin Schuhf257f3c2019-10-27 21:00:43 -0700102 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event) == 0)
103 << ": Failed to add fd " << fd;
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700104 fns_.push_back(::std::move(event_data));
105}
106
107// Removes fd from the event loop.
108void EPoll::DeleteFd(int fd) {
109 auto element = fns_.begin();
Austin Schuhf257f3c2019-10-27 21:00:43 -0700110 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == 0);
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700111 while (fns_.end() != element) {
112 if (element->get()->fd == fd) {
113 fns_.erase(element);
114 return;
115 }
116 ++element;
117 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700118 LOG(FATAL) << "fd " << fd << " not found";
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700119}
120
121} // namespace internal
122} // namespace aos