| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_KQUEUE_H_ |
| #define BASE_MESSAGE_LOOP_MESSAGE_PUMP_KQUEUE_H_ |
| |
| #include <mach/mach.h> |
| #include <stdint.h> |
| #include <sys/event.h> |
| |
| #include <vector> |
| |
| #include "base/containers/id_map.h" |
| #include "base/files/scoped_file.h" |
| #include "base/location.h" |
| #include "base/mac/scoped_mach_port.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/message_loop/message_pump.h" |
| #include "base/message_loop/watchable_io_message_pump_posix.h" |
| |
| namespace base { |
| |
| // MessagePumpKqueue is used on macOS to drive an IO MessageLoop that is |
| // capable of watching both POSIX file descriptors and Mach ports. |
| class BASE_EXPORT MessagePumpKqueue : public MessagePump, |
| public WatchableIOMessagePumpPosix { |
| public: |
| class FdWatchController : public FdWatchControllerInterface { |
| public: |
| explicit FdWatchController(const Location& from_here); |
| |
| FdWatchController(const FdWatchController&) = delete; |
| FdWatchController& operator=(const FdWatchController&) = delete; |
| |
| ~FdWatchController() override; |
| |
| // FdWatchControllerInterface: |
| bool StopWatchingFileDescriptor() override; |
| |
| protected: |
| friend class MessagePumpKqueue; |
| |
| void Init(WeakPtr<MessagePumpKqueue> pump, |
| int fd, |
| int mode, |
| FdWatcher* watcher); |
| void Reset(); |
| |
| int fd() { return fd_; } |
| int mode() { return mode_; } |
| FdWatcher* watcher() { return watcher_; } |
| |
| private: |
| int fd_ = -1; |
| int mode_ = 0; |
| raw_ptr<FdWatcher> watcher_ = nullptr; |
| WeakPtr<MessagePumpKqueue> pump_; |
| }; |
| |
| // Delegate interface that provides notifications of Mach message receive |
| // events. |
| class MachPortWatcher { |
| public: |
| virtual ~MachPortWatcher() {} |
| virtual void OnMachMessageReceived(mach_port_t port) = 0; |
| }; |
| |
| // Controller interface that is used to stop receiving events for an |
| // installed MachPortWatcher. |
| class MachPortWatchController { |
| public: |
| explicit MachPortWatchController(const Location& from_here); |
| |
| MachPortWatchController(const MachPortWatchController&) = delete; |
| MachPortWatchController& operator=(const MachPortWatchController&) = delete; |
| |
| ~MachPortWatchController(); |
| |
| bool StopWatchingMachPort(); |
| |
| protected: |
| friend class MessagePumpKqueue; |
| |
| void Init(WeakPtr<MessagePumpKqueue> pump, |
| mach_port_t port, |
| MachPortWatcher* watcher); |
| void Reset(); |
| |
| mach_port_t port() { return port_; } |
| MachPortWatcher* watcher() { return watcher_; } |
| |
| private: |
| mach_port_t port_ = MACH_PORT_NULL; |
| raw_ptr<MachPortWatcher> watcher_ = nullptr; |
| WeakPtr<MessagePumpKqueue> pump_; |
| const Location from_here_; |
| }; |
| |
| MessagePumpKqueue(); |
| |
| MessagePumpKqueue(const MessagePumpKqueue&) = delete; |
| MessagePumpKqueue& operator=(const MessagePumpKqueue&) = delete; |
| |
| ~MessagePumpKqueue() override; |
| |
| static void InitializeFeatures(); |
| |
| // MessagePump: |
| void Run(Delegate* delegate) override; |
| // Simplified version of the loop used under experiment (crbug.com/1200141) |
| void RunSimplified(Delegate* delegate); |
| void Quit() override; |
| void ScheduleWork() override; |
| void ScheduleDelayedWork( |
| const Delegate::NextWorkInfo& next_work_info) override; |
| |
| // Begins watching the Mach receive right named by |port|. The |controller| |
| // can be used to stop watching for incoming messages, and new message |
| // notifications are delivered to the |delegate|. Returns true if the watch |
| // was successfully set-up and false on error. |
| bool WatchMachReceivePort(mach_port_t port, |
| MachPortWatchController* controller, |
| MachPortWatcher* delegate); |
| |
| // WatchableIOMessagePumpPosix: |
| bool WatchFileDescriptor(int fd, |
| bool persistent, |
| int mode, |
| FdWatchController* controller, |
| FdWatcher* delegate); |
| |
| private: |
| // Called by the watch controller implementations to stop watching the |
| // respective types of handles. |
| bool StopWatchingMachPort(MachPortWatchController* controller); |
| bool StopWatchingFileDescriptor(FdWatchController* controller); |
| |
| // Checks the |kqueue_| for events. If |next_work_info| is null, then the |
| // kqueue will be polled for events. If it is non-null, it will wait for the |
| // amount of time specified by the NextWorkInfo or until an event is |
| // triggered. Returns whether any events were dispatched, with the events |
| // stored in |events_|. |
| bool DoInternalWork(Delegate* delegate, |
| Delegate::NextWorkInfo* next_work_info); |
| |
| // Called by DoInternalWork() to dispatch the user events stored in |events_| |
| // that were triggered. |count| is the number of events to process. Returns |
| // true if work was done, or false if no work was done. |
| bool ProcessEvents(Delegate* delegate, size_t count); |
| |
| // Updates the wakeup timer to |wakeup_time| if it differs from the currently |
| // scheduled wakeup. Clears the wakeup timer if |wakeup_time| is |
| // base::TimeTicks::Max(). |
| // Updates |scheduled_wakeup_time_| to follow. |
| void MaybeUpdateWakeupTimer(const base::TimeTicks& wakeup_time); |
| |
| void SetWakeupTimerEvent(const base::TimeTicks& wakeup_time, |
| kevent64_s* timer_event); |
| |
| // Receive right to which an empty Mach message is sent to wake up the pump |
| // in response to ScheduleWork(). |
| mac::ScopedMachReceiveRight wakeup_; |
| // Scratch buffer that is used to receive the message sent to |wakeup_|. |
| mach_msg_empty_rcv_t wakeup_buffer_; |
| |
| // Watch controllers for FDs. IDs are generated by the map and are stored in |
| // the kevent64_s::udata field. |
| IDMap<FdWatchController*, uint64_t> fd_controllers_; |
| |
| // Watch controllers for Mach ports. IDs are the port being watched. |
| IDMap<MachPortWatchController*, mach_port_t> port_controllers_; |
| |
| // The kqueue that drives the pump. |
| ScopedFD kqueue_; |
| |
| // Whether the pump has been Quit() or not. |
| bool keep_running_ = true; |
| |
| // The currently scheduled wakeup, if any. If no wakeup is scheduled, |
| // contains base::TimeTicks::Max(). |
| base::TimeTicks scheduled_wakeup_time_{base::TimeTicks::Max()}; |
| |
| // The number of events scheduled on the |kqueue_|. There is always at least |
| // 1, for the |wakeup_| port. |
| size_t event_count_ = 1; |
| // Buffer used by DoInternalWork() to be notified of triggered events. This |
| // is always at least |event_count_|-sized. |
| std::vector<kevent64_s> events_{event_count_}; |
| |
| WeakPtrFactory<MessagePumpKqueue> weak_factory_; |
| }; |
| |
| } // namespace base |
| |
| #endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_KQUEUE_H_ |