1 // Copyright 2012 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_LIBEVENT_H_ 6 #define BASE_MESSAGE_LOOP_MESSAGE_PUMP_LIBEVENT_H_ 7 8 #include <memory> 9 #include <tuple> 10 11 #include "base/base_export.h" 12 #include "base/compiler_specific.h" 13 #include "base/memory/raw_ptr.h" 14 #include "base/memory/raw_ptr_exclusion.h" 15 #include "base/memory/weak_ptr.h" 16 #include "base/message_loop/message_pump.h" 17 #include "base/message_loop/message_pump_buildflags.h" 18 #include "base/message_loop/watchable_io_message_pump_posix.h" 19 #include "base/threading/thread_checker.h" 20 #include "third_party/libevent/event.h" 21 22 // Declare structs we need from libevent.h rather than including it 23 struct event_base; 24 struct event; 25 namespace base { 26 27 class MessagePumpEpoll; 28 29 // Class to monitor sockets and issue callbacks when sockets are ready for I/O 30 // TODO(dkegel): add support for background file IO somehow 31 class BASE_EXPORT MessagePumpLibevent : public MessagePump, 32 public WatchableIOMessagePumpPosix { 33 public: 34 class FdWatchController; 35 36 // Parameters used to construct and describe an EpollInterest. 37 struct EpollInterestParams { 38 // The file descriptor of interest. 39 int fd; 40 41 // Indicates an interest in being able to read() from `fd`. 42 bool read; 43 44 // Indicates an interest in being able to write() to `fd`. 45 bool write; 46 47 // Indicates whether this interest is a one-shot interest, meaning that it 48 // must be automatically deactivated every time it triggers an epoll event. 49 bool one_shot; 50 IsEqualEpollInterestParams51 bool IsEqual(const EpollInterestParams& rhs) const { 52 return std::tie(fd, read, write, one_shot) == 53 std::tie(rhs.fd, rhs.read, rhs.write, rhs.one_shot); 54 } 55 }; 56 57 // Represents a single controller's interest in a file descriptor via epoll, 58 // and tracks whether that interest is currently active. Though an interest 59 // persists as long as its controller is alive and hasn't changed interests, 60 // it only participates in epoll waits while active. These objects are only 61 // used when MessagePumpLibevent is configured to use the epoll API instead of 62 // libevent. 63 class EpollInterest : public RefCounted<EpollInterest> { 64 public: 65 EpollInterest(FdWatchController* controller, 66 const EpollInterestParams& params); 67 EpollInterest(const EpollInterest&) = delete; 68 EpollInterest& operator=(const EpollInterest&) = delete; 69 controller()70 FdWatchController* controller() { return controller_; } params()71 const EpollInterestParams& params() const { return params_; } 72 active()73 bool active() const { return active_; } set_active(bool active)74 void set_active(bool active) { active_ = active; } 75 76 // Only meaningful between WatchForControllerDestruction() and 77 // StopWatchingForControllerDestruction(). was_controller_destroyed()78 bool was_controller_destroyed() const { return was_controller_destroyed_; } 79 WatchForControllerDestruction()80 void WatchForControllerDestruction() { 81 DCHECK(!controller_->was_destroyed_); 82 controller_->was_destroyed_ = &was_controller_destroyed_; 83 } 84 StopWatchingForControllerDestruction()85 void StopWatchingForControllerDestruction() { 86 if (!was_controller_destroyed_) { 87 DCHECK_EQ(controller_->was_destroyed_, &was_controller_destroyed_); 88 controller_->was_destroyed_ = nullptr; 89 } 90 } 91 92 private: 93 friend class RefCounted<EpollInterest>; 94 ~EpollInterest(); 95 96 const raw_ptr<FdWatchController, DanglingUntriaged> controller_; 97 const EpollInterestParams params_; 98 bool active_ = true; 99 bool was_controller_destroyed_ = false; 100 }; 101 102 // Note that this class is used as the FdWatchController for both 103 // MessagePumpLibevent *and* MessagePumpEpoll in order to avoid unnecessary 104 // code churn during experimentation and eventual transition. Consumers 105 // construct their own FdWatchController instances, so switching this type 106 // at runtime would require potentially complex logic changes to all 107 // consumers. 108 class FdWatchController : public FdWatchControllerInterface { 109 public: 110 explicit FdWatchController(const Location& from_here); 111 112 FdWatchController(const FdWatchController&) = delete; 113 FdWatchController& operator=(const FdWatchController&) = delete; 114 115 // Implicitly calls StopWatchingFileDescriptor. 116 ~FdWatchController() override; 117 118 // FdWatchControllerInterface: 119 bool StopWatchingFileDescriptor() override; 120 121 private: 122 friend class MessagePumpEpoll; 123 friend class MessagePumpLibevent; 124 friend class MessagePumpLibeventTest; 125 126 // Common methods called by both pump implementations. set_watcher(FdWatcher * watcher)127 void set_watcher(FdWatcher* watcher) { watcher_ = watcher; } 128 129 // Methods called only by MessagePumpLibevent set_libevent_pump(MessagePumpLibevent * pump)130 void set_libevent_pump(MessagePumpLibevent* pump) { libevent_pump_ = pump; } libevent_pump()131 MessagePumpLibevent* libevent_pump() const { return libevent_pump_; } 132 133 void Init(std::unique_ptr<event> e); 134 std::unique_ptr<event> ReleaseEvent(); 135 136 void OnFileCanReadWithoutBlocking(int fd, MessagePumpLibevent* pump); 137 void OnFileCanWriteWithoutBlocking(int fd, MessagePumpLibevent* pump); 138 139 // Methods called only by MessagePumpEpoll set_epoll_pump(WeakPtr<MessagePumpEpoll> pump)140 void set_epoll_pump(WeakPtr<MessagePumpEpoll> pump) { 141 epoll_pump_ = std::move(pump); 142 } epoll_interest()143 const scoped_refptr<EpollInterest>& epoll_interest() const { 144 return epoll_interest_; 145 } 146 147 // Creates a new Interest described by `params` and adopts it as this 148 // controller's exclusive interest. Any prior interest is dropped by the 149 // controller and should be unregistered on the MessagePumpEpoll. 150 const scoped_refptr<EpollInterest>& AssignEpollInterest( 151 const EpollInterestParams& params); 152 153 void OnFdReadable(); 154 void OnFdWritable(); 155 156 // Common state 157 raw_ptr<FdWatcher> watcher_ = nullptr; 158 159 // If this pointer is non-null when the FdWatchController is destroyed, the 160 // pointee is set to true. 161 raw_ptr<bool> was_destroyed_ = nullptr; 162 163 // State used only with libevent 164 std::unique_ptr<event> event_; 165 166 // Tests (e.g. FdWatchControllerPosixTest) deliberately make this dangle. 167 raw_ptr<MessagePumpLibevent, DisableDanglingPtrDetection> libevent_pump_ = 168 nullptr; 169 170 // State used only with epoll 171 WeakPtr<MessagePumpEpoll> epoll_pump_; 172 scoped_refptr<EpollInterest> epoll_interest_; 173 }; 174 175 MessagePumpLibevent(); 176 177 #if BUILDFLAG(ENABLE_MESSAGE_PUMP_EPOLL) 178 // Constructs a MessagePumpLibevent which is forced to use epoll directly 179 // instead of libevent. 180 enum { kUseEpoll }; 181 explicit MessagePumpLibevent(decltype(kUseEpoll)); 182 #endif 183 184 MessagePumpLibevent(const MessagePumpLibevent&) = delete; 185 MessagePumpLibevent& operator=(const MessagePumpLibevent&) = delete; 186 187 ~MessagePumpLibevent() override; 188 189 // Must be called early in process startup, but after FeatureList 190 // initialization. This allows MessagePumpLibevent to query and cache the 191 // enabled state of any relevant features. 192 static void InitializeFeatures(); 193 194 bool WatchFileDescriptor(int fd, 195 bool persistent, 196 int mode, 197 FdWatchController* controller, 198 FdWatcher* delegate); 199 200 // MessagePump methods: 201 void Run(Delegate* delegate) override; 202 void Quit() override; 203 void ScheduleWork() override; 204 void ScheduleDelayedWork( 205 const Delegate::NextWorkInfo& next_work_info) override; 206 207 private: 208 friend class MessagePumpLibeventTest; 209 210 // Risky part of constructor. Returns true on success. 211 bool Init(); 212 213 // Called by libevent to tell us a registered FD can be read/written to. 214 static void OnLibeventNotification(int fd, short flags, void* context); 215 216 // Unix pipe used to implement ScheduleWork() 217 // ... callback; called by libevent inside Run() when pipe is ready to read 218 static void OnWakeup(int socket, short flags, void* context); 219 220 struct RunState { RunStateRunState221 explicit RunState(Delegate* delegate_in) : delegate(delegate_in) {} 222 223 // `delegate` is not a raw_ptr<...> for performance reasons (based on 224 // analysis of sampling profiler data and tab_search:top100:2020). 225 RAW_PTR_EXCLUSION Delegate* const delegate; 226 227 // Used to flag that the current Run() invocation should return ASAP. 228 bool should_quit = false; 229 }; 230 231 #if BUILDFLAG(ENABLE_MESSAGE_PUMP_EPOLL) 232 // If direct use of epoll is enabled, this is the MessagePumpEpoll instance 233 // used. In that case, all libevent state below is ignored and unused. 234 // Otherwise this is null. 235 std::unique_ptr<MessagePumpEpoll> epoll_pump_; 236 #endif 237 238 // State for the current invocation of Run(). null if not running. 239 // This field is not a raw_ptr<> because it was filtered by the rewriter for: 240 // #addr-of 241 RAW_PTR_EXCLUSION RunState* run_state_ = nullptr; 242 243 // This flag is set if libevent has processed I/O events. 244 bool processed_io_events_ = false; 245 246 struct EventBaseFree { operatorEventBaseFree247 inline void operator()(event_base* e) const { 248 if (e) 249 event_base_free(e); 250 } 251 }; 252 // Libevent dispatcher. Watches all sockets registered with it, and sends 253 // readiness callbacks when a socket is ready for I/O. 254 std::unique_ptr<event_base, EventBaseFree> event_base_{event_base_new()}; 255 256 // ... write end; ScheduleWork() writes a single byte to it 257 int wakeup_pipe_in_ = -1; 258 // ... read end; OnWakeup reads it and then breaks Run() out of its sleep 259 int wakeup_pipe_out_ = -1; 260 // ... libevent wrapper for read end 261 std::unique_ptr<event> wakeup_event_; 262 263 ThreadChecker watch_file_descriptor_caller_checker_; 264 }; 265 266 } // namespace base 267 268 #endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_LIBEVENT_H_ 269