Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(551)

Unified Diff: base/message_loop/message_pump_win.cc

Issue 1714263002: Version of MessagePumpForUI optimized for GPU process (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « base/message_loop/message_pump_win.h ('k') | content/gpu/gpu_main.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
« no previous file with comments | « base/message_loop/message_pump_win.h ('k') | content/gpu/gpu_main.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698