diff options
Diffstat (limited to 'chromium/base/message_loop/message_pump_win.cc')
-rw-r--r-- | chromium/base/message_loop/message_pump_win.cc | 338 |
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; |