summaryrefslogtreecommitdiffstats
path: root/chromium/base/message_loop/message_pump_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/base/message_loop/message_pump_win.cc')
-rw-r--r--chromium/base/message_loop/message_pump_win.cc338
1 files changed, 282 insertions, 56 deletions
diff --git a/chromium/base/message_loop/message_pump_win.cc b/chromium/base/message_loop/message_pump_win.cc
index 91a3496cd3c..de20bdc084d 100644
--- a/chromium/base/message_loop/message_pump_win.cc
+++ b/chromium/base/message_loop/message_pump_win.cc
@@ -9,6 +9,7 @@
#include <limits>
+#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/strings/stringprintf.h"
@@ -27,6 +28,85 @@ enum MessageLoopProblems {
MESSAGE_LOOP_PROBLEM_MAX,
};
+// The following define pointers to user32 API's for the API's which are used
+// in this file. These are added to avoid directly depending on user32 from
+// base as there are users of base who don't want this.
+decltype(::TranslateMessage)* g_translate_message = nullptr;
+decltype(::DispatchMessageW)* g_dispatch_message = nullptr;
+decltype(::PeekMessageW)* g_peek_message = nullptr;
+decltype(::PostMessageW)* g_post_message = nullptr;
+decltype(::DefWindowProcW)* g_def_window_proc = nullptr;
+decltype(::PostQuitMessage)* g_post_quit = nullptr;
+decltype(::UnregisterClassW)* g_unregister_class = nullptr;
+decltype(::RegisterClassExW)* g_register_class = nullptr;
+decltype(::CreateWindowExW)* g_create_window_ex = nullptr;
+decltype(::DestroyWindow)* g_destroy_window = nullptr;
+decltype(::CallMsgFilterW)* g_call_msg_filter = nullptr;
+decltype(::GetQueueStatus)* g_get_queue_status = nullptr;
+decltype(::MsgWaitForMultipleObjectsEx)* g_msg_wait_for_multiple_objects_ex =
+ nullptr;
+decltype(::SetTimer)* g_set_timer = nullptr;
+decltype(::KillTimer)* g_kill_timer = nullptr;
+
+#define GET_USER32_API(module, name) \
+ reinterpret_cast<decltype(name)*>(::GetProcAddress(module, #name))
+
+// Initializes the global pointers to user32 APIs for the API's used in this
+// file.
+void InitUser32APIs() {
+ if (g_translate_message)
+ return;
+
+ HMODULE user32_module = ::GetModuleHandle(L"user32.dll");
+ CHECK(user32_module);
+
+ g_translate_message = GET_USER32_API(user32_module, TranslateMessage);
+ CHECK(g_translate_message);
+
+ g_dispatch_message = GET_USER32_API(user32_module, DispatchMessageW);
+ CHECK(g_dispatch_message);
+
+ g_peek_message = GET_USER32_API(user32_module, PeekMessageW);
+ CHECK(g_peek_message);
+
+ g_post_message = GET_USER32_API(user32_module, PostMessageW);
+ CHECK(g_post_message);
+
+ g_def_window_proc = GET_USER32_API(user32_module, DefWindowProcW);
+ CHECK(g_def_window_proc);
+
+ g_post_quit = GET_USER32_API(user32_module, PostQuitMessage);
+ CHECK(g_post_quit);
+
+ g_unregister_class = GET_USER32_API(user32_module, UnregisterClassW);
+ CHECK(g_unregister_class);
+
+ g_register_class = GET_USER32_API(user32_module, RegisterClassExW);
+ CHECK(g_register_class);
+
+ g_create_window_ex = GET_USER32_API(user32_module, CreateWindowExW);
+ CHECK(g_create_window_ex);
+
+ g_destroy_window = GET_USER32_API(user32_module, DestroyWindow);
+ CHECK(g_destroy_window);
+
+ g_call_msg_filter = GET_USER32_API(user32_module, CallMsgFilterW);
+ CHECK(g_call_msg_filter);
+
+ g_get_queue_status = GET_USER32_API(user32_module, GetQueueStatus);
+ CHECK(g_get_queue_status);
+
+ g_msg_wait_for_multiple_objects_ex =
+ GET_USER32_API(user32_module, MsgWaitForMultipleObjectsEx);
+ CHECK(g_msg_wait_for_multiple_objects_ex);
+
+ g_set_timer = GET_USER32_API(user32_module, SetTimer);
+ CHECK(g_set_timer);
+
+ g_kill_timer = GET_USER32_API(user32_module, KillTimer);
+ CHECK(g_kill_timer);
+}
+
} // namespace
static const wchar_t kWndClassFormat[] = L"Chrome_MessagePumpWindow_%p";
@@ -35,9 +115,15 @@ 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 application-defined code passed to the hook procedure.
+static const int kMessageFilterCode = 0x5001;
+
//-----------------------------------------------------------------------------
// MessagePumpWin public:
+MessagePumpWin::MessagePumpWin() {
+}
+
void MessagePumpWin::Run(Delegate* delegate) {
RunState s;
s.delegate = delegate;
@@ -88,21 +174,22 @@ int MessagePumpWin::GetCurrentDelay() const {
MessagePumpForUI::MessagePumpForUI()
: atom_(0) {
+ InitUser32APIs();
InitMessageWnd();
}
MessagePumpForUI::~MessagePumpForUI() {
- DestroyWindow(message_hwnd_);
- UnregisterClass(MAKEINTATOM(atom_), CURRENT_MODULE());
+ g_destroy_window(message_hwnd_);
+ g_unregister_class(MAKEINTATOM(atom_), CURRENT_MODULE());
}
void MessagePumpForUI::ScheduleWork() {
- if (InterlockedExchange(&have_work_, 1))
+ if (InterlockedExchange(&work_state_, HAVE_WORK) != READY)
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);
+ BOOL ret = g_post_message(message_hwnd_, kMsgHaveWork,
+ reinterpret_cast<WPARAM>(this), 0);
if (ret)
return; // There was room in the Window Message queue.
@@ -114,7 +201,9 @@ 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);
state_->schedule_work_error_count++;
@@ -140,7 +229,7 @@ LRESULT CALLBACK MessagePumpForUI::WndProcThunk(
reinterpret_cast<MessagePumpForUI*>(wparam)->HandleTimerMessage();
break;
}
- return DefWindowProc(hwnd, message, wparam, lparam);
+ return g_def_window_proc(hwnd, message, wparam, lparam);
}
void MessagePumpForUI::DoRunLoop() {
@@ -181,7 +270,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));
+ g_kill_timer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
if (state_->should_quit)
break;
@@ -209,11 +298,11 @@ void MessagePumpForUI::InitMessageWnd() {
wc.lpfnWndProc = base::win::WrappedWindowProc<WndProcThunk>;
wc.hInstance = instance;
wc.lpszClassName = class_name.c_str();
- atom_ = RegisterClassEx(&wc);
+ atom_ = g_register_class(&wc);
DCHECK(atom_);
- message_hwnd_ = CreateWindow(MAKEINTATOM(atom_), 0, 0, 0, 0, 0, 0,
- HWND_MESSAGE, 0, instance, 0);
+ message_hwnd_ = g_create_window_ex(0, MAKEINTATOM(atom_), 0, 0, 0, 0, 0, 0,
+ HWND_MESSAGE, 0, instance, 0);
DCHECK(message_hwnd_);
}
@@ -227,8 +316,8 @@ void MessagePumpForUI::WaitForWork() {
if (delay < 0) // Negative value means no timers waiting.
delay = INFINITE;
- DWORD result =
- MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT, wait_flags);
+ DWORD result = g_msg_wait_for_multiple_objects_ex(0, nullptr, delay,
+ QS_ALLINPUT, wait_flags);
if (WAIT_OBJECT_0 == result) {
// A WM_* message is available.
@@ -246,9 +335,9 @@ void MessagePumpForUI::WaitForWork() {
// current thread.
MSG msg = {0};
bool has_pending_sent_message =
- (HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
+ (HIWORD(g_get_queue_status(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
if (has_pending_sent_message ||
- PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
+ g_peek_message(&msg, nullptr, 0, 0, PM_NOREMOVE)) {
return;
}
@@ -268,7 +357,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;
}
@@ -277,7 +366,7 @@ void MessagePumpForUI::HandleWorkMessage() {
// messages that may be in the Windows message queue.
ProcessPumpReplacementMessage();
- // Now give the delegate a chance to do some work. He'll let us know if he
+ // Now give the delegate a chance to do some work. It'll let us know if it
// needs to do more work.
if (state_->delegate->DoWork())
ScheduleWork();
@@ -286,7 +375,7 @@ void MessagePumpForUI::HandleWorkMessage() {
}
void MessagePumpForUI::HandleTimerMessage() {
- KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
+ g_kill_timer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
// 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
@@ -331,8 +420,8 @@ void MessagePumpForUI::RescheduleTimer() {
// 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);
+ BOOL ret = g_set_timer(message_hwnd_, reinterpret_cast<UINT_PTR>(this),
+ delay_msec, nullptr);
if (ret)
return;
// If we can't set timers, we are in big trouble... but cross our fingers
@@ -349,12 +438,12 @@ bool MessagePumpForUI::ProcessNextWindowsMessage() {
// case to ensure that the message loop peeks again instead of calling
// MsgWaitForMultipleObjectsEx again.
bool sent_messages_in_queue = false;
- DWORD queue_status = GetQueueStatus(QS_SENDMESSAGE);
+ DWORD queue_status = g_get_queue_status(QS_SENDMESSAGE);
if (HIWORD(queue_status) & QS_SENDMESSAGE)
sent_messages_in_queue = true;
MSG msg;
- if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE)
+ if (g_peek_message(&msg, nullptr, 0, 0, PM_REMOVE) != FALSE)
return ProcessMessageHelper(msg);
return sent_messages_in_queue;
@@ -367,7 +456,7 @@ bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) {
// 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));
+ g_post_quit(static_cast<int>(msg.wParam));
return false;
}
@@ -375,44 +464,36 @@ bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) {
if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_)
return ProcessPumpReplacementMessage();
- if (CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode))
+ if (g_call_msg_filter(const_cast<MSG*>(&msg), kMessageFilterCode))
return true;
- TranslateMessage(&msg);
- DispatchMessage(&msg);
+ g_translate_message(&msg);
+ g_dispatch_message(&msg);
return true;
}
bool MessagePumpForUI::ProcessPumpReplacementMessage() {
- // 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;
+ // When we encounter a kMsgHaveWork message, this method is called to peek and
+ // process a replacement message. 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!!
+
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;
- }
+ const bool have_message =
+ g_peek_message(&msg, nullptr, 0, 0, PM_REMOVE) != FALSE;
+ // Expect no message or a message different than kMsgHaveWork.
DCHECK(!have_message || kMsgHaveWork != msg.message ||
msg.hwnd != message_hwnd_);
// 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)
@@ -427,6 +508,150 @@ bool MessagePumpForUI::ProcessPumpReplacementMessage() {
}
//-----------------------------------------------------------------------------
+// MessagePumpForGpu public:
+
+MessagePumpForGpu::MessagePumpForGpu() {
+ event_.Set(CreateEvent(nullptr, FALSE, FALSE, nullptr));
+ InitUser32APIs();
+}
+
+MessagePumpForGpu::~MessagePumpForGpu() {}
+
+// static
+void MessagePumpForGpu::InitFactory() {
+ bool init_result = MessageLoop::InitMessagePumpForUIFactory(
+ &MessagePumpForGpu::CreateMessagePumpForGpu);
+ DCHECK(init_result);
+}
+
+// static
+std::unique_ptr<MessagePump> MessagePumpForGpu::CreateMessagePumpForGpu() {
+ return WrapUnique<MessagePump>(new MessagePumpForGpu);
+}
+
+void MessagePumpForGpu::ScheduleWork() {
+ if (InterlockedExchange(&work_state_, HAVE_WORK) != READY)
+ return; // Someone else continued the pumping.
+
+ // TODO(stanisc): crbug.com/596190: Preserve for crash dump analysis.
+ // Remove this when the bug is fixed.
+ last_set_event_timeticks_ = TimeTicks::Now();
+
+ // Make sure the MessagePump does some work for us.
+ SetEvent(event_.Get());
+}
+
+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;
+}
+
+bool MessagePumpForGpu::WasSignaled() {
+ // If |event_| was set this would reset it back to unset state.
+ return WaitForSingleObject(event_.Get(), 0) == WAIT_OBJECT_0;
+}
+
+//-----------------------------------------------------------------------------
+// MessagePumpForGpu private:
+
+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 = ProcessNextMessage();
+ if (state_->should_quit)
+ break;
+
+ 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 (InterlockedCompareExchange(&work_state_, READY, WORKING) == HAVE_WORK)
+ 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;
+
+ // TODO(stanisc): crbug.com/596190: Preserve for crash dump analysis.
+ // Remove this when the bug is fixed.
+ TimeTicks wait_for_work_timeticks = TimeTicks::Now();
+ debug::Alias(&wait_for_work_timeticks);
+ debug::Alias(&delay);
+
+ HANDLE handle = event_.Get();
+ DWORD result =
+ g_msg_wait_for_multiple_objects_ex(1, &handle, delay, QS_ALLINPUT, 0);
+ DCHECK_NE(WAIT_FAILED, result) << GetLastError();
+ if (result != WAIT_TIMEOUT) {
+ // Either work or message available.
+ return;
+ }
+ }
+}
+
+bool MessagePumpForGpu::ProcessNextMessage() {
+ MSG msg;
+ if (!g_peek_message(&msg, nullptr, 0, 0, PM_REMOVE))
+ return false;
+
+ if (msg.message == WM_QUIT) {
+ // Repost the QUIT message so that it will be retrieved by the primary
+ // GetMessage() loop.
+ state_->should_quit = true;
+ g_post_quit(static_cast<int>(msg.wParam));
+ return false;
+ }
+
+ if (!g_call_msg_filter(const_cast<MSG*>(&msg), kMessageFilterCode)) {
+ g_translate_message(&msg);
+ g_dispatch_message(&msg);
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
// MessagePumpForIO public:
MessagePumpForIO::IOContext::IOContext() {
@@ -434,14 +659,15 @@ MessagePumpForIO::IOContext::IOContext() {
}
MessagePumpForIO::MessagePumpForIO() {
- port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1));
+ port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr,
+ reinterpret_cast<ULONG_PTR>(nullptr), 1));
DCHECK(port_.IsValid());
}
MessagePumpForIO::~MessagePumpForIO() = default;
void MessagePumpForIO::ScheduleWork() {
- if (InterlockedExchange(&have_work_, 1))
+ if (InterlockedExchange(&work_state_, HAVE_WORK) != READY)
return; // Someone else continued the pumping.
// Make sure the MessagePump does some work for us.
@@ -452,7 +678,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);
state_->schedule_work_error_count++;
@@ -502,7 +728,7 @@ void MessagePumpForIO::DoRunLoop() {
if (state_->should_quit)
break;
- more_work_is_plausible |= WaitForIOCompletion(0, NULL);
+ more_work_is_plausible |= WaitForIOCompletion(0, nullptr);
if (state_->should_quit)
break;
@@ -536,7 +762,7 @@ void MessagePumpForIO::WaitForWork() {
if (timeout < 0) // Negative value means no timers waiting.
timeout = INFINITE;
- WaitForIOCompletion(timeout, NULL);
+ WaitForIOCompletion(timeout, nullptr);
}
bool MessagePumpForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
@@ -563,8 +789,8 @@ bool MessagePumpForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
// Asks the OS for another IO completion result.
bool MessagePumpForIO::GetIOItem(DWORD timeout, IOItem* item) {
memset(item, 0, sizeof(*item));
- ULONG_PTR key = NULL;
- OVERLAPPED* overlapped = NULL;
+ ULONG_PTR key = reinterpret_cast<ULONG_PTR>(nullptr);
+ OVERLAPPED* overlapped = nullptr;
if (!GetQueuedCompletionStatus(port_.Get(), &item->bytes_transfered, &key,
&overlapped, timeout)) {
if (!overlapped)
@@ -583,7 +809,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;