diff options
author | kjellander <kjellander@chromium.org> | 2015-06-10 00:53:58 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-06-10 07:54:25 +0000 |
commit | e799035ef225f9238d5aa7bea125d6d0289db3cd (patch) | |
tree | 1b48471a91675dec344ac565c3bfc2ca189131a6 /base/message_loop | |
parent | 6cddaa991266cd12f37625fd390e0d36a93e49c3 (diff) | |
download | chromium_src-e799035ef225f9238d5aa7bea125d6d0289db3cd.zip chromium_src-e799035ef225f9238d5aa7bea125d6d0289db3cd.tar.gz chromium_src-e799035ef225f9238d5aa7bea125d6d0289db3cd.tar.bz2 |
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}
Diffstat (limited to 'base/message_loop')
-rw-r--r-- | base/message_loop/incoming_task_queue.cc | 5 | ||||
-rw-r--r-- | base/message_loop/incoming_task_queue.h | 3 | ||||
-rw-r--r-- | base/message_loop/message_loop.cc | 4 | ||||
-rw-r--r-- | base/message_loop/message_loop.h | 1 | ||||
-rw-r--r-- | base/message_loop/message_pump.h | 3 | ||||
-rw-r--r-- | base/message_loop/message_pump_win.cc | 138 | ||||
-rw-r--r-- | base/message_loop/message_pump_win.h | 31 |
7 files changed, 55 insertions, 130 deletions
diff --git a/base/message_loop/incoming_task_queue.cc b/base/message_loop/incoming_task_queue.cc index 642222e..5e9a461 100644 --- a/base/message_loop/incoming_task_queue.cc +++ b/base/message_loop/incoming_task_queue.cc @@ -119,11 +119,6 @@ void IncomingTaskQueue::StartScheduling() { ScheduleWork(); } -TimeTicks IncomingTaskQueue::GetNewlyAddedTaskDelay() { - return !incoming_queue_.empty() ? incoming_queue_.front().delayed_run_time : - TimeTicks(); -} - IncomingTaskQueue::~IncomingTaskQueue() { // Verify that WillDestroyCurrentMessageLoop() has been called. DCHECK(!message_loop_); diff --git a/base/message_loop/incoming_task_queue.h b/base/message_loop/incoming_task_queue.h index 544f3e9..7dd1e82 100644 --- a/base/message_loop/incoming_task_queue.h +++ b/base/message_loop/incoming_task_queue.h @@ -57,9 +57,6 @@ class BASE_EXPORT IncomingTaskQueue // scheduling work. void StartScheduling(); - // Returns the delay for the most recently added task. - TimeTicks GetNewlyAddedTaskDelay(); - private: friend class RefCountedThreadSafe<IncomingTaskQueue>; virtual ~IncomingTaskQueue(); diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc index b3c895c..4222c77 100644 --- a/base/message_loop/message_loop.cc +++ b/base/message_loop/message_loop.cc @@ -640,10 +640,6 @@ bool MessageLoop::DoIdleWork() { return false; } -TimeTicks MessageLoop::GetNewlyAddedTaskDelay() { - return incoming_task_queue_->GetNewlyAddedTaskDelay(); -} - void MessageLoop::DeleteSoonInternal(const tracked_objects::Location& from_here, void(*deleter)(const void*), const void* object) { diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h index 2d67fe5..f2f89d0 100644 --- a/base/message_loop/message_loop.h +++ b/base/message_loop/message_loop.h @@ -475,7 +475,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { bool DoWork() override; bool DoDelayedWork(TimeTicks* next_delayed_work_time) override; bool DoIdleWork() override; - TimeTicks GetNewlyAddedTaskDelay() override; const Type type_; diff --git a/base/message_loop/message_pump.h b/base/message_loop/message_pump.h index 77a49ce..a2edb45 100644 --- a/base/message_loop/message_pump.h +++ b/base/message_loop/message_pump.h @@ -42,9 +42,6 @@ class BASE_EXPORT MessagePump : public NonThreadSafe { // Returns true to indicate that idle work was done. Returning false means // the pump will now wait. virtual bool DoIdleWork() = 0; - - // Returns the delay for the newly added task. - virtual TimeTicks GetNewlyAddedTaskDelay() = 0; }; MessagePump(); diff --git a/base/message_loop/message_pump_win.cc b/base/message_loop/message_pump_win.cc index 53f79cd..cdbf0c2 100644 --- a/base/message_loop/message_pump_win.cc +++ b/base/message_loop/message_pump_win.cc @@ -12,7 +12,6 @@ #include "base/process/memory.h" #include "base/profiler/scoped_tracker.h" #include "base/strings/stringprintf.h" -#include "base/threading/thread.h" #include "base/trace_event/trace_event.h" #include "base/win/wrapped_window_proc.h" @@ -35,10 +34,6 @@ static const wchar_t kWndClassFormat[] = L"Chrome_MessagePumpWindow_%p"; // task (a series of such messages creates a continuous task pump). 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: @@ -95,38 +90,35 @@ int MessagePumpWin::GetCurrentDelay() const { MessagePumpForUI::MessagePumpForUI() : atom_(0) { 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() { DestroyWindow(message_hwnd_); UnregisterClass(MAKEINTATOM(atom_), GetModuleFromAddress(&WndProcThunk)); - - ::QueueUserAPC( - reinterpret_cast<PAPCFUNC>(&MessagePumpForUI::ShutdownWorkerThread), - ui_worker_thread_->thread_handle().platform_handle(), NULL); - ui_worker_thread_->Stop(); } void MessagePumpForUI::ScheduleWork() { - // If we have a regular posted task at the head of queue then we need to - // process it quickly. - if (state_ && state_->delegate->GetNewlyAddedTaskDelay().is_null()) { - // Make sure the MessagePump does some work for us. - PostWorkMessage(); - return; - } - // Set a one shot timer to fire after 3 milliseconds. The actual resolution - // of the timer is dependent on timeBeginPeriod being called. - SetWakeupTimer(kDefaultUIWorkerThreadWakeupTimerMs); + if (InterlockedExchange(&have_work_, 1)) + return; // Someone else continued the pumping. + + // Make sure the MessagePump does some work for us. + BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork, + reinterpret_cast<WPARAM>(this), 0); + if (ret) + return; // There was room in the Window Message queue. + + // 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) { @@ -417,65 +409,45 @@ bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { } bool MessagePumpForUI::ProcessPumpReplacementMessage() { - // Since we discarded a kMsgHaveWork message, we must update the flag. - InterlockedExchange(&have_work_, 0); - return true; -} - -void MessagePumpForUI::DoWorkerThreadRunLoop() { - DCHECK(ui_worker_thread_timer_.Get()); - while (TRUE) { - DWORD ret = WaitForSingleObjectEx( - ui_worker_thread_timer_.Get(), INFINITE, TRUE); - // The only APC this thread could receive is the Shutdown APC. - if (ret == WAIT_IO_COMPLETION) - return; + // When we encounter a kMsgHaveWork message, this method is called to peek + // and process a replacement message, such as a WM_PAINT or WM_TIMER. The + // 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, + // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to + // possibly be posted), and finally dispatches that peeked replacement. Note + // that the re-post of kMsgHaveWork may be asynchronous to this thread!! + + bool have_message = false; + MSG msg; + // We should not process all window messages if we are in the context of an + // 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. - PostWorkMessage(); + DCHECK(!have_message || kMsgHaveWork != msg.message || + msg.hwnd != message_hwnd_); - // Set a one shot timer to process pending delayed tasks if any in the - // queue. The actual resolution of the timer is dependent on the - // timeBeginPeriod API being called. - SetWakeupTimer(kDefaultUIWorkerThreadWakeupTimerMs); - } -} + // Since we discarded a kMsgHaveWork message, we must update the flag. + int old_have_work = InterlockedExchange(&have_work_, 0); + DCHECK(old_have_work); -// static -void CALLBACK MessagePumpForUI::ShutdownWorkerThread(ULONG_PTR param) { - // This function is empty because we only use the fact that an APC was posted - // 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); - } -} + // We don't need a special time slice if we didn't have_message to process. + if (!have_message) + return false; -void MessagePumpForUI::SetWakeupTimer(int64 delay_ms) { - // Set the timer for the delay passed in. The actual resolution of the - // timer is dependent on whether timeBeginPeriod was called. - LARGE_INTEGER due_time = {0}; - due_time.QuadPart = -delay_ms * 10000; - BOOL timer_set = ::SetWaitableTimer(ui_worker_thread_timer_.Get(), - &due_time, 0, NULL, NULL, FALSE); - CHECK(timer_set); + // Guarantee we'll get another time slice in the case where we go into native + // windows code. This ScheduleWork() may hurt performance a tiny bit when + // tasks appear very infrequently, but when the event queue is busy, the + // kMsgHaveWork events get (percentage wise) rarer and rarer. + ScheduleWork(); + return ProcessMessageHelper(msg); } //----------------------------------------------------------------------------- diff --git a/base/message_loop/message_pump_win.h b/base/message_loop/message_pump_win.h index 042efdf..00a1e77 100644 --- a/base/message_loop/message_pump_win.h +++ b/base/message_loop/message_pump_win.h @@ -11,7 +11,6 @@ #include "base/base_export.h" #include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" #include "base/message_loop/message_pump.h" #include "base/message_loop/message_pump_dispatcher.h" #include "base/observer_list.h" @@ -20,8 +19,6 @@ namespace base { -class Thread; - // MessagePumpWin serves as the base for specialized versions of the MessagePump // for Windows. It provides basic functionality like handling of observers and // controlling the lifetime of the message pump. @@ -138,39 +135,11 @@ class BASE_EXPORT MessagePumpForUI : public MessagePumpWin { bool ProcessMessageHelper(const MSG& msg); 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 atom_; // A hidden message-only window. 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_; }; //----------------------------------------------------------------------------- |