blob: 83f44c361657b74c684a4aa560d539b8d5f3a352 [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
10#include "aos/logging/logging.h"
11#include "aos/time/time.h"
12
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
22void TimerFd::SetTime(monotonic_clock::time_point start,
23 monotonic_clock::duration interval) {
24 struct itimerspec new_value;
25 new_value.it_interval = ::aos::time::to_timespec(interval);
26 new_value.it_value = ::aos::time::to_timespec(start);
27 PCHECK(timerfd_settime(fd_, TFD_TIMER_ABSTIME, &new_value, nullptr) == 0);
28}
29
30void TimerFd::Read() {
31 uint64_t buf;
32 ssize_t result = read(fd_, &buf, sizeof(buf));
33 PCHECK(result != -1);
34 CHECK_EQ(result, static_cast<int>(sizeof(buf)));
35}
36
37EPoll::EPoll() : epoll_fd_(epoll_create1(EPOLL_CLOEXEC)) {
38 PCHECK(epoll_fd_ > 0);
39
40 // Create a pipe for the Quit function. We want to use a pipe to be async
41 // safe so this can be called from signal handlers.
42 int pipefd[2];
43 PCHECK(pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == 0);
44 quit_epoll_fd_ = pipefd[0];
45 quit_signal_fd_ = pipefd[1];
46 // Read the fd when data is sent and set run_ to false.
47 OnReadable(quit_epoll_fd_, [this]() {
48 run_ = false;
49 char buf[1];
50 PCHECK(read(quit_epoll_fd_, &buf[0], 1) == 1);
51 });
52}
53
54EPoll::~EPoll() {
55 // Clean up the quit pipe and epoll fd.
56 DeleteFd(quit_epoll_fd_);
57 close(quit_signal_fd_);
58 close(quit_epoll_fd_);
59 CHECK_EQ(fns_.size(), 0u);
60 close(epoll_fd_);
61}
62
63void EPoll::Run() {
64 while (true) {
65 // Pull a single event out. Infinite timeout if we are supposed to be
66 // running, and 0 length timeout otherwise. This lets us flush the event
67 // queue before quitting.
68 struct epoll_event event;
69 int num_events = epoll_wait(epoll_fd_, &event, 1, run_ ? -1 : 0);
70 // Retry on EINTR and nothing else.
71 if (num_events == -1) {
72 if (errno == EINTR) {
73 continue;
74 }
75 PCHECK(num_events != -1);
76 }
77 if (!run_) {
78 // If we ran out of events, quit.
79 if (num_events == 0) {
80 return;
81 }
82 }
83 EventData *event_data = static_cast<struct EventData *>(event.data.ptr);
84 if (event.events & (EPOLLIN | EPOLLPRI)) {
85 event_data->in_fn();
86 }
87 }
88}
89
90void EPoll::Quit() { PCHECK(write(quit_signal_fd_, "q", 1) == 1); }
91
92void EPoll::OnReadable(int fd, ::std::function<void()> function) {
93 ::std::unique_ptr<EventData> event_data(
94 new EventData{fd, ::std::move(function)});
95
96 struct epoll_event event;
97 event.events = EPOLLIN | EPOLLPRI;
98 event.data.ptr = event_data.get();
99 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event) != 0);
100 fns_.push_back(::std::move(event_data));
101}
102
103// Removes fd from the event loop.
104void EPoll::DeleteFd(int fd) {
105 auto element = fns_.begin();
106 PCHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) != 0);
107 while (fns_.end() != element) {
108 if (element->get()->fd == fd) {
109 fns_.erase(element);
110 return;
111 }
112 ++element;
113 }
114 LOG(FATAL, "fd %d not found\n", fd);
115}
116
117} // namespace internal
118} // namespace aos