// Copyright (c) 2012 The Chromium Authors. All rights reserved. // 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_LIBEVENT_H_ #define BASE_MESSAGE_LOOP_MESSAGE_PUMP_LIBEVENT_H_ #include #include #include "base/base_export.h" #include "base/compiler_specific.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_pump.h" #include "base/message_loop/message_pump_buildflags.h" #include "base/message_loop/watchable_io_message_pump_posix.h" #include "base/threading/thread_checker.h" #include "third_party/libevent/event.h" // Declare structs we need from libevent.h rather than including it struct event_base; struct event; namespace base { class MessagePumpEpoll; // Class to monitor sockets and issue callbacks when sockets are ready for I/O // TODO(dkegel): add support for background file IO somehow class BASE_EXPORT MessagePumpLibevent : public MessagePump, public WatchableIOMessagePumpPosix { public: class FdWatchController; // Parameters used to construct and describe an EpollInterest. struct EpollInterestParams { // The file descriptor of interest. int fd; // Indicates an interest in being able to read() from `fd`. bool read; // Indicates an interest in being able to write() to `fd`. bool write; // Indicates whether this interest is a one-shot interest, meaning that it // must be automatically deactivated every time it triggers an epoll event. bool one_shot; bool IsEqual(const EpollInterestParams& rhs) const { return std::tie(fd, read, write, one_shot) == std::tie(rhs.fd, rhs.read, rhs.write, rhs.one_shot); } }; // Represents a single controller's interest in a file descriptor via epoll, // and tracks whether that interest is currently active. Though an interest // persists as long as its controller is alive and hasn't changed interests, // it only participates in epoll waits while active. These objects are only // used when MessagePumpLibevent is configured to use the epoll API instead of // libevent. class EpollInterest : public RefCounted { public: EpollInterest(FdWatchController* controller, const EpollInterestParams& params); EpollInterest(const EpollInterest&) = delete; EpollInterest& operator=(const EpollInterest&) = delete; FdWatchController* controller() { return controller_; } const EpollInterestParams& params() const { return params_; } bool active() const { return active_; } void set_active(bool active) { active_ = active; } private: friend class RefCounted; ~EpollInterest(); FdWatchController* const controller_; const EpollInterestParams params_; bool active_ = true; }; // Note that this class is used as the FdWatchController for both // MessagePumpLibevent *and* MessagePumpEpoll in order to avoid unnecessary // code churn during experimentation and eventual transition. Consumers // construct their own FdWatchController instances, so switching this type // at runtime would require potentially complex logic changes to all // consumers. class FdWatchController : public FdWatchControllerInterface { public: explicit FdWatchController(const Location& from_here); FdWatchController(const FdWatchController&) = delete; FdWatchController& operator=(const FdWatchController&) = delete; // Implicitly calls StopWatchingFileDescriptor. ~FdWatchController() override; // FdWatchControllerInterface: bool StopWatchingFileDescriptor() override; private: friend class MessagePumpEpoll; friend class MessagePumpLibevent; friend class MessagePumpLibeventTest; // Common methods called by both pump implementations. void set_watcher(FdWatcher* watcher) { watcher_ = watcher; } // Methods called only by MessagePumpLibevent void set_libevent_pump(MessagePumpLibevent* pump) { libevent_pump_ = pump; } MessagePumpLibevent* libevent_pump() const { return libevent_pump_; } void Init(std::unique_ptr e); std::unique_ptr ReleaseEvent(); void OnFileCanReadWithoutBlocking(int fd, MessagePumpLibevent* pump); void OnFileCanWriteWithoutBlocking(int fd, MessagePumpLibevent* pump); // Methods called only by MessagePumpEpoll void set_epoll_pump(WeakPtr pump) { epoll_pump_ = std::move(pump); } const scoped_refptr& epoll_interest() const { return epoll_interest_; } // Creates a new Interest described by `params` and adopts it as this // controller's exclusive interest. Any prior interest is dropped by the // controller and should be unregistered on the MessagePumpEpoll. const scoped_refptr& AssignEpollInterest( const EpollInterestParams& params); void OnFdReadable(); void OnFdWritable(); // Common state raw_ptr watcher_ = nullptr; // If this pointer is non-null when the FdWatchController is destroyed, the // pointee is set to true. raw_ptr was_destroyed_ = nullptr; // State used only with libevent std::unique_ptr event_; raw_ptr libevent_pump_ = nullptr; // State used only with epoll WeakPtr epoll_pump_; scoped_refptr epoll_interest_; }; MessagePumpLibevent(); #if BUILDFLAG(ENABLE_MESSAGE_PUMP_EPOLL) // Constructs a MessagePumpLibevent which is forced to use epoll directly // instead of libevent. enum { kUseEpoll }; explicit MessagePumpLibevent(decltype(kUseEpoll)); #endif MessagePumpLibevent(const MessagePumpLibevent&) = delete; MessagePumpLibevent& operator=(const MessagePumpLibevent&) = delete; ~MessagePumpLibevent() override; // Must be called early in process startup, but after FeatureList // initialization. This allows MessagePumpLibevent to query and cache the // enabled state of any relevant features. static void InitializeFeatures(); bool WatchFileDescriptor(int fd, bool persistent, int mode, FdWatchController* controller, FdWatcher* delegate); // MessagePump methods: void Run(Delegate* delegate) override; void Quit() override; void ScheduleWork() override; void ScheduleDelayedWork( const Delegate::NextWorkInfo& next_work_info) override; private: friend class MessagePumpLibeventTest; // Risky part of constructor. Returns true on success. bool Init(); // Called by libevent to tell us a registered FD can be read/written to. static void OnLibeventNotification(int fd, short flags, void* context); // Unix pipe used to implement ScheduleWork() // ... callback; called by libevent inside Run() when pipe is ready to read static void OnWakeup(int socket, short flags, void* context); struct RunState { explicit RunState(Delegate* delegate_in) : delegate(delegate_in) {} // `delegate` is not a raw_ptr<...> for performance reasons (based on // analysis of sampling profiler data and tab_search:top100:2020). RAW_PTR_EXCLUSION Delegate* const delegate; // Used to flag that the current Run() invocation should return ASAP. bool should_quit = false; }; #if BUILDFLAG(ENABLE_MESSAGE_PUMP_EPOLL) // If direct use of epoll is enabled, this is the MessagePumpEpoll instance // used. In that case, all libevent state below is ignored and unused. // Otherwise this is null. std::unique_ptr epoll_pump_; #endif // State for the current invocation of Run(). null if not running. RunState* run_state_ = nullptr; // This flag is set if libevent has processed I/O events. bool processed_io_events_ = false; struct EventBaseFree { inline void operator()(event_base* e) const { if (e) event_base_free(e); } }; // Libevent dispatcher. Watches all sockets registered with it, and sends // readiness callbacks when a socket is ready for I/O. std::unique_ptr event_base_{event_base_new()}; // ... write end; ScheduleWork() writes a single byte to it int wakeup_pipe_in_ = -1; // ... read end; OnWakeup reads it and then breaks Run() out of its sleep int wakeup_pipe_out_ = -1; // ... libevent wrapper for read end std::unique_ptr wakeup_event_; ThreadChecker watch_file_descriptor_caller_checker_; }; } // namespace base #endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_LIBEVENT_H_