• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   static void InitializeFeatures();
109 
110   // MessagePump:
111   void Run(Delegate* delegate) override;
112   // Simplified version of the loop used under experiment (crbug.com/1200141)
113   void RunSimplified(Delegate* delegate);
114   void Quit() override;
115   void ScheduleWork() override;
116   void ScheduleDelayedWork(
117       const Delegate::NextWorkInfo& next_work_info) override;
118   TimeTicks AdjustDelayedRunTime(TimeTicks earliest_time,
119                                  TimeTicks run_time,
120                                  TimeTicks latest_time) override;
121 
122   // Begins watching the Mach receive right named by |port|. The |controller|
123   // can be used to stop watching for incoming messages, and new message
124   // notifications are delivered to the |delegate|. Returns true if the watch
125   // was successfully set-up and false on error.
126   bool WatchMachReceivePort(mach_port_t port,
127                             MachPortWatchController* controller,
128                             MachPortWatcher* delegate);
129 
130   // WatchableIOMessagePumpPosix:
131   bool WatchFileDescriptor(int fd,
132                            bool persistent,
133                            int mode,
134                            FdWatchController* controller,
135                            FdWatcher* delegate);
136 
137  private:
138   // Called by the watch controller implementations to stop watching the
139   // respective types of handles.
140   bool StopWatchingMachPort(MachPortWatchController* controller);
141   bool StopWatchingFileDescriptor(FdWatchController* controller);
142 
143   // Checks the |kqueue_| for events. If |next_work_info| is null, then the
144   // kqueue will be polled for events. If it is non-null, it will wait for the
145   // amount of time specified by the NextWorkInfo or until an event is
146   // triggered. Returns whether any events were dispatched, with the events
147   // stored in |events_|.
148   bool DoInternalWork(Delegate* delegate,
149                       Delegate::NextWorkInfo* next_work_info);
150 
151   // Called by DoInternalWork() to dispatch the user events stored in |events_|
152   // that were triggered. |count| is the number of events to process. Returns
153   // true if work was done, or false if no work was done.
154   bool ProcessEvents(Delegate* delegate, size_t count);
155 
156   // Updates the wakeup timer to |wakeup_time| if it differs from the currently
157   // scheduled wakeup. Clears the wakeup timer if |wakeup_time| is
158   // base::TimeTicks::Max().
159   // Updates |scheduled_wakeup_time_| to follow.
160   void MaybeUpdateWakeupTimer(const base::TimeTicks& wakeup_time,
161                               base::TimeDelta leeway);
162 
163   void SetWakeupTimerEvent(const base::TimeTicks& wakeup_time,
164                            base::TimeDelta leeway,
165                            kevent64_s* timer_event);
166 
167   // Receive right to which an empty Mach message is sent to wake up the pump
168   // in response to ScheduleWork().
169   apple::ScopedMachReceiveRight wakeup_;
170   // Scratch buffer that is used to receive the message sent to |wakeup_|.
171   mach_msg_empty_rcv_t wakeup_buffer_;
172 
173   // Watch controllers for FDs. IDs are generated by the map and are stored in
174   // the kevent64_s::udata field.
175   IDMap<FdWatchController*, uint64_t> fd_controllers_;
176 
177   // Watch controllers for Mach ports. IDs are the port being watched.
178   IDMap<MachPortWatchController*, mach_port_t> port_controllers_;
179 
180   // The kqueue that drives the pump.
181   ScopedFD kqueue_;
182 
183   // Whether the pump has been Quit() or not.
184   bool keep_running_ = true;
185 
186   // The currently scheduled wakeup, if any. If no wakeup is scheduled,
187   // contains base::TimeTicks::Max().
188   base::TimeTicks scheduled_wakeup_time_{base::TimeTicks::Max()};
189 
190   // The number of events scheduled on the |kqueue_|. There is always at least
191   // 1, for the |wakeup_| port.
192   size_t event_count_ = 1;
193   // Buffer used by DoInternalWork() to be notified of triggered events. This
194   // is always at least |event_count_|-sized.
195   std::vector<kevent64_s> events_{event_count_};
196 
197   WeakPtrFactory<MessagePumpKqueue> weak_factory_;
198 };
199 
200 }  // namespace base
201 
202 #endif  // BASE_MESSAGE_LOOP_MESSAGE_PUMP_KQUEUE_H_
203