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