Commit db1d3907 authored by Gabriel Charette's avatar Gabriel Charette Committed by Commit Bot

[base] Migrate MessagePumpGlib to DoSomeWork()

This CL is intended to be a no-op logic wise (aside from merging
DoWork()/DoDelayedWork() into a single call).

As a follow-up, it might be possible to take better advantage of
NextWorkInfo::recent_now but it wasn't trivial to do so in this pump
since the sleep is not invoked directly from Run() but rather implicit
from HandlePrepare(). We could also potentially reuse |recent_now|
between HandlePrepare() and HandleCheck(). For now we stick to invoking
TimeTicks::Now() as much as the previous impl did.

Bug: 885371
Change-Id: I4f9d6642c44dbad82cb3588cf3f5323ddf9c19ad
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1594695
Commit-Queue: Gabriel Charette <gab@chromium.org>
Commit-Queue: Mark Mentovai <mark@chromium.org>
Auto-Submit: Gabriel Charette <gab@chromium.org>
Reviewed-by: default avatarMark Mentovai <mark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#656415}
parent 1df5ebfe
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/lazy_instance.h" #include "base/lazy_instance.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/posix/eintr_wrapper.h" #include "base/posix/eintr_wrapper.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "base/threading/platform_thread.h" #include "base/threading/platform_thread.h"
...@@ -19,20 +20,19 @@ namespace base { ...@@ -19,20 +20,19 @@ namespace base {
namespace { namespace {
// Return a timeout suitable for the glib loop, -1 to block forever, // Return a timeout suitable for the glib loop according to |next_task_time|, -1
// 0 to return right away, or a timeout in milliseconds from now. // to block forever, 0 to return right away, or a timeout in milliseconds from
int GetTimeIntervalMilliseconds(const TimeTicks& from) { // now.
if (from.is_null()) int GetTimeIntervalMilliseconds(TimeTicks next_task_time) {
if (next_task_time.is_null())
return 0;
else if (next_task_time.is_max())
return -1; return -1;
// Be careful here. TimeDelta has a precision of microseconds, but we want a auto timeout_ms =
// value in milliseconds. If there are 5.5ms left, should the delay be 5 or (next_task_time - TimeTicks::Now()).InMillisecondsRoundedUp();
// 6? It should be 6 to avoid executing delayed work too early.
int delay = static_cast<int>(
ceil((from - TimeTicks::Now()).InMillisecondsF()));
// If this value is negative, then we need to run delayed work soon. return timeout_ms < 0 ? 0 : saturated_cast<int>(timeout_ms);
return delay < 0 ? 0 : delay;
} }
// A brief refresher on GLib: // A brief refresher on GLib:
...@@ -68,8 +68,8 @@ int GetTimeIntervalMilliseconds(const TimeTicks& from) { ...@@ -68,8 +68,8 @@ int GetTimeIntervalMilliseconds(const TimeTicks& from) {
// - Return true if any of prepare() or check() returned true. // - Return true if any of prepare() or check() returned true.
// //
// gtk_main_iteration just calls g_main_context_iteration, which does the whole // gtk_main_iteration just calls g_main_context_iteration, which does the whole
// thing, respecting the timeout for the poll (and block, although it is // thing, respecting the timeout for the poll (and block, although it is to if
// expected not to if gtk_events_pending returned true), and call dispatch. // gtk_events_pending returned true), and call dispatch.
// //
// Thus it is important to only return true from prepare or check if we // Thus it is important to only return true from prepare or check if we
// actually have events or work to do. We also need to make sure we keep // actually have events or work to do. We also need to make sure we keep
...@@ -79,10 +79,9 @@ int GetTimeIntervalMilliseconds(const TimeTicks& from) { ...@@ -79,10 +79,9 @@ int GetTimeIntervalMilliseconds(const TimeTicks& from) {
// //
// For the GLib pump we try to follow the Windows UI pump model: // For the GLib pump we try to follow the Windows UI pump model:
// - Whenever we receive a wakeup event or the timer for delayed work expires, // - Whenever we receive a wakeup event or the timer for delayed work expires,
// we run DoWork and/or DoDelayedWork. That part will also run in the other // we run DoSomeWork. That part will also run in the other event pumps.
// event pumps. // - We also run DoSomeWork, and possibly DoIdleWork, in the main loop,
// - We also run DoWork, DoDelayedWork, and possibly DoIdleWork in the main // around event handling.
// loop, around event handling.
struct WorkSource : public GSource { struct WorkSource : public GSource {
MessagePumpGlib* pump; MessagePumpGlib* pump;
...@@ -169,10 +168,10 @@ struct MessagePumpGlib::RunState { ...@@ -169,10 +168,10 @@ struct MessagePumpGlib::RunState {
// Used to count how many Run() invocations are on the stack. // Used to count how many Run() invocations are on the stack.
int run_depth; int run_depth;
// This keeps the state of whether the pump got signaled that there was new // The information of the next task available at this run-level. Stored in
// work to be done. Since we eat the message on the wake up pipe as soon as // RunState because different set of tasks can be accessible at various
// we get it, we keep that state here to stay consistent. // run-levels (e.g. non-nestable tasks).
bool has_work; Delegate::NextWorkInfo next_work_info;
}; };
MessagePumpGlib::MessagePumpGlib() MessagePumpGlib::MessagePumpGlib()
...@@ -212,15 +211,11 @@ MessagePumpGlib::~MessagePumpGlib() { ...@@ -212,15 +211,11 @@ MessagePumpGlib::~MessagePumpGlib() {
// Return the timeout we want passed to poll. // Return the timeout we want passed to poll.
int MessagePumpGlib::HandlePrepare() { int MessagePumpGlib::HandlePrepare() {
// We know we have work, but we haven't called HandleDispatch yet. Don't let // |state_| may be null during tests.
// the pump block so that we can do some processing. if (!state_)
if (state_ && // state_ may be null during tests.
state_->has_work)
return 0; return 0;
// We don't think we have work to do, but make sure not to block return GetTimeIntervalMilliseconds(state_->next_work_info.delayed_run_time);
// longer than the next time we need to run delayed work.
return GetTimeIntervalMilliseconds(delayed_work_time_);
} }
bool MessagePumpGlib::HandleCheck() { bool MessagePumpGlib::HandleCheck() {
...@@ -240,18 +235,17 @@ bool MessagePumpGlib::HandleCheck() { ...@@ -240,18 +235,17 @@ bool MessagePumpGlib::HandleCheck() {
} }
DCHECK((num_bytes == 1 && msg[0] == '!') || DCHECK((num_bytes == 1 && msg[0] == '!') ||
(num_bytes == 2 && msg[0] == '!' && msg[1] == '!')); (num_bytes == 2 && msg[0] == '!' && msg[1] == '!'));
// Since we ate the message, we need to record that we have more work, // Since we ate the message, we need to record that we have immediate work,
// because HandleCheck() may be called without HandleDispatch being called // because HandleCheck() may be called without HandleDispatch being called
// afterwards. // afterwards.
state_->has_work = true; state_->next_work_info = {TimeTicks()};
}
if (state_->has_work)
return true; return true;
}
if (GetTimeIntervalMilliseconds(delayed_work_time_) == 0) { // As described in the summary at the top : Check is a second-chance to
// The timer has expired. That condition will stay true until we process // Prepare, verify whether we have work ready again.
// that delayed work, so we don't need to record this differently. if (GetTimeIntervalMilliseconds(state_->next_work_info.delayed_run_time) ==
0) {
return true; return true;
} }
...@@ -259,19 +253,7 @@ bool MessagePumpGlib::HandleCheck() { ...@@ -259,19 +253,7 @@ bool MessagePumpGlib::HandleCheck() {
} }
void MessagePumpGlib::HandleDispatch() { void MessagePumpGlib::HandleDispatch() {
state_->has_work = false; state_->next_work_info = state_->delegate->DoSomeWork();
if (state_->delegate->DoWork()) {
// NOTE: on Windows at this point we would call ScheduleWork (see
// MessagePumpGlib::HandleWorkMessage in message_pump_win.cc). But here,
// instead of posting a message on the wakeup pipe, we can avoid the
// syscalls and just signal that we have more work.
state_->has_work = true;
}
if (state_->should_quit)
return;
state_->delegate->DoDelayedWork(&delayed_work_time_);
} }
void MessagePumpGlib::Run(Delegate* delegate) { void MessagePumpGlib::Run(Delegate* delegate) {
...@@ -283,7 +265,6 @@ void MessagePumpGlib::Run(Delegate* delegate) { ...@@ -283,7 +265,6 @@ void MessagePumpGlib::Run(Delegate* delegate) {
state.delegate = delegate; state.delegate = delegate;
state.should_quit = false; state.should_quit = false;
state.run_depth = state_ ? state_->run_depth + 1 : 1; state.run_depth = state_ ? state_->run_depth + 1 : 1;
state.has_work = false;
RunState* previous_state = state_; RunState* previous_state = state_;
state_ = &state; state_ = &state;
...@@ -306,12 +287,8 @@ void MessagePumpGlib::Run(Delegate* delegate) { ...@@ -306,12 +287,8 @@ void MessagePumpGlib::Run(Delegate* delegate) {
if (state_->should_quit) if (state_->should_quit)
break; break;
more_work_is_plausible |= state_->delegate->DoWork(); state_->next_work_info = state_->delegate->DoSomeWork();
if (state_->should_quit) more_work_is_plausible |= state_->next_work_info.is_immediate();
break;
more_work_is_plausible |=
state_->delegate->DoDelayedWork(&delayed_work_time_);
if (state_->should_quit) if (state_->should_quit)
break; break;
...@@ -347,7 +324,6 @@ void MessagePumpGlib::ScheduleWork() { ...@@ -347,7 +324,6 @@ void MessagePumpGlib::ScheduleWork() {
void MessagePumpGlib::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { void MessagePumpGlib::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
// We need to wake up the loop in case the poll timeout needs to be // We need to wake up the loop in case the poll timeout needs to be
// adjusted. This will cause us to try to do work, but that's OK. // adjusted. This will cause us to try to do work, but that's OK.
delayed_work_time_ = delayed_work_time;
ScheduleWork(); ScheduleWork();
} }
......
...@@ -56,9 +56,6 @@ class BASE_EXPORT MessagePumpGlib : public MessagePump { ...@@ -56,9 +56,6 @@ class BASE_EXPORT MessagePumpGlib : public MessagePump {
// dispatched. // dispatched.
GMainContext* context_; GMainContext* context_;
// This is the time when we need to do delayed work.
TimeTicks delayed_work_time_;
// The work source. It is shared by all calls to Run and destroyed when // The work source. It is shared by all calls to Run and destroyed when
// the message pump is destroyed. // the message pump is destroyed.
GSource* work_source_; GSource* work_source_;
......
...@@ -44,7 +44,7 @@ bool PumpTypeUsesDoSomeWork(MessageLoop::Type type) { ...@@ -44,7 +44,7 @@ bool PumpTypeUsesDoSomeWork(MessageLoop::Type type) {
// iOS uses a MessagePumpDefault for UI in unit tests, ref. // iOS uses a MessagePumpDefault for UI in unit tests, ref.
// test_support_ios.mm::CreateMessagePumpForUIForTests(). // test_support_ios.mm::CreateMessagePumpForUIForTests().
return true; return true;
#elif defined(OS_WIN) || defined(OS_ANDROID) #elif defined(OS_WIN) || defined(OS_ANDROID) || defined(USE_GLIB)
return true; return true;
#elif defined(OS_POSIX) && !defined(OS_NACL_SFI) #elif defined(OS_POSIX) && !defined(OS_NACL_SFI)
// MessagePumpLibevent was migrated (ref. message_pump_for_ui.h and // MessagePumpLibevent was migrated (ref. message_pump_for_ui.h and
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment