blob: e19cf599a1b77065bce6737e08d738b9d3d05f27 [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() {
74 while (true) {
75 // Pull a single event out. Infinite timeout if we are supposed to be
76 // running, and 0 length timeout otherwise. This lets us flush the event
77 // queue before quitting.
78 struct epoll_event event;
79 int num_events = epoll_wait(epoll_fd_, &event, 1, run_ ? -1 : 0);
80 // Retry on EINTR and nothing else.
81 if (num_events == -1) {
82 if (errno == EINTR) {
83 continue;
84 }
85 PCHECK(num_events != -1);
86 }
87 if (!run_) {
88 // If we ran out of events, quit.
89 if (num_events == 0) {
90 return;
91 }
92 }
93 EventData *event_data = static_cast<struct EventData *>(event.data.ptr);
94 if (event.events & (EPOLLIN | EPOLLPRI)) {
95 event_data->in_fn();
96 }
97 }
98}
99
100void EPoll::Quit() { PCHECK(write(quit_signal_fd_, "q", 1) == 1); }
101
102void EPoll::OnReadable(int fd, ::std::function<void()> function) {
103 ::std::unique_ptr<EventData> event_data(
104 new EventData{fd, ::std::move(function)});
105
106 struct epoll_event event;
107 event.events = EPOLLIN | EPOLLPRI;
108 event.data.ptr = event_data.get();
Austin Schuhf257f3c2019-10-27 21:00:43 -0700109 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event) == 0)
110 << ": Failed to add fd " << fd;
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700111 fns_.push_back(::std::move(event_data));
112}
113
114// Removes fd from the event loop.
115void EPoll::DeleteFd(int fd) {
116 auto element = fns_.begin();
Austin Schuhf257f3c2019-10-27 21:00:43 -0700117 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == 0);
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700118 while (fns_.end() != element) {
119 if (element->get()->fd == fd) {
120 fns_.erase(element);
121 return;
122 }
123 ++element;
124 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700125 LOG(FATAL) << "fd " << fd << " not found";
Austin Schuh6b6dfa52019-06-12 20:16:20 -0700126}
127
128} // namespace internal
129} // namespace aos