Chromium Code Reviews| Index: base/message_loop/message_pump_win.cc |
| diff --git a/base/message_loop/message_pump_win.cc b/base/message_loop/message_pump_win.cc |
| index 803bb5d4f82e1602cd821f898b36bf734dc8228e..f238c3c3a132643f89277e245b0b122779527da4 100644 |
| --- a/base/message_loop/message_pump_win.cc |
| +++ b/base/message_loop/message_pump_win.cc |
| @@ -80,21 +80,21 @@ int MessagePumpWin::GetCurrentDelay() const { |
| } |
| //----------------------------------------------------------------------------- |
| -// MessagePumpForUI public: |
| +// MessagePumpForUIBase public: |
| -MessagePumpForUI::MessagePumpForUI() |
| +MessagePumpForUIBase::MessagePumpForUIBase() |
| : atom_(0) { |
| InitMessageWnd(); |
|
jbauman
2016/03/18 01:00:50
We could essentially get rid of MessagePumpForUIBa
stanisc
2016/03/18 18:16:46
Thanks for the feedback! I've tried a separate cla
|
| } |
| -MessagePumpForUI::~MessagePumpForUI() { |
| +MessagePumpForUIBase::~MessagePumpForUIBase() { |
| DestroyWindow(message_hwnd_); |
| UnregisterClass(MAKEINTATOM(atom_), |
| GetModuleFromAddress(&WndProcThunk)); |
| } |
| -void MessagePumpForUI::ScheduleWork() { |
| - if (InterlockedExchange(&have_work_, 1)) |
| +void MessagePumpForUIBase::ScheduleWork() { |
| + if (READY != InterlockedExchange(&work_state_, HAVE_WORK)) |
| return; // Someone else continued the pumping. |
| // Make sure the MessagePump does some work for us. |
| @@ -111,33 +111,112 @@ void MessagePumpForUI::ScheduleWork() { |
| // 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. |
| + |
| + // Clarify that we didn't really insert. |
| + InterlockedExchange(&work_state_, READY); |
| UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR, |
| MESSAGE_LOOP_PROBLEM_MAX); |
| } |
| -void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { |
| - delayed_work_time_ = delayed_work_time; |
| - RescheduleTimer(); |
| +//----------------------------------------------------------------------------- |
| +// MessagePumpForUIBase protected: |
| + |
| +void MessagePumpForUIBase::SetTimer(int delay_msec) { |
| + // We would *like* to provide high resolution timers. Windows timers using |
| + // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup |
| + // mechanism because the application can enter modal windows loops where it |
| + // is not running our MessageLoop; the only way to have our timers fire in |
| + // these cases is to post messages there. |
| + // |
| + // To provide sub-10ms timers, we process timers directly from our run loop. |
| + // For the common case, timers will be processed there as the run loop does |
| + // its normal work. However, we *also* set the system timer so that WM_TIMER |
| + // events fire. This mops up the case of timers not being able to work in |
| + // modal message loops. It is possible for the SetTimer to pop and have no |
| + // pending timers, because they could have already been processed by the |
| + // run loop itself. |
| + // |
| + // We use a single SetTimer corresponding to the timer that will expire |
| + // soonest. As new timers are created and destroyed, we update SetTimer. |
| + // Getting a spurious SetTimer event firing is benign, as we'll just be |
| + // processing an empty timer queue. |
| + if (delay_msec < USER_TIMER_MINIMUM) |
| + delay_msec = USER_TIMER_MINIMUM; |
| + |
| + // Create a WM_TIMER event that will wake us up to check for any pending |
| + // timers (in case we are running within a nested, external sub-pump). |
| + BOOL ret = ::SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), |
| + delay_msec, NULL); |
| + if (ret) |
| + return; |
| + // If we can't set timers, we are in big trouble... but cross our fingers |
| + // for now. |
| + // TODO(jar): If we don't see this error, use a CHECK() here instead. |
| + UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", SET_TIMER_ERROR, |
| + MESSAGE_LOOP_PROBLEM_MAX); |
| +} |
| + |
| +void MessagePumpForUIBase::KillTimer() { |
| + ::KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); |
| +} |
| + |
| +bool MessagePumpForUIBase::IsWorkMessage(const MSG& msg) const { |
| + return msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_; |
| } |
| //----------------------------------------------------------------------------- |
| -// MessagePumpForUI private: |
| +// MessagePumpForUIBase private: |
| // static |
| -LRESULT CALLBACK MessagePumpForUI::WndProcThunk( |
| +LRESULT CALLBACK MessagePumpForUIBase::WndProcThunk( |
| HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { |
| switch (message) { |
| case kMsgHaveWork: |
| - reinterpret_cast<MessagePumpForUI*>(wparam)->HandleWorkMessage(); |
| + reinterpret_cast<MessagePumpForUIBase*>(wparam)->HandleWorkMessage(); |
| break; |
| case WM_TIMER: |
| - reinterpret_cast<MessagePumpForUI*>(wparam)->HandleTimerMessage(); |
| + reinterpret_cast<MessagePumpForUIBase*>(wparam)->HandleTimerMessage(); |
| break; |
| } |
| return DefWindowProc(hwnd, message, wparam, lparam); |
| } |
| +void MessagePumpForUIBase::InitMessageWnd() { |
| + // Generate a unique window class name. |
| + string16 class_name = StringPrintf(kWndClassFormat, this); |
| + |
| + HINSTANCE instance = GetModuleFromAddress(&WndProcThunk); |
| + WNDCLASSEX wc = {0}; |
| + wc.cbSize = sizeof(wc); |
| + wc.lpfnWndProc = base::win::WrappedWindowProc<WndProcThunk>; |
| + wc.hInstance = instance; |
| + wc.lpszClassName = class_name.c_str(); |
| + atom_ = RegisterClassEx(&wc); |
| + DCHECK(atom_); |
| + |
| + message_hwnd_ = CreateWindow(MAKEINTATOM(atom_), 0, 0, 0, 0, 0, 0, |
| + HWND_MESSAGE, 0, instance, 0); |
| + DCHECK(message_hwnd_); |
| +} |
| + |
| +//----------------------------------------------------------------------------- |
| +// MessagePumpForUI public: |
| + |
| +MessagePumpForUI::MessagePumpForUI() { |
| +} |
| + |
| +MessagePumpForUI::~MessagePumpForUI() { |
| +} |
| + |
| +//----------------------------------------------------------------------------- |
| +// MessagePumpForUI private: |
| + |
| +void MessagePumpForUI::ScheduleDelayedWork( |
| + const TimeTicks& delayed_work_time) { |
| + delayed_work_time_ = delayed_work_time; |
| + RescheduleTimer(); |
| +} |
| + |
| void MessagePumpForUI::DoRunLoop() { |
| // IF this was just a simple PeekMessage() loop (servicing all possible work |
| // queues), then Windows would try to achieve the following order according |
| @@ -176,7 +255,7 @@ void MessagePumpForUI::DoRunLoop() { |
| // don't want to disturb that timer if it is already in flight. However, |
| // if we did do all remaining delayed work, then lets kill the WM_TIMER. |
| if (more_work_is_plausible && delayed_work_time_.is_null()) |
| - KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); |
| + KillTimer(); |
| if (state_->should_quit) |
| break; |
| @@ -194,24 +273,6 @@ void MessagePumpForUI::DoRunLoop() { |
| } |
| } |
| -void MessagePumpForUI::InitMessageWnd() { |
| - // Generate a unique window class name. |
| - string16 class_name = StringPrintf(kWndClassFormat, this); |
| - |
| - HINSTANCE instance = GetModuleFromAddress(&WndProcThunk); |
| - WNDCLASSEX wc = {0}; |
| - wc.cbSize = sizeof(wc); |
| - wc.lpfnWndProc = base::win::WrappedWindowProc<WndProcThunk>; |
| - wc.hInstance = instance; |
| - wc.lpszClassName = class_name.c_str(); |
| - atom_ = RegisterClassEx(&wc); |
| - DCHECK(atom_); |
| - |
| - message_hwnd_ = CreateWindow(MAKEINTATOM(atom_), 0, 0, 0, 0, 0, 0, |
| - HWND_MESSAGE, 0, instance, 0); |
| - DCHECK(message_hwnd_); |
| -} |
| - |
| void MessagePumpForUI::WaitForWork() { |
| // Wait until a message is available, up to the time needed by the timer |
| // manager to fire the next set of timers. |
| @@ -253,7 +314,7 @@ void MessagePumpForUI::HandleWorkMessage() { |
| // sort. |
| if (!state_) { |
| // Since we handled a kMsgHaveWork message, we must still update this flag. |
| - InterlockedExchange(&have_work_, 0); |
| + InterlockedExchange(&work_state_, READY); |
| return; |
| } |
| @@ -271,7 +332,7 @@ void MessagePumpForUI::HandleWorkMessage() { |
| } |
| void MessagePumpForUI::HandleTimerMessage() { |
| - KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); |
| + KillTimer(); |
| // If we are being called outside of the context of Run, then don't do |
| // anything. This could correspond to a MessageBox call or something of |
| @@ -286,45 +347,13 @@ void MessagePumpForUI::HandleTimerMessage() { |
| void MessagePumpForUI::RescheduleTimer() { |
| if (delayed_work_time_.is_null()) |
| return; |
| - // |
| - // We would *like* to provide high resolution timers. Windows timers using |
| - // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup |
| - // mechanism because the application can enter modal windows loops where it |
| - // is not running our MessageLoop; the only way to have our timers fire in |
| - // these cases is to post messages there. |
| - // |
| - // To provide sub-10ms timers, we process timers directly from our run loop. |
| - // For the common case, timers will be processed there as the run loop does |
| - // its normal work. However, we *also* set the system timer so that WM_TIMER |
| - // events fire. This mops up the case of timers not being able to work in |
| - // modal message loops. It is possible for the SetTimer to pop and have no |
| - // pending timers, because they could have already been processed by the |
| - // run loop itself. |
| - // |
| - // We use a single SetTimer corresponding to the timer that will expire |
| - // soonest. As new timers are created and destroyed, we update SetTimer. |
| - // Getting a spurious SetTimer event firing is benign, as we'll just be |
| - // processing an empty timer queue. |
| - // |
| + |
| int delay_msec = GetCurrentDelay(); |
| DCHECK_GE(delay_msec, 0); |
| if (delay_msec == 0) { |
| ScheduleWork(); |
| } else { |
| - if (delay_msec < USER_TIMER_MINIMUM) |
| - delay_msec = USER_TIMER_MINIMUM; |
| - |
| - // Create a WM_TIMER event that will wake us up to check for any pending |
| - // timers (in case we are running within a nested, external sub-pump). |
| - BOOL ret = SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), |
| - delay_msec, NULL); |
| - if (ret) |
| - return; |
| - // If we can't set timers, we are in big trouble... but cross our fingers |
| - // for now. |
| - // TODO(jar): If we don't see this error, use a CHECK() here instead. |
| - UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", SET_TIMER_ERROR, |
| - MESSAGE_LOOP_PROBLEM_MAX); |
| + SetTimer(delay_msec); |
| } |
| } |
| @@ -346,7 +375,7 @@ bool MessagePumpForUI::ProcessNextWindowsMessage() { |
| } |
| bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { |
| - TRACE_EVENT1("base", "MessagePumpForUI::ProcessMessageHelper", |
| + TRACE_EVENT1("base", "MessagePumpForUIBase::ProcessMessageHelper", |
| "message", msg.message); |
| if (WM_QUIT == msg.message) { |
| // Repost the QUIT message so that it will be retrieved by the primary |
| @@ -357,7 +386,7 @@ bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { |
| } |
| // While running our main message pump, we discard kMsgHaveWork messages. |
| - if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_) |
| + if (IsWorkMessage(msg)) |
| return ProcessPumpReplacementMessage(); |
| if (CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode)) |
| @@ -392,12 +421,11 @@ bool MessagePumpForUI::ProcessPumpReplacementMessage() { |
| have_message = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE; |
| } |
| - DCHECK(!have_message || kMsgHaveWork != msg.message || |
| - msg.hwnd != message_hwnd_); |
| + DCHECK(!have_message || !IsWorkMessage(msg)); |
| // Since we discarded a kMsgHaveWork message, we must update the flag. |
| - int old_have_work = InterlockedExchange(&have_work_, 0); |
| - DCHECK(old_have_work); |
| + int old_work_state_ = InterlockedExchange(&work_state_, READY); |
| + DCHECK_EQ(HAVE_WORK, old_work_state_); |
| // We don't need a special time slice if we didn't have_message to process. |
| if (!have_message) |
| @@ -412,6 +440,125 @@ bool MessagePumpForUI::ProcessPumpReplacementMessage() { |
| } |
| //----------------------------------------------------------------------------- |
| +// MessagePumpForGpu public: |
| + |
| +MessagePumpForGpu::MessagePumpForGpu() { |
| +} |
| + |
| +MessagePumpForGpu::~MessagePumpForGpu() { |
| +} |
| + |
| +//----------------------------------------------------------------------------- |
| +// MessagePumpForGpu private: |
| + |
| +void MessagePumpForGpu::ScheduleDelayedWork( |
| + const TimeTicks& delayed_work_time) { |
| + // We know that we can't be blocked right now since this method can only be |
| + // called on the same thread as Run, so we only need to update our record of |
| + // how long to sleep when we do sleep. |
| + delayed_work_time_ = delayed_work_time; |
| +} |
| + |
| +void MessagePumpForGpu::DoRunLoop() { |
| + while (!state_->should_quit) { |
| + // Indicate that the loop is handling the work. |
| + // If there is a race condition between switching to WORKING state here and |
| + // the producer thread setting the HAVE_WORK state after exiting the wait, |
| + // the event might remain in the signalled state. That might be less than |
| + // optimal but wouldn't result in failing to handle the work. |
| + InterlockedExchange(&work_state_, WORKING); |
| + |
| + bool more_work_is_plausible = state_->delegate->DoWork(); |
| + if (state_->should_quit) |
| + break; |
| + |
| + more_work_is_plausible |= |
| + state_->delegate->DoDelayedWork(&delayed_work_time_); |
| + if (state_->should_quit) |
| + break; |
| + |
| + if (more_work_is_plausible) |
| + continue; |
| + |
| + more_work_is_plausible = state_->delegate->DoIdleWork(); |
| + if (state_->should_quit) |
| + break; |
| + |
| + if (more_work_is_plausible) |
| + continue; |
| + |
| + // Switch that working state to READY to indicate that the loop is |
| + // waiting for accepting new work if it is still in WORKING state and hasn't |
| + // been signalled. Otherwise if it is in HAVE_WORK state skip the wait |
| + // and proceed to handing the work. |
| + if (HAVE_WORK == InterlockedCompareExchange(&work_state_, READY, WORKING)) |
| + continue; // Skip wait, more work was requested. |
| + |
| + WaitForWork(); // Wait (sleep) until we have work to do again. |
| + } |
| +} |
| + |
| +void MessagePumpForGpu::WaitForWork() { |
| + // Wait until a message is available, up to the time needed by the timer |
| + // manager to fire the next set of timers. |
| + int delay; |
| + |
| + // The while loop handles the situation where on Windows 7 and later versions |
| + // MsgWaitForMultipleObjectsEx might time out slightly earlier (less than one |
| + // ms) than the specified |delay|. In that situation it is more optimal to |
| + // just wait again rather than waste a DoRunLoop cycle. |
| + while((delay = GetCurrentDelay()) != 0) { |
| + if (delay < 0) // Negative value means no timers waiting. |
| + delay = INFINITE; |
| + |
| + DWORD result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT, |
| + MWMO_INPUTAVAILABLE); |
| + if (WAIT_OBJECT_0 == result) { |
| + // A WM_* message is available. |
| + ProcessWindowsMessages(); |
| + return; |
| + } |
| + |
| + DCHECK_NE(WAIT_FAILED, result) << GetLastError(); |
| + } |
| +} |
| + |
| +void MessagePumpForGpu::ProcessWindowsMessages() { |
| + MSG msg; |
| + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE) { |
| + if (WM_QUIT == msg.message) { |
| + // Repost the QUIT message so that it will be retrieved by the primary |
| + // GetMessage() loop. |
| + state_->should_quit = true; |
| + PostQuitMessage(static_cast<int>(msg.wParam)); |
| + return; |
| + } |
| + |
| + if (!IsWorkMessage(msg)) { |
| + TranslateMessage(&msg); |
| + DispatchMessage(&msg); |
| + } |
| + } |
| +} |
| + |
| +void MessagePumpForGpu::HandleWorkMessage() { |
| + NOTREACHED(); |
| +} |
| + |
| +void MessagePumpForGpu::HandleTimerMessage() { |
| + NOTREACHED(); |
| +} |
| + |
| +void MessagePumpForGpu::InitFactory() { |
| + MessageLoop::InitMessagePumpForUIFactory( |
| + &MessagePumpForGpu::CreateMessagePumpForGpu); |
| +} |
| + |
| +scoped_ptr<MessagePump> MessagePumpForGpu::CreateMessagePumpForGpu() { |
| + return scoped_ptr<MessagePump>(new MessagePumpForGpu); |
| +} |
| + |
| +//----------------------------------------------------------------------------- |
| // MessagePumpForIO public: |
| MessagePumpForIO::MessagePumpForIO() { |
| @@ -423,7 +570,7 @@ MessagePumpForIO::~MessagePumpForIO() { |
| } |
| void MessagePumpForIO::ScheduleWork() { |
| - if (InterlockedExchange(&have_work_, 1)) |
| + if (READY != InterlockedExchange(&work_state_, HAVE_WORK)) |
| return; // Someone else continued the pumping. |
| // Make sure the MessagePump does some work for us. |
| @@ -434,7 +581,7 @@ void MessagePumpForIO::ScheduleWork() { |
| return; // Post worked perfectly. |
| // See comment in MessagePumpForUI::ScheduleWork() for this error recovery. |
| - InterlockedExchange(&have_work_, 0); // Clarify that we didn't succeed. |
| + InterlockedExchange(&work_state_, READY); // Clarify that we didn't succeed. |
| UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR, |
| MESSAGE_LOOP_PROBLEM_MAX); |
| } |
| @@ -579,7 +726,7 @@ bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { |
| reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.handler)) { |
| // This is our internal completion. |
| DCHECK(!item.bytes_transfered); |
| - InterlockedExchange(&have_work_, 0); |
| + InterlockedExchange(&work_state_, READY); |
| return true; |
| } |
| return false; |