1 // Copyright 2022 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_EPOLL_H_ 6 #define BASE_MESSAGE_LOOP_MESSAGE_PUMP_EPOLL_H_ 7 8 #include <sys/epoll.h> 9 10 #include <cstdint> 11 #include <map> 12 13 #include "base/base_export.h" 14 #include "base/files/scoped_file.h" 15 #include "base/memory/raw_ptr.h" 16 #include "base/memory/raw_ptr_exclusion.h" 17 #include "base/memory/ref_counted.h" 18 #include "base/memory/weak_ptr.h" 19 #include "base/message_loop/message_pump.h" 20 #include "base/message_loop/message_pump_libevent.h" 21 #include "base/message_loop/watchable_io_message_pump_posix.h" 22 #include "base/threading/thread_checker.h" 23 #include "base/time/time.h" 24 #include "third_party/abseil-cpp/absl/container/inlined_vector.h" 25 26 namespace base { 27 28 // A MessagePump implementation suitable for I/O message loops on Linux-based 29 // systems with epoll API support. 30 class BASE_EXPORT MessagePumpEpoll : public MessagePump, 31 public WatchableIOMessagePumpPosix { 32 using InterestParams = MessagePumpLibevent::EpollInterestParams; 33 using Interest = MessagePumpLibevent::EpollInterest; 34 35 public: 36 using FdWatchController = MessagePumpLibevent::FdWatchController; 37 38 MessagePumpEpoll(); 39 MessagePumpEpoll(const MessagePumpEpoll&) = delete; 40 MessagePumpEpoll& operator=(const MessagePumpEpoll&) = delete; 41 ~MessagePumpEpoll() override; 42 43 bool WatchFileDescriptor(int fd, 44 bool persistent, 45 int mode, 46 FdWatchController* controller, 47 FdWatcher* watcher); 48 49 // MessagePump methods: 50 void Run(Delegate* delegate) override; 51 void Quit() override; 52 void ScheduleWork() override; 53 void ScheduleDelayedWork( 54 const Delegate::NextWorkInfo& next_work_info) override; 55 56 private: 57 friend class MessagePumpLibevent; 58 friend class MessagePumpLibeventTest; 59 60 // The WatchFileDescriptor API supports multiple FdWatchControllers watching 61 // the same file descriptor, potentially for different events; but the epoll 62 // API only supports a single interest list entry per unique file descriptor. 63 // 64 // EpollEventEntry tracks all epoll state relevant to a single file 65 // descriptor, including references to all active and inactive Interests 66 // concerned with that descriptor. This is used to derive a single aggregate 67 // interest entry for the descriptor when manipulating epoll. 68 struct EpollEventEntry { 69 explicit EpollEventEntry(int fd); 70 EpollEventEntry(const EpollEventEntry&) = delete; 71 EpollEventEntry& operator=(const EpollEventEntry&) = delete; 72 ~EpollEventEntry(); 73 FromEpollEventEpollEventEntry74 static EpollEventEntry& FromEpollEvent(epoll_event& e) { 75 return *static_cast<EpollEventEntry*>(e.data.ptr); 76 } 77 78 // Returns the combined set of epoll event flags which should be monitored 79 // by the epoll instance for `fd`. This is based on a combination of the 80 // parameters of all currently active elements in `interests`. Namely: 81 // - EPOLLIN is set if any active Interest wants to `read`. 82 // - EPOLLOUT is set if any active Interest wants to `write`. 83 // - EPOLLONESHOT is set if all active Interests are one-shot. 84 uint32_t ComputeActiveEvents(); 85 86 // The file descriptor to which this entry pertains. 87 const int fd; 88 89 // A cached copy of the last known epoll event bits registered for this 90 // descriptor on the epoll instance. 91 uint32_t registered_events = 0; 92 93 // A collection of all the interests regarding `fd` on this message pump. 94 // The small amount of inline storage avoids heap allocation in virtually 95 // all real scenarios, since there's little practical value in having more 96 // than two controllers (e.g. one reader and one writer) watch the same 97 // descriptor on the same thread. 98 absl::InlinedVector<scoped_refptr<Interest>, 2> interests; 99 100 // Temporary pointer to an active epoll_event structure which refers to 101 // this entry. This is set immediately upon returning from epoll_wait() and 102 // cleared again immediately before dispatching to any registered interests, 103 // so long as this entry isn't destroyed in the interim. 104 raw_ptr<epoll_event> active_event = nullptr; 105 106 // If the file descriptor is disconnected and no active `interests`, remove 107 // it from the epoll interest list to avoid unconditionally epoll_wait 108 // return, and prevent any future update on this `EpollEventEntry`. 109 bool stopped = false; 110 }; 111 112 // State which lives on the stack within Run(), to support nested run loops. 113 struct RunState { RunStateRunState114 explicit RunState(Delegate* delegate) : delegate(delegate) {} 115 116 // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of sampling 117 // profiler data and tab_search:top100:2020). 118 RAW_PTR_EXCLUSION Delegate* const delegate; 119 120 // Used to flag that the current Run() invocation should return ASAP. 121 bool should_quit = false; 122 }; 123 124 void AddEpollEvent(EpollEventEntry& entry); 125 void UpdateEpollEvent(EpollEventEntry& entry); 126 void StopEpollEvent(EpollEventEntry& entry); 127 void UnregisterInterest(const scoped_refptr<Interest>& interest); 128 bool WaitForEpollEvents(TimeDelta timeout); 129 void OnEpollEvent(EpollEventEntry& entry, uint32_t events); 130 void HandleEvent(int fd, 131 bool can_read, 132 bool can_write, 133 FdWatchController* controller); 134 void HandleWakeUp(); 135 136 void BeginNativeWorkBatch(); 137 138 // Null if Run() is not currently executing. Otherwise it's a pointer into the 139 // stack of the innermost nested Run() invocation. 140 raw_ptr<RunState> run_state_ = nullptr; 141 142 // This flag is set if epoll has processed I/O events. 143 bool processed_io_events_ = false; 144 145 // This flag is set when starting to process native work; reset after every 146 // `DoWork()` call. See crbug.com/1500295. 147 bool native_work_started_ = false; 148 149 // Mapping of all file descriptors currently watched by this message pump. 150 // std::map was chosen because (1) the number of elements can vary widely, 151 // (2) we don't do frequent lookups, and (3) values need stable addresses 152 // across insertion or removal of other elements. 153 std::map<int, EpollEventEntry> entries_; 154 155 // The epoll instance used by this message pump to monitor file descriptors. 156 ScopedFD epoll_; 157 158 // An eventfd object used to wake the pump's thread when scheduling new work. 159 ScopedFD wake_event_; 160 161 // WatchFileDescriptor() must be called from this thread, and so must 162 // FdWatchController::StopWatchingFileDescriptor(). 163 THREAD_CHECKER(thread_checker_); 164 165 WeakPtrFactory<MessagePumpEpoll> weak_ptr_factory_{this}; 166 }; 167 168 } // namespace base 169 170 #endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_EPOLL_H_ 171