| // Copyright 2010 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/message_loop/message_pump.h" |
| |
| #include "base/check.h" |
| #include "base/message_loop/io_watcher.h" |
| #include "base/message_loop/message_pump_default.h" |
| #include "base/message_loop/message_pump_for_io.h" |
| #include "base/message_loop/message_pump_for_ui.h" |
| #include "base/notreached.h" |
| #include "base/task/current_thread.h" |
| #include "base/task/task_features.h" |
| #include "base/threading/platform_thread.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| |
| #if BUILDFLAG(IS_APPLE) |
| #include "base/message_loop/message_pump_apple.h" |
| #endif |
| |
| namespace base { |
| |
| namespace { |
| |
| constexpr uint64_t kAlignWakeUpsMask = 1; |
| constexpr uint64_t kLeewayOffset = 1; |
| |
| constexpr uint64_t PackAlignWakeUpsAndLeeway(bool align_wake_ups, |
| TimeDelta leeway) { |
| return (static_cast<uint64_t>(leeway.InMilliseconds()) << kLeewayOffset) | |
| (align_wake_ups ? kAlignWakeUpsMask : 0); |
| } |
| |
| // This stores the current state of |kAlignWakeUps| and leeway. The last bit |
| // represents if |kAlignWakeUps| is enabled, and the other bits represent the |
| // leeway value applied to delayed tasks in milliseconds. An atomic is used here |
| // because the value is queried from multiple threads. |
| std::atomic<uint64_t> g_align_wake_ups_and_leeway = |
| PackAlignWakeUpsAndLeeway(false, kDefaultLeeway); |
| |
| MessagePump::MessagePumpFactory* message_pump_for_ui_factory_ = nullptr; |
| |
| #if BUILDFLAG(IS_POSIX) |
| class MessagePumpForIOFdWatchImpl : public IOWatcher::FdWatch, |
| public MessagePumpForIO::FdWatcher { |
| public: |
| MessagePumpForIOFdWatchImpl(IOWatcher::FdWatcher* fd_watcher, |
| const Location& location) |
| : fd_watcher_(fd_watcher), controller_(location) {} |
| |
| ~MessagePumpForIOFdWatchImpl() override { |
| controller_.StopWatchingFileDescriptor(); |
| } |
| |
| MessagePumpForIO::FdWatchController& controller() { return controller_; } |
| |
| private: |
| // MessagePumpForIO::FdWatcher: |
| void OnFileCanReadWithoutBlocking(int fd) override { |
| fd_watcher_->OnFdReadable(fd); |
| } |
| |
| void OnFileCanWriteWithoutBlocking(int fd) override { |
| fd_watcher_->OnFdWritable(fd); |
| } |
| |
| const raw_ptr<IOWatcher::FdWatcher> fd_watcher_; |
| MessagePumpForIO::FdWatchController controller_; |
| }; |
| #endif |
| |
| class IOWatcherForCurrentIOThread : public IOWatcher { |
| public: |
| IOWatcherForCurrentIOThread() : thread_(CurrentIOThread::Get()) {} |
| |
| // IOWatcher: |
| #if BUILDFLAG(IS_WIN) |
| bool RegisterIOHandlerImpl(HANDLE file, |
| MessagePumpForIO::IOHandler* handler) override { |
| return thread_.RegisterIOHandler(file, handler); |
| } |
| |
| bool RegisterJobObjectImpl(HANDLE job, |
| MessagePumpForIO::IOHandler* handler) override { |
| return thread_.RegisterJobObject(job, handler); |
| } |
| #elif BUILDFLAG(IS_POSIX) |
| std::unique_ptr<FdWatch> WatchFileDescriptorImpl( |
| int fd, |
| FdWatchDuration duration, |
| FdWatchMode mode, |
| FdWatcher& fd_watcher, |
| const Location& location) override { |
| MessagePumpForIO::Mode io_mode; |
| switch (mode) { |
| case FdWatchMode::kRead: |
| io_mode = MessagePumpForIO::WATCH_READ; |
| break; |
| case FdWatchMode::kWrite: |
| io_mode = MessagePumpForIO::WATCH_WRITE; |
| break; |
| case FdWatchMode::kReadWrite: |
| io_mode = MessagePumpForIO::WATCH_READ_WRITE; |
| break; |
| } |
| const bool is_persistent = duration == FdWatchDuration::kPersistent; |
| auto watch = |
| std::make_unique<MessagePumpForIOFdWatchImpl>(&fd_watcher, location); |
| if (!thread_.WatchFileDescriptor(fd, is_persistent, io_mode, |
| &watch->controller(), watch.get())) { |
| return nullptr; |
| } |
| return watch; |
| } |
| #endif |
| #if BUILDFLAG(IS_MAC) || \ |
| (BUILDFLAG(IS_IOS) && !BUILDFLAG(CRONET_BUILD) && !BUILDFLAG(IS_IOS_TVOS)) |
| bool WatchMachReceivePortImpl( |
| mach_port_t port, |
| MessagePumpForIO::MachPortWatchController* controller, |
| MessagePumpForIO::MachPortWatcher* delegate) override { |
| return thread_.WatchMachReceivePort(port, controller, delegate); |
| } |
| #elif BUILDFLAG(IS_FUCHSIA) |
| bool WatchZxHandleImpl(zx_handle_t handle, |
| bool persistent, |
| zx_signals_t signals, |
| MessagePumpForIO::ZxHandleWatchController* controller, |
| MessagePumpForIO::ZxHandleWatcher* delegate) override { |
| return thread_.WatchZxHandle(handle, persistent, signals, controller, |
| delegate); |
| } |
| #endif // BUILDFLAG(IS_FUCHSIA) |
| |
| private: |
| CurrentIOThread thread_; |
| }; |
| |
| } // namespace |
| |
| MessagePump::MessagePump() = default; |
| |
| MessagePump::~MessagePump() = default; |
| |
| bool MessagePump::HandleNestedNativeLoopWithApplicationTasks( |
| bool application_tasks_desired) { |
| return false; |
| } |
| |
| // static |
| void MessagePump::OverrideMessagePumpForUIFactory(MessagePumpFactory* factory) { |
| DCHECK(!message_pump_for_ui_factory_); |
| message_pump_for_ui_factory_ = factory; |
| } |
| |
| // static |
| bool MessagePump::IsMessagePumpForUIFactoryOveridden() { |
| return message_pump_for_ui_factory_ != nullptr; |
| } |
| |
| // static |
| std::unique_ptr<MessagePump> MessagePump::Create(MessagePumpType type) { |
| switch (type) { |
| case MessagePumpType::UI: |
| if (message_pump_for_ui_factory_) { |
| return message_pump_for_ui_factory_(); |
| } |
| #if BUILDFLAG(IS_APPLE) |
| return message_pump_apple::Create(); |
| #elif BUILDFLAG(IS_AIX) |
| // Currently AIX doesn't have a UI MessagePump. |
| NOTREACHED(); |
| #elif BUILDFLAG(IS_ANDROID) |
| { |
| auto message_pump = std::make_unique<MessagePumpAndroid>(); |
| message_pump->set_is_type_ui(true); |
| return message_pump; |
| } |
| #else |
| return std::make_unique<MessagePumpForUI>(); |
| #endif |
| |
| case MessagePumpType::IO: |
| return std::make_unique<MessagePumpForIO>(); |
| |
| #if BUILDFLAG(IS_ANDROID) |
| case MessagePumpType::JAVA: |
| return std::make_unique<MessagePumpAndroid>(); |
| #endif |
| |
| #if BUILDFLAG(IS_APPLE) |
| case MessagePumpType::NS_RUNLOOP: |
| return std::make_unique<MessagePumpNSRunLoop>(); |
| #endif |
| |
| case MessagePumpType::CUSTOM: |
| NOTREACHED(); |
| |
| case MessagePumpType::DEFAULT: |
| #if BUILDFLAG(IS_IOS) |
| // On iOS, a native runloop is always required to pump system work. |
| return std::make_unique<MessagePumpCFRunLoop>(); |
| #else |
| return std::make_unique<MessagePumpDefault>(); |
| #endif |
| } |
| } |
| |
| // static |
| void MessagePump::InitializeFeatures() { |
| ResetAlignWakeUpsState(); |
| #if BUILDFLAG(IS_WIN) |
| MessagePumpWin::InitializeFeatures(); |
| #elif BUILDFLAG(IS_ANDROID) |
| MessagePumpAndroid::InitializeFeatures(); |
| #endif |
| } |
| |
| // static |
| void MessagePump::OverrideAlignWakeUpsState(bool enabled, TimeDelta leeway) { |
| g_align_wake_ups_and_leeway.store(PackAlignWakeUpsAndLeeway(enabled, leeway), |
| std::memory_order_relaxed); |
| } |
| |
| // static |
| void MessagePump::ResetAlignWakeUpsState() { |
| OverrideAlignWakeUpsState(FeatureList::IsEnabled(kAlignWakeUps), |
| kTaskLeewayParam.Get()); |
| } |
| |
| // static |
| bool MessagePump::GetAlignWakeUpsEnabled() { |
| return g_align_wake_ups_and_leeway.load(std::memory_order_relaxed) & |
| kAlignWakeUpsMask; |
| } |
| |
| // static |
| TimeDelta MessagePump::GetLeewayIgnoringThreadOverride() { |
| return Milliseconds( |
| g_align_wake_ups_and_leeway.load(std::memory_order_relaxed) >> |
| kLeewayOffset); |
| } |
| |
| // static |
| TimeDelta MessagePump::GetLeewayForCurrentThread() { |
| // For some threads, there might be an override of the leeway, so check it |
| // first. |
| auto leeway_override = PlatformThread::GetThreadLeewayOverride(); |
| if (leeway_override.has_value()) { |
| return leeway_override.value(); |
| } |
| return GetLeewayIgnoringThreadOverride(); |
| } |
| |
| TimeTicks MessagePump::AdjustDelayedRunTime(TimeTicks earliest_time, |
| TimeTicks run_time, |
| TimeTicks latest_time) { |
| const TimeDelta leeway = GetLeewayForCurrentThread(); |
| |
| #if BUILDFLAG(IS_WIN) |
| // On Windows, we can rely on the low-res clock if we want the wakeup within |
| // kMinLowResolutionThresholdMs (16ms). |
| if (GetAlignWakeUpsEnabled() && |
| leeway > Milliseconds(Time::kMinLowResolutionThresholdMs)) { |
| TimeTicks aligned_run_time = |
| earliest_time.SnappedToNextTick(TimeTicks(), leeway); |
| return std::min(aligned_run_time, latest_time); |
| } |
| // We need to return `earliest_time` to honor the above dependency on the |
| // low-res clock. Note: If this wakeup has a DelayPolicy::kPrecise, then |
| // `earliest_time == run_time` and we're thus fine returning `earliest_time` |
| // even though `run_time` is semantically what we want... |
| return earliest_time; |
| #else |
| if (GetAlignWakeUpsEnabled()) { |
| TimeTicks aligned_run_time = |
| earliest_time.SnappedToNextTick(TimeTicks(), leeway); |
| return std::min(aligned_run_time, latest_time); |
| } |
| return run_time; |
| #endif // BUILDFLAG(IS_WIN) |
| } |
| |
| IOWatcher* MessagePump::GetIOWatcher() { |
| // By default only "IO thread" message pumps support async IO. |
| // |
| // TODO(crbug.com/379190028): This is done for convenience given the |
| // preexistence of CurrentIOThread, but we should eventually remove this in |
| // favor of each IO MessagePump implementation defining their own override. |
| if (!io_watcher_ && CurrentIOThread::IsSet()) { |
| io_watcher_ = std::make_unique<IOWatcherForCurrentIOThread>(); |
| } |
| return io_watcher_.get(); |
| } |
| |
| } // namespace base |