910e62b5创建于 1月15日历史提交
// Copyright 2006-2008 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_default.h"

#include "base/auto_reset.h"
#include "base/logging.h"
#include "base/synchronization/waitable_event.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"

#if BUILDFLAG(IS_APPLE)
#include <mach/thread_policy.h>

#include "base/apple/mach_logging.h"
#include "base/apple/scoped_mach_port.h"
#include "base/apple/scoped_nsautorelease_pool.h"
#include "base/threading/threading_features.h"
#endif

namespace base {

MessagePumpDefault::MessagePumpDefault()
    : keep_running_(true),
      event_(WaitableEvent::ResetPolicy::AUTOMATIC,
             WaitableEvent::InitialState::NOT_SIGNALED) {
  event_.declare_only_used_while_idle();
}

MessagePumpDefault::~MessagePumpDefault() = default;

void MessagePumpDefault::Run(Delegate* delegate) {
  AutoReset<bool> auto_reset_keep_running(&keep_running_, true);

  for (;;) {
#if BUILDFLAG(IS_APPLE)
    apple::ScopedNSAutoreleasePool autorelease_pool;
#endif

    Delegate::NextWorkInfo next_work_info = delegate->DoWork();
    bool has_more_immediate_work = next_work_info.is_immediate();
    if (!keep_running_) {
      break;
    }

    if (has_more_immediate_work) {
      continue;
    }

    delegate->DoIdleWork();
    if (!keep_running_) {
      break;
    }

    base::TimeTicks before;
    bool may_busy_loop = max_busy_loop_time_.is_positive();
    if (may_busy_loop) {
      before = base::TimeTicks::Now();
    }

    if (next_work_info.delayed_run_time.is_max()) {
      if (ShouldBusyLoop()) {
        bool signaled = BusyWaitOnEvent(before);
        if (!signaled) {
          event_.Wait();
        }
      } else {
        event_.Wait();
      }
    } else {
      // Not handling shorter sleeps to keep the code as simple as possible.
      if (ShouldBusyLoop() &&
          next_work_info.remaining_delay() > max_busy_loop_time_) {
        bool signaled = BusyWaitOnEvent(before);
        if (!signaled) {
          next_work_info.recent_now = base::TimeTicks::Now();
          event_.TimedWait(next_work_info.remaining_delay());
        }
      } else {
        event_.TimedWait(next_work_info.remaining_delay());
      }
    }
    if (may_busy_loop) {
      RecordWaitTime(base::TimeTicks::Now() - before);
    }
    // Since event_ is auto-reset, we don't need to do anything special here
    // other than service each delegate method.
  }
}

void MessagePumpDefault::Quit() {
  keep_running_ = false;
}

void MessagePumpDefault::ScheduleWork() {
  // Since this can be called on any thread, we need to ensure that our Run
  // loop wakes up.
  event_.Signal();
}

void MessagePumpDefault::ScheduleDelayedWork(
    const Delegate::NextWorkInfo& next_work_info) {
  // Since this is always called from the same thread as Run(), there is nothing
  // to do as the loop is already running. It will wait in Run() with the
  // correct timeout when it's out of immediate tasks.
  // TODO(gab): Consider removing ScheduleDelayedWork() when all pumps function
  // this way (bit.ly/merge-message-pump-do-work).
}

void MessagePumpDefault::RecordWaitTime(base::TimeDelta wait_time) {
  last_wait_time_ = wait_time;
  constexpr float kAlpha = .9;
  wait_time_exponential_moving_average_ =
      kAlpha * wait_time_exponential_moving_average_ +
      (1. - kAlpha) * wait_time;
}

bool MessagePumpDefault::ShouldBusyLoop() const {
  // Should only busy loop when the expected wait time is short. Of course, we
  // don't know whether it will be, but we have two crude heuristics here:
  // - Last wait was short, maybe the next one will. Not that if this one is
  //   wrong, it only impacts a single wait.
  // - Recent waits were short (burst of small tasks with waiting in-between)
  //
  // The second one is laggy, both to start and to stop, which is why the first
  // one is there too, to start busy looping faster.
  //
  // One important part though is that to avoid wasting too much power, we
  // should not busy wait for regular sleeps, for instance animations updating
  // at 60Hz.
  return max_busy_loop_time_.is_positive() &&
         (last_wait_time_ < max_busy_loop_time_ ||
          wait_time_exponential_moving_average_ < max_busy_loop_time_);
}

bool MessagePumpDefault::BusyWaitOnEvent(base::TimeTicks before) {
  bool signaled = false;
  {
    TRACE_EVENT(TRACE_DISABLED_BY_DEFAULT("base"), "BusyWait",
                "last_wait_time_ms", last_wait_time_.InMillisecondsF(),
                "wait_time_exponential_moving_average_ms",
                wait_time_exponential_moving_average_.InMillisecondsF());
    do {
      signaled = event_.TimedWait(base::TimeDelta());
    } while (!signaled &&
             (base::TimeTicks::Now() - before) < max_busy_loop_time_);
  }
  return signaled;
}

}  // namespace base