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 #pragma once
8
9 #include "td/utils/common.h"
10 #include "td/utils/format.h"
11 #include "td/utils/List.h"
12 #include "td/utils/logging.h"
13 #include "td/utils/Observer.h"
14 #include "td/utils/port/detail/NativeFd.h"
15 #include "td/utils/port/PollFlags.h"
16 #include "td/utils/SpinLock.h"
17
18 #include <atomic>
19 #include <memory>
20
21 namespace td {
22
23 class PollableFdInfo;
24 class PollableFdInfoUnlock {
25 public:
26 void operator()(PollableFdInfo *ptr);
27 };
28
29 class PollableFd;
30 class PollableFdRef {
31 public:
PollableFdRef(ListNode * list_node)32 explicit PollableFdRef(ListNode *list_node) : list_node_(list_node) {
33 }
34 PollableFd lock();
35
36 private:
37 ListNode *list_node_;
38 };
39
40 class PollableFd {
41 public:
42 // Interface for kqueue, epoll and e.t.c.
43 const NativeFd &native_fd() const;
44
45 ListNode *release_as_list_node();
46 PollableFdRef ref();
47 static PollableFd from_list_node(ListNode *node);
48 void add_flags(PollFlags flags);
49 PollFlags get_flags_unsafe() const;
50
51 private:
52 std::unique_ptr<PollableFdInfo, PollableFdInfoUnlock> fd_info_;
53 friend class PollableFdInfo;
54
PollableFd(std::unique_ptr<PollableFdInfo,PollableFdInfoUnlock> fd_info)55 explicit PollableFd(std::unique_ptr<PollableFdInfo, PollableFdInfoUnlock> fd_info) : fd_info_(std::move(fd_info)) {
56 }
57 };
58
lock()59 inline PollableFd PollableFdRef::lock() {
60 return PollableFd::from_list_node(list_node_);
61 }
62
63 class PollableFdInfo final : private ListNode {
64 public:
65 PollableFdInfo() = default;
66 PollableFdInfo(const PollableFdInfo &) = delete;
67 PollableFdInfo &operator=(const PollableFdInfo &) = delete;
68 PollableFdInfo(PollableFdInfo &&) = delete;
69 PollableFdInfo &operator=(PollableFdInfo &&) = delete;
70
extract_pollable_fd(ObserverBase * observer)71 PollableFd extract_pollable_fd(ObserverBase *observer) {
72 VLOG(fd) << native_fd() << " extract pollable fd " << tag("observer", observer);
73 CHECK(!empty());
74 bool was_locked = lock_.test_and_set(std::memory_order_acquire);
75 CHECK(!was_locked);
76 set_observer(observer);
77 return PollableFd{std::unique_ptr<PollableFdInfo, PollableFdInfoUnlock>{this}};
78 }
get_pollable_fd_ref()79 PollableFdRef get_pollable_fd_ref() {
80 CHECK(!empty());
81 bool was_locked = lock_.test_and_set(std::memory_order_acquire);
82 CHECK(was_locked);
83 return PollableFdRef{as_list_node()};
84 }
85
add_flags(PollFlags flags)86 void add_flags(PollFlags flags) {
87 flags_.write_flags_local(flags);
88 }
89
clear_flags(PollFlags flags)90 void clear_flags(PollFlags flags) {
91 flags_.clear_flags(flags);
92 }
sync_with_poll()93 PollFlags sync_with_poll() const {
94 return flags_.read_flags();
95 }
get_flags_local()96 PollFlags get_flags_local() const {
97 return flags_.read_flags_local();
98 }
99
empty()100 bool empty() const {
101 return !fd_;
102 }
103
set_native_fd(NativeFd new_native_fd)104 void set_native_fd(NativeFd new_native_fd) {
105 if (fd_) {
106 CHECK(!new_native_fd);
107 bool was_locked = lock_.test_and_set(std::memory_order_acquire);
108 CHECK(!was_locked);
109 lock_.clear(std::memory_order_release);
110 }
111
112 fd_ = std::move(new_native_fd);
113 }
PollableFdInfo(NativeFd native_fd)114 explicit PollableFdInfo(NativeFd native_fd) {
115 set_native_fd(std::move(native_fd));
116 }
native_fd()117 const NativeFd &native_fd() const {
118 //CHECK(!empty());
119 return fd_;
120 }
move_as_native_fd()121 NativeFd move_as_native_fd() {
122 return std::move(fd_);
123 }
124
~PollableFdInfo()125 ~PollableFdInfo() {
126 VLOG(fd) << native_fd() << " destroy PollableFdInfo";
127 bool was_locked = lock_.test_and_set(std::memory_order_acquire);
128 CHECK(!was_locked);
129 }
130
add_flags_from_poll(PollFlags flags)131 void add_flags_from_poll(PollFlags flags) {
132 VLOG(fd) << native_fd() << " add flags from poll " << flags;
133 if (flags_.write_flags(flags)) {
134 notify_observer();
135 }
136 }
137
138 private:
139 NativeFd fd_{};
140 std::atomic_flag lock_ = ATOMIC_FLAG_INIT;
141 PollFlagsSet flags_;
142 #if TD_PORT_WINDOWS
143 SpinLock observer_lock_;
144 #endif
145 ObserverBase *observer_{nullptr};
146
147 friend class PollableFd;
148 friend class PollableFdInfoUnlock;
149
set_observer(ObserverBase * observer)150 void set_observer(ObserverBase *observer) {
151 #if TD_PORT_WINDOWS
152 auto lock = observer_lock_.lock();
153 #endif
154 CHECK(observer_ == nullptr);
155 observer_ = observer;
156 }
clear_observer()157 void clear_observer() {
158 #if TD_PORT_WINDOWS
159 auto lock = observer_lock_.lock();
160 #endif
161 observer_ = nullptr;
162 }
notify_observer()163 void notify_observer() {
164 #if TD_PORT_WINDOWS
165 auto lock = observer_lock_.lock();
166 #endif
167 VLOG(fd) << native_fd() << " notify " << tag("observer", observer_);
168 if (observer_ != nullptr) {
169 observer_->notify();
170 }
171 }
172
unlock()173 void unlock() {
174 clear_observer();
175 lock_.clear(std::memory_order_release);
176 as_list_node()->remove();
177 }
178
as_list_node()179 ListNode *as_list_node() {
180 return static_cast<ListNode *>(this);
181 }
from_list_node(ListNode * list_node)182 static PollableFdInfo *from_list_node(ListNode *list_node) {
183 return static_cast<PollableFdInfo *>(list_node);
184 }
185 };
operator()186 inline void PollableFdInfoUnlock::operator()(PollableFdInfo *ptr) {
187 ptr->unlock();
188 }
189
release_as_list_node()190 inline ListNode *PollableFd::release_as_list_node() {
191 return fd_info_.release()->as_list_node();
192 }
ref()193 inline PollableFdRef PollableFd::ref() {
194 return PollableFdRef{fd_info_->as_list_node()};
195 }
from_list_node(ListNode * node)196 inline PollableFd PollableFd::from_list_node(ListNode *node) {
197 return PollableFd(std::unique_ptr<PollableFdInfo, PollableFdInfoUnlock>(PollableFdInfo::from_list_node(node)));
198 }
199
add_flags(PollFlags flags)200 inline void PollableFd::add_flags(PollFlags flags) {
201 fd_info_->add_flags_from_poll(flags);
202 }
get_flags_unsafe()203 inline PollFlags PollableFd::get_flags_unsafe() const {
204 return fd_info_->get_flags_local();
205 }
native_fd()206 inline const NativeFd &PollableFd::native_fd() const {
207 return fd_info_->native_fd();
208 }
209
210 template <class FdT>
sync_with_poll(const FdT & fd)211 void sync_with_poll(const FdT &fd) {
212 fd.get_poll_info().sync_with_poll();
213 }
214
215 template <class FdT>
can_read_local(const FdT & fd)216 bool can_read_local(const FdT &fd) {
217 return fd.get_poll_info().get_flags_local().can_read() || fd.get_poll_info().get_flags_local().has_pending_error();
218 }
219
220 template <class FdT>
can_write_local(const FdT & fd)221 bool can_write_local(const FdT &fd) {
222 return fd.get_poll_info().get_flags_local().can_write();
223 }
224
225 template <class FdT>
can_close_local(const FdT & fd)226 bool can_close_local(const FdT &fd) {
227 return fd.get_poll_info().get_flags_local().can_close();
228 }
229
230 } // namespace td
231