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