xref: /aosp_15_r20/external/cronet/base/message_loop/message_pump_kqueue.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 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_KQUEUE_H_
6 #define BASE_MESSAGE_LOOP_MESSAGE_PUMP_KQUEUE_H_
7 
8 #include <mach/mach.h>
9 #include <stdint.h>
10 #include <sys/event.h>
11 
12 #include <vector>
13 
14 #include "base/apple/scoped_mach_port.h"
15 #include "base/containers/id_map.h"
16 #include "base/files/scoped_file.h"
17 #include "base/location.h"
18 #include "base/memory/raw_ptr.h"
19 #include "base/memory/weak_ptr.h"
20 #include "base/message_loop/message_pump.h"
21 #include "base/message_loop/watchable_io_message_pump_posix.h"
22 
23 namespace base {
24 
25 // MessagePumpKqueue is used on macOS to drive an IO MessageLoop that is
26 // capable of watching both POSIX file descriptors and Mach ports.
27 class BASE_EXPORT MessagePumpKqueue : public MessagePump,
28                                       public WatchableIOMessagePumpPosix {
29  public:
30   class FdWatchController : public FdWatchControllerInterface {
31    public:
32     explicit FdWatchController(const Location& from_here);
33 
34     FdWatchController(const FdWatchController&) = delete;
35     FdWatchController& operator=(const FdWatchController&) = delete;
36 
37     ~FdWatchController() override;
38 
39     // FdWatchControllerInterface:
40     bool StopWatchingFileDescriptor() override;
41 
42    protected:
43     friend class MessagePumpKqueue;
44 
45     void Init(WeakPtr<MessagePumpKqueue> pump,
46               int fd,
47               int mode,
48               FdWatcher* watcher);
49     void Reset();
50 
fd()51     int fd() { return fd_; }
mode()52     int mode() { return mode_; }
watcher()53     FdWatcher* watcher() { return watcher_; }
54 
55    private:
56     int fd_ = -1;
57     int mode_ = 0;
58     raw_ptr<FdWatcher> watcher_ = nullptr;
59     WeakPtr<MessagePumpKqueue> pump_;
60   };
61 
62   // Delegate interface that provides notifications of Mach message receive
63   // events.
64   class MachPortWatcher {
65    public:
~MachPortWatcher()66     virtual ~MachPortWatcher() {}
67     virtual void OnMachMessageReceived(mach_port_t port) = 0;
68   };
69 
70   // Controller interface that is used to stop receiving events for an
71   // installed MachPortWatcher.
72   class MachPortWatchController {
73    public:
74     explicit MachPortWatchController(const Location& from_here);
75 
76     MachPortWatchController(const MachPortWatchController&) = delete;
77     MachPortWatchController& operator=(const MachPortWatchController&) = delete;
78 
79     ~MachPortWatchController();
80 
81     bool StopWatchingMachPort();
82 
83    protected:
84     friend class MessagePumpKqueue;
85 
86     void Init(WeakPtr<MessagePumpKqueue> pump,
87               mach_port_t port,
88               MachPortWatcher* watcher);
89     void Reset();
90 
port()91     mach_port_t port() { return port_; }
watcher()92     MachPortWatcher* watcher() { return watcher_; }
93 
94    private:
95     mach_port_t port_ = MACH_PORT_NULL;
96     raw_ptr<MachPortWatcher> watcher_ = nullptr;
97     WeakPtr<MessagePumpKqueue> pump_;
98     const Location from_here_;
99   };
100 
101   MessagePumpKqueue();
102 
103   MessagePumpKqueue(const MessagePumpKqueue&) = delete;
104   MessagePumpKqueue& operator=(const MessagePumpKqueue&) = delete;
105 
106   ~MessagePumpKqueue() override;
107 
108   // Initializes features for this class. See `base::features::Init()`.
109   static void InitializeFeatures();
110 
111   // MessagePump:
112   void Run(Delegate* delegate) override;
113   // Simplified version of the loop used under experiment (crbug.com/1200141)
114   void RunSimplified(Delegate* delegate);
115   void Quit() override;
116   void ScheduleWork() override;
117   void ScheduleDelayedWork(
118       const Delegate::NextWorkInfo& next_work_info) override;
119   TimeTicks AdjustDelayedRunTime(TimeTicks earliest_time,
120                                  TimeTicks run_time,
121                                  TimeTicks latest_time) override;
122 
123   // Begins watching the Mach receive right named by |port|. The |controller|
124   // can be used to stop watching for incoming messages, and new message
125   // notifications are delivered to the |delegate|. Returns true if the watch
126   // was successfully set-up and false on error.
127   bool WatchMachReceivePort(mach_port_t port,
128                             MachPortWatchController* controller,
129                             MachPortWatcher* delegate);
130 
131   // WatchableIOMessagePumpPosix:
132   bool WatchFileDescriptor(int fd,
133                            bool persistent,
134                            int mode,
135                            FdWatchController* controller,
136                            FdWatcher* delegate);
137 
138  private:
139   // Called by the watch controller implementations to stop watching the
140   // respective types of handles.
141   bool StopWatchingMachPort(MachPortWatchController* controller);
142   bool StopWatchingFileDescriptor(FdWatchController* controller);
143 
144   // Checks the |kqueue_| for events. If |next_work_info| is null, then the
145   // kqueue will be polled for events. If it is non-null, it will wait for the
146   // amount of time specified by the NextWorkInfo or until an event is
147   // triggered. Returns whether any events were dispatched, with the events
148   // stored in |events_|.
149   bool DoInternalWork(Delegate* delegate,
150                       Delegate::NextWorkInfo* next_work_info);
151 
152   // Called by DoInternalWork() to dispatch the user events stored in |events_|
153   // that were triggered. |count| is the number of events to process. Returns
154   // true if work was done, or false if no work was done.
155   bool ProcessEvents(Delegate* delegate, size_t count);
156 
157   // Updates the wakeup timer to |wakeup_time| if it differs from the currently
158   // scheduled wakeup. Clears the wakeup timer if |wakeup_time| is
159   // base::TimeTicks::Max().
160   // Updates |scheduled_wakeup_time_| to follow.
161   void MaybeUpdateWakeupTimer(const base::TimeTicks& wakeup_time,
162                               base::TimeDelta leeway);
163 
164   void SetWakeupTimerEvent(const base::TimeTicks& wakeup_time,
165                            base::TimeDelta leeway,
166                            kevent64_s* timer_event);
167 
168   // Receive right to which an empty Mach message is sent to wake up the pump
169   // in response to ScheduleWork().
170   apple::ScopedMachReceiveRight wakeup_;
171   // Scratch buffer that is used to receive the message sent to |wakeup_|.
172   mach_msg_empty_rcv_t wakeup_buffer_;
173 
174   // Watch controllers for FDs. IDs are generated by the map and are stored in
175   // the kevent64_s::udata field.
176   IDMap<FdWatchController*, uint64_t> fd_controllers_;
177 
178   // Watch controllers for Mach ports. IDs are the port being watched.
179   IDMap<MachPortWatchController*, mach_port_t> port_controllers_;
180 
181   // The kqueue that drives the pump.
182   ScopedFD kqueue_;
183 
184   // Whether the pump has been Quit() or not.
185   bool keep_running_ = true;
186 
187   // The currently scheduled wakeup, if any. If no wakeup is scheduled,
188   // contains base::TimeTicks::Max().
189   base::TimeTicks scheduled_wakeup_time_{base::TimeTicks::Max()};
190 
191   // The number of events scheduled on the |kqueue_|. There is always at least
192   // 1, for the |wakeup_| port.
193   size_t event_count_ = 1;
194   // Buffer used by DoInternalWork() to be notified of triggered events. This
195   // is always at least |event_count_|-sized.
196   std::vector<kevent64_s> events_{event_count_};
197 
198   WeakPtrFactory<MessagePumpKqueue> weak_factory_;
199 };
200 
201 }  // namespace base
202 
203 #endif  // BASE_MESSAGE_LOOP_MESSAGE_PUMP_KQUEUE_H_
204