Commit e799035e authored by kjellander's avatar kjellander Committed by Commit bot

Revert of Don't peek messages in the MessagePumpForUI class when we receive...

Revert of Don't peek messages in the MessagePumpForUI class when we receive our kMsgHaveWork message. (patchset #13 id:240001 of https://codereview.chromium.org/1156503005/)

Reason for revert:
I suspect this CL breaks Windows 8 content_unittests, as it's listed both in
https://build.chromium.org/p/chromium.fyi/builders/Win8%20Tests%20%281%29/builds/8675 and our WebRTC-specific waterfall: https://build.chromium.org/p/chromium.webrtc/builders/Win8%20Tester/builds/14164

I'll try to reland if it turns out not to solve the breakage.

Original issue's description:
> Don't peek messages in the MessagePumpForUI class when we receive our kMsgHaveWork message.
>
> Currently the MessagePumpForUI class peeks Windows messages when we receive the kMsgHaveWork message in
> our main message loop and in nested modal loops. This is because the posted message starves the message loop
> of input and other lower priority messages. While this is ok for the main message loop our peeking and dispatching
> messages in nested loops is wrong and violates the silent contract where in the nested loop should be the one peeking
> and dispatching messages.
>
> To fix this the approach we are taking is to create a worker thread which uses a waitable timer of 3 ms which posts
> the kMsgHaveWork message to the main loop when the timer fires. As a result we can safely delete all the code
> in the MessagePumpForUI::ScheduleWork function and simplify the ProcessPumpReplacementMessage function.
>
> The MessagePumpForUI::ScheduleWork function now checks the delay for the task at the head of the queue
> If it is 0 then we post the message right away as it is a regular task. Added functions (GetNewlyAddedTaskDelay) to the MessagePump::Delegate class and the IncomingTaskQueue for this.
>
> The other change here is to change the GPU process to use the IO message pump by default and use the UI pump only
> if we are using opengl. Reason being this patch causes a delay in processing tasks due to the worker thread which
> causes tests like webgl_conformance to fail. We will continue working on addressing this over the coming days.
>
> BUG=492837
> R=cpu
>
> Committed: https://crrev.com/b8e126c8b532b1327f38afe2bdf59aa5ff933971
> Cr-Commit-Position: refs/heads/master@{#333572}

TBR=cpu@chromium.org,jar@chromium.org,kbr@chromium.org,geofflang@chromium.org,scottmg@chromium.org,ananta@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=492837

Review URL: https://codereview.chromium.org/1163423006

Cr-Commit-Position: refs/heads/master@{#333700}
parent 6cddaa99
...@@ -119,11 +119,6 @@ void IncomingTaskQueue::StartScheduling() { ...@@ -119,11 +119,6 @@ void IncomingTaskQueue::StartScheduling() {
ScheduleWork(); ScheduleWork();
} }
TimeTicks IncomingTaskQueue::GetNewlyAddedTaskDelay() {
return !incoming_queue_.empty() ? incoming_queue_.front().delayed_run_time :
TimeTicks();
}
IncomingTaskQueue::~IncomingTaskQueue() { IncomingTaskQueue::~IncomingTaskQueue() {
// Verify that WillDestroyCurrentMessageLoop() has been called. // Verify that WillDestroyCurrentMessageLoop() has been called.
DCHECK(!message_loop_); DCHECK(!message_loop_);
......
...@@ -57,9 +57,6 @@ class BASE_EXPORT IncomingTaskQueue ...@@ -57,9 +57,6 @@ class BASE_EXPORT IncomingTaskQueue
// scheduling work. // scheduling work.
void StartScheduling(); void StartScheduling();
// Returns the delay for the most recently added task.
TimeTicks GetNewlyAddedTaskDelay();
private: private:
friend class RefCountedThreadSafe<IncomingTaskQueue>; friend class RefCountedThreadSafe<IncomingTaskQueue>;
virtual ~IncomingTaskQueue(); virtual ~IncomingTaskQueue();
......
...@@ -640,10 +640,6 @@ bool MessageLoop::DoIdleWork() { ...@@ -640,10 +640,6 @@ bool MessageLoop::DoIdleWork() {
return false; return false;
} }
TimeTicks MessageLoop::GetNewlyAddedTaskDelay() {
return incoming_task_queue_->GetNewlyAddedTaskDelay();
}
void MessageLoop::DeleteSoonInternal(const tracked_objects::Location& from_here, void MessageLoop::DeleteSoonInternal(const tracked_objects::Location& from_here,
void(*deleter)(const void*), void(*deleter)(const void*),
const void* object) { const void* object) {
......
...@@ -475,7 +475,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { ...@@ -475,7 +475,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
bool DoWork() override; bool DoWork() override;
bool DoDelayedWork(TimeTicks* next_delayed_work_time) override; bool DoDelayedWork(TimeTicks* next_delayed_work_time) override;
bool DoIdleWork() override; bool DoIdleWork() override;
TimeTicks GetNewlyAddedTaskDelay() override;
const Type type_; const Type type_;
......
...@@ -42,9 +42,6 @@ class BASE_EXPORT MessagePump : public NonThreadSafe { ...@@ -42,9 +42,6 @@ class BASE_EXPORT MessagePump : public NonThreadSafe {
// Returns true to indicate that idle work was done. Returning false means // Returns true to indicate that idle work was done. Returning false means
// the pump will now wait. // the pump will now wait.
virtual bool DoIdleWork() = 0; virtual bool DoIdleWork() = 0;
// Returns the delay for the newly added task.
virtual TimeTicks GetNewlyAddedTaskDelay() = 0;
}; };
MessagePump(); MessagePump();
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#include "base/process/memory.h" #include "base/process/memory.h"
#include "base/profiler/scoped_tracker.h" #include "base/profiler/scoped_tracker.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/threading/thread.h"
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
#include "base/win/wrapped_window_proc.h" #include "base/win/wrapped_window_proc.h"
...@@ -35,10 +34,6 @@ static const wchar_t kWndClassFormat[] = L"Chrome_MessagePumpWindow_%p"; ...@@ -35,10 +34,6 @@ static const wchar_t kWndClassFormat[] = L"Chrome_MessagePumpWindow_%p";
// task (a series of such messages creates a continuous task pump). // task (a series of such messages creates a continuous task pump).
static const int kMsgHaveWork = WM_USER + 1; static const int kMsgHaveWork = WM_USER + 1;
// The default delay for the waitable timer used to wake up the UI worker
// thread.
static const int64 kDefaultUIWorkerThreadWakeupTimerMs = 3;
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// MessagePumpWin public: // MessagePumpWin public:
...@@ -95,38 +90,35 @@ int MessagePumpWin::GetCurrentDelay() const { ...@@ -95,38 +90,35 @@ int MessagePumpWin::GetCurrentDelay() const {
MessagePumpForUI::MessagePumpForUI() MessagePumpForUI::MessagePumpForUI()
: atom_(0) { : atom_(0) {
InitMessageWnd(); InitMessageWnd();
ui_worker_thread_timer_.Set(::CreateWaitableTimer(NULL, FALSE, NULL));
ui_worker_thread_.reset(new base::Thread("UI Pump Worker thread"));
ui_worker_thread_->Start();
ui_worker_thread_->task_runner()->PostTask(
FROM_HERE,
base::Bind(&MessagePumpForUI::DoWorkerThreadRunLoop,
base::Unretained(this)));
} }
MessagePumpForUI::~MessagePumpForUI() { MessagePumpForUI::~MessagePumpForUI() {
DestroyWindow(message_hwnd_); DestroyWindow(message_hwnd_);
UnregisterClass(MAKEINTATOM(atom_), UnregisterClass(MAKEINTATOM(atom_),
GetModuleFromAddress(&WndProcThunk)); GetModuleFromAddress(&WndProcThunk));
::QueueUserAPC(
reinterpret_cast<PAPCFUNC>(&MessagePumpForUI::ShutdownWorkerThread),
ui_worker_thread_->thread_handle().platform_handle(), NULL);
ui_worker_thread_->Stop();
} }
void MessagePumpForUI::ScheduleWork() { void MessagePumpForUI::ScheduleWork() {
// If we have a regular posted task at the head of queue then we need to if (InterlockedExchange(&have_work_, 1))
// process it quickly. return; // Someone else continued the pumping.
if (state_ && state_->delegate->GetNewlyAddedTaskDelay().is_null()) {
// Make sure the MessagePump does some work for us. // Make sure the MessagePump does some work for us.
PostWorkMessage(); BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork,
return; reinterpret_cast<WPARAM>(this), 0);
} if (ret)
// Set a one shot timer to fire after 3 milliseconds. The actual resolution return; // There was room in the Window Message queue.
// of the timer is dependent on timeBeginPeriod being called.
SetWakeupTimer(kDefaultUIWorkerThreadWakeupTimerMs); // We have failed to insert a have-work message, so there is a chance that we
// will starve tasks/timers while sitting in a nested message loop. Nested
// loops only look at Windows Message queues, and don't look at *our* task
// queues, etc., so we might not get a time slice in such. :-(
// We could abort here, but the fear is that this failure mode is plausibly
// common (queue is full, of about 2000 messages), so we'll do a near-graceful
// recovery. Nested loops are pretty transient (we think), so this will
// probably be recoverable.
InterlockedExchange(&have_work_, 0); // Clarify that we didn't really insert.
UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR,
MESSAGE_LOOP_PROBLEM_MAX);
} }
void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
...@@ -417,65 +409,45 @@ bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { ...@@ -417,65 +409,45 @@ bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) {
} }
bool MessagePumpForUI::ProcessPumpReplacementMessage() { bool MessagePumpForUI::ProcessPumpReplacementMessage() {
// Since we discarded a kMsgHaveWork message, we must update the flag. // When we encounter a kMsgHaveWork message, this method is called to peek
InterlockedExchange(&have_work_, 0); // and process a replacement message, such as a WM_PAINT or WM_TIMER. The
return true; // goal is to make the kMsgHaveWork as non-intrusive as possible, even though
} // a continuous stream of such messages are posted. This method carefully
// peeks a message while there is no chance for a kMsgHaveWork to be pending,
void MessagePumpForUI::DoWorkerThreadRunLoop() { // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to
DCHECK(ui_worker_thread_timer_.Get()); // possibly be posted), and finally dispatches that peeked replacement. Note
while (TRUE) { // that the re-post of kMsgHaveWork may be asynchronous to this thread!!
DWORD ret = WaitForSingleObjectEx(
ui_worker_thread_timer_.Get(), INFINITE, TRUE); bool have_message = false;
// The only APC this thread could receive is the Shutdown APC. MSG msg;
if (ret == WAIT_IO_COMPLETION) // We should not process all window messages if we are in the context of an
return; // OS modal loop, i.e. in the context of a windows API call like MessageBox.
// This is to ensure that these messages are peeked out by the OS modal loop.
if (MessageLoop::current()->os_modal_loop()) {
// We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above.
have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) ||
PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE);
} else {
have_message = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE;
}
// Make sure the MessagePump does some work for us. DCHECK(!have_message || kMsgHaveWork != msg.message ||
PostWorkMessage(); msg.hwnd != message_hwnd_);
// Set a one shot timer to process pending delayed tasks if any in the // Since we discarded a kMsgHaveWork message, we must update the flag.
// queue. The actual resolution of the timer is dependent on the int old_have_work = InterlockedExchange(&have_work_, 0);
// timeBeginPeriod API being called. DCHECK(old_have_work);
SetWakeupTimer(kDefaultUIWorkerThreadWakeupTimerMs);
}
}
// static // We don't need a special time slice if we didn't have_message to process.
void CALLBACK MessagePumpForUI::ShutdownWorkerThread(ULONG_PTR param) { if (!have_message)
// This function is empty because we only use the fact that an APC was posted return false;
// to the worker thread to shut it down.
return;
}
void MessagePumpForUI::PostWorkMessage() {
BOOL posted = PostMessage(message_hwnd_, kMsgHaveWork,
reinterpret_cast<WPARAM>(this),
0);
if (!posted) {
// We have failed to insert a have-work message, so there is a chance
// that we will starve tasks/timers while sitting in a nested message
// loop. Nested loops only look at Windows Message queues, and don't
// look at *our* task queues, etc., so we might not get a time slice in
// such. :-(
// We could abort here, but the fear is that this failure mode is
// plausibly common (queue is full, of about 2000 messages), so we'll
// do a near-graceful recovery. Nested loops are pretty transient
// (we think), so this will probably be recoverable.
UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem",
MESSAGE_POST_ERROR,
MESSAGE_LOOP_PROBLEM_MAX);
}
}
void MessagePumpForUI::SetWakeupTimer(int64 delay_ms) { // Guarantee we'll get another time slice in the case where we go into native
// Set the timer for the delay passed in. The actual resolution of the // windows code. This ScheduleWork() may hurt performance a tiny bit when
// timer is dependent on whether timeBeginPeriod was called. // tasks appear very infrequently, but when the event queue is busy, the
LARGE_INTEGER due_time = {0}; // kMsgHaveWork events get (percentage wise) rarer and rarer.
due_time.QuadPart = -delay_ms * 10000; ScheduleWork();
BOOL timer_set = ::SetWaitableTimer(ui_worker_thread_timer_.Get(), return ProcessMessageHelper(msg);
&due_time, 0, NULL, NULL, FALSE);
CHECK(timer_set);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
......
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
#include "base/base_export.h" #include "base/base_export.h"
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_pump.h" #include "base/message_loop/message_pump.h"
#include "base/message_loop/message_pump_dispatcher.h" #include "base/message_loop/message_pump_dispatcher.h"
#include "base/observer_list.h" #include "base/observer_list.h"
...@@ -20,8 +19,6 @@ ...@@ -20,8 +19,6 @@
namespace base { namespace base {
class Thread;
// MessagePumpWin serves as the base for specialized versions of the MessagePump // MessagePumpWin serves as the base for specialized versions of the MessagePump
// for Windows. It provides basic functionality like handling of observers and // for Windows. It provides basic functionality like handling of observers and
// controlling the lifetime of the message pump. // controlling the lifetime of the message pump.
...@@ -138,39 +135,11 @@ class BASE_EXPORT MessagePumpForUI : public MessagePumpWin { ...@@ -138,39 +135,11 @@ class BASE_EXPORT MessagePumpForUI : public MessagePumpWin {
bool ProcessMessageHelper(const MSG& msg); bool ProcessMessageHelper(const MSG& msg);
bool ProcessPumpReplacementMessage(); bool ProcessPumpReplacementMessage();
// We spawn a worker thread to periodically post (3 ms) the kMsgHaveWork
// message to the UI message pump. This is to ensure that the main thread
// gets to process tasks and delayed tasks when there is no activity in the
// Windows message pump or when there is a nested modal loop (sizing/moving/
// drag drop/message boxes) etc.
void DoWorkerThreadRunLoop();
// This function is posted as part of a user mode APC to shutdown the worker
// thread when the main message pump is shutting down.
static void CALLBACK ShutdownWorkerThread(ULONG_PTR param);
// Helper function for posting the kMsgHaveWork message to wake up the pump
// for processing tasks.
void PostWorkMessage();
// Helper function to set the waitable timer used to wake up the UI worker
// thread for processing delayed tasks.
// |delay_ms| : The delay in milliseconds.
void SetWakeupTimer(int64 delay_ms);
// Atom representing the registered window class. // Atom representing the registered window class.
ATOM atom_; ATOM atom_;
// A hidden message-only window. // A hidden message-only window.
HWND message_hwnd_; HWND message_hwnd_;
// This thread is used to periodically wake up the main thread to process
// tasks.
scoped_ptr<base::Thread> ui_worker_thread_;
// The UI worker thread waits on this timer indefinitely. When the main
// thread has tasks ready to be processed it sets the timer.
base::win::ScopedHandle ui_worker_thread_timer_;
}; };
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
......
...@@ -160,22 +160,9 @@ int GpuMain(const MainFunctionParams& parameters) { ...@@ -160,22 +160,9 @@ int GpuMain(const MainFunctionParams& parameters) {
bool dead_on_arrival = false; bool dead_on_arrival = false;
#if defined(OS_WIN) #if defined(OS_WIN)
base::MessageLoop::Type loop_type = base::MessageLoop::TYPE_IO;
// Use a UI message loop because ANGLE and the desktop GL platform can // Use a UI message loop because ANGLE and the desktop GL platform can
// create child windows to render to. // create child windows to render to.
// TODO(ananta) : Recent changes to the UI message pump class on Windows base::MessageLoop main_message_loop(base::MessageLoop::TYPE_UI);
// will cause delays in tasks getting processed in the GPU process which
// will show up on the bots in webgl conformance tests, perf tests etc.
// It will be great if we can work around the issues with desktop GL and
// avoid creating a child window in the GPU process which requires a UI
// message pump.
if ((command_line.HasSwitch(switches::kUseGL) &&
command_line.GetSwitchValueASCII(switches::kUseGL) == "desktop") ||
(command_line.HasSwitch(switches::kUseANGLE) &&
command_line.GetSwitchValueASCII(switches::kUseANGLE) == "gl")) {
loop_type = base::MessageLoop::TYPE_UI;
}
base::MessageLoop main_message_loop(loop_type);
#elif defined(OS_LINUX) && defined(USE_X11) #elif defined(OS_LINUX) && defined(USE_X11)
// We need a UI loop so that we can grab the Expose events. See GLSurfaceGLX // We need a UI loop so that we can grab the Expose events. See GLSurfaceGLX
// and https://crbug.com/326995. // and https://crbug.com/326995.
......
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