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/EventFdBsd.h"
8 
9 char disable_linker_warning_about_empty_file_event_fd_bsd_cpp TD_UNUSED;
10 
11 #ifdef TD_EVENTFD_BSD
12 
13 #include "td/utils/logging.h"
14 #include "td/utils/port/detail/NativeFd.h"
15 #include "td/utils/port/detail/skip_eintr.h"
16 #include "td/utils/port/PollFlags.h"
17 #include "td/utils/port/SocketFd.h"
18 #include "td/utils/Slice.h"
19 
20 #include <cerrno>
21 
22 #include <fcntl.h>
23 #include <poll.h>
24 #include <sys/socket.h>
25 #include <sys/types.h>
26 
27 namespace td {
28 namespace detail {
29 
30 // TODO: it is extremely non optimal on Darwin. kqueue events should be used instead
init()31 void EventFdBsd::init() {
32   int fds[2];
33   int err = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
34   auto socketpair_errno = errno;
35 #if TD_CYGWIN
36   // it looks like CYGWIN bug
37   int max_retries = 1000000;
38   while (err == -1 && socketpair_errno == EADDRINUSE && max_retries-- > 0) {
39     err = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
40     socketpair_errno = errno;
41   }
42 // LOG_IF(ERROR, max_retries < 1000000) << max_retries;
43 #endif
44   LOG_IF(FATAL, err == -1) << Status::PosixError(socketpair_errno, "socketpair failed");
45 
46   auto fd_a = NativeFd(fds[0]);
47   auto fd_b = NativeFd(fds[1]);
48   fd_a.set_is_blocking_unsafe(false).ensure();
49   fd_b.set_is_blocking_unsafe(false).ensure();
50 
51   in_ = SocketFd::from_native_fd(std::move(fd_a)).move_as_ok();
52   out_ = SocketFd::from_native_fd(std::move(fd_b)).move_as_ok();
53 }
54 
empty()55 bool EventFdBsd::empty() {
56   return in_.empty();
57 }
58 
close()59 void EventFdBsd::close() {
60   in_.close();
61   out_.close();
62 }
63 
get_pending_error()64 Status EventFdBsd::get_pending_error() {
65   return Status::OK();
66 }
67 
get_poll_info()68 PollableFdInfo &EventFdBsd::get_poll_info() {
69   return out_.get_poll_info();
70 }
71 
release()72 void EventFdBsd::release() {
73   int value = 1;
74   auto result = in_.write(Slice(reinterpret_cast<const char *>(&value), sizeof(value)));
75   if (result.is_error()) {
76     LOG(FATAL) << "EventFdBsd write failed: " << result.error();
77   }
78   size_t size = result.ok();
79   if (size != sizeof(value)) {
80     LOG(FATAL) << "EventFdBsd write returned " << size << " instead of " << sizeof(value);
81   }
82 }
83 
acquire()84 void EventFdBsd::acquire() {
85   sync_with_poll(out_);
86   out_.get_poll_info().add_flags(PollFlags::Read());
87   while (can_read_local(out_)) {
88     uint8 value[1024];
89     auto result = out_.read(MutableSlice(value, sizeof(value)));
90     if (result.is_error()) {
91       LOG(FATAL) << "EventFdBsd read failed:" << result.error();
92     }
93   }
94 }
95 
wait(int timeout_ms)96 void EventFdBsd::wait(int timeout_ms) {
97   detail::skip_eintr_timeout(
98       [this](int timeout_ms) {
99         pollfd fd;
100         fd.fd = get_poll_info().native_fd().fd();
101         fd.events = POLLIN;
102         return poll(&fd, 1, timeout_ms);
103       },
104       timeout_ms);
105 }
106 
107 }  // namespace detail
108 }  // namespace td
109 
110 #endif
111