1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "td/utils/port/detail/Epoll.h"
8 
9 char disable_linker_warning_about_empty_file_epoll_cpp TD_UNUSED;
10 
11 #ifdef TD_POLL_EPOLL
12 
13 #include "td/utils/format.h"
14 #include "td/utils/logging.h"
15 #include "td/utils/Status.h"
16 
17 #include <cerrno>
18 
19 #include <unistd.h>
20 
21 namespace td {
22 namespace detail {
init()23 void Epoll::init() {
24   CHECK(!epoll_fd_);
25   epoll_fd_ = NativeFd(epoll_create(1));
26   auto epoll_create_errno = errno;
27   LOG_IF(FATAL, !epoll_fd_) << Status::PosixError(epoll_create_errno, "epoll_create failed");
28 
29   events_.resize(1000);
30 }
31 
clear()32 void Epoll::clear() {
33   if (!epoll_fd_) {
34     return;
35   }
36   events_.clear();
37 
38   epoll_fd_.close();
39 
40   for (auto *list_node = list_root_.next; list_node != &list_root_;) {
41     auto pollable_fd = PollableFd::from_list_node(list_node);
42     list_node = list_node->next;
43   }
44 }
45 
subscribe(PollableFd fd,PollFlags flags)46 void Epoll::subscribe(PollableFd fd, PollFlags flags) {
47   epoll_event event;
48   event.events = EPOLLHUP | EPOLLERR | EPOLLET;
49 #ifdef EPOLLRDHUP
50   event.events |= EPOLLRDHUP;
51 #endif
52   if (flags.can_read()) {
53     event.events |= EPOLLIN;
54   }
55   if (flags.can_write()) {
56     event.events |= EPOLLOUT;
57   }
58   auto native_fd = fd.native_fd().fd();
59   auto *list_node = fd.release_as_list_node();
60   list_root_.put(list_node);
61   event.data.ptr = list_node;
62 
63   int err = epoll_ctl(epoll_fd_.fd(), EPOLL_CTL_ADD, native_fd, &event);
64   auto epoll_ctl_errno = errno;
65   LOG_IF(FATAL, err == -1) << Status::PosixError(epoll_ctl_errno, "epoll_ctl ADD failed")
66                            << ", epoll_fd = " << epoll_fd_.fd() << ", fd = " << native_fd;
67 }
68 
unsubscribe(PollableFdRef fd_ref)69 void Epoll::unsubscribe(PollableFdRef fd_ref) {
70   auto fd = fd_ref.lock();
71   auto native_fd = fd.native_fd().fd();
72   int err = epoll_ctl(epoll_fd_.fd(), EPOLL_CTL_DEL, native_fd, nullptr);
73   auto epoll_ctl_errno = errno;
74   LOG_IF(FATAL, err == -1) << Status::PosixError(epoll_ctl_errno, "epoll_ctl DEL failed")
75                            << ", epoll_fd = " << epoll_fd_.fd() << ", fd = " << native_fd
76                            << ", status = " << fd.native_fd().validate();
77 }
78 
unsubscribe_before_close(PollableFdRef fd)79 void Epoll::unsubscribe_before_close(PollableFdRef fd) {
80   unsubscribe(fd);
81 }
82 
run(int timeout_ms)83 void Epoll::run(int timeout_ms) {
84   int ready_n = epoll_wait(epoll_fd_.fd(), &events_[0], static_cast<int>(events_.size()), timeout_ms);
85   auto epoll_wait_errno = errno;
86   LOG_IF(FATAL, ready_n == -1 && epoll_wait_errno != EINTR)
87       << Status::PosixError(epoll_wait_errno, "epoll_wait failed");
88 
89   for (int i = 0; i < ready_n; i++) {
90     PollFlags flags;
91     epoll_event *event = &events_[i];
92     if (event->events & EPOLLIN) {
93       event->events &= ~EPOLLIN;
94       flags = flags | PollFlags::Read();
95     }
96     if (event->events & EPOLLOUT) {
97       event->events &= ~EPOLLOUT;
98       flags = flags | PollFlags::Write();
99     }
100 #ifdef EPOLLRDHUP
101     if (event->events & EPOLLRDHUP) {
102       event->events &= ~EPOLLRDHUP;
103       flags = flags | PollFlags::Close();
104     }
105 #endif
106     if (event->events & EPOLLHUP) {
107       event->events &= ~EPOLLHUP;
108       flags = flags | PollFlags::Close();
109     }
110     if (event->events & EPOLLERR) {
111       event->events &= ~EPOLLERR;
112       flags = flags | PollFlags::Error();
113     }
114     if (event->events) {
115       LOG(FATAL) << "Unsupported epoll events: " << static_cast<int32>(event->events);
116     }
117     //LOG(DEBUG) << "Epoll event " << tag("fd", event->data.fd) << tag("flags", format::as_binary(flags));
118     auto pollable_fd = PollableFd::from_list_node(static_cast<ListNode *>(event->data.ptr));
119     pollable_fd.add_flags(flags);
120     pollable_fd.release_as_list_node();
121   }
122 }
123 }  // namespace detail
124 }  // namespace td
125 
126 #endif
127