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