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