xref: /aosp_15_r20/external/cronet/base/message_loop/message_pump_epoll.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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