message_pump_win.cc (20344B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style license that can be 5 // found in the LICENSE file. 6 7 #include "base/message_pump_win.h" 8 9 #include <math.h> 10 11 #include "base/message_loop.h" 12 #include "base/histogram.h" 13 #include "base/win_util.h" 14 #include "mozilla/Maybe.h" 15 #include "mozilla/ProfilerLabels.h" 16 #include "mozilla/ProfilerThreadSleep.h" 17 #include "WinUtils.h" 18 19 using base::Time; 20 21 namespace base { 22 23 static const wchar_t kWndClass[] = L"Chrome_MessagePumpWindow"; 24 25 // Message sent to get an additional time slice for pumping (processing) another 26 // task (a series of such messages creates a continuous task pump). 27 static const int kMsgHaveWork = WM_USER + 1; 28 29 //----------------------------------------------------------------------------- 30 // MessagePumpWin public: 31 32 void MessagePumpWin::AddObserver(Observer* observer) { 33 observers_.AddObserver(observer); 34 } 35 36 void MessagePumpWin::RemoveObserver(Observer* observer) { 37 observers_.RemoveObserver(observer); 38 } 39 40 void MessagePumpWin::WillProcessMessage(const MSG& msg) { 41 FOR_EACH_OBSERVER(Observer, observers_, WillProcessMessage(msg)); 42 } 43 44 void MessagePumpWin::DidProcessMessage(const MSG& msg) { 45 FOR_EACH_OBSERVER(Observer, observers_, DidProcessMessage(msg)); 46 } 47 48 void MessagePumpWin::RunWithDispatcher(Delegate* delegate, 49 Dispatcher* dispatcher) { 50 RunState s; 51 s.delegate = delegate; 52 s.dispatcher = dispatcher; 53 s.should_quit = false; 54 s.run_depth = state_ ? state_->run_depth + 1 : 1; 55 56 RunState* previous_state = state_; 57 state_ = &s; 58 59 DoRunLoop(); 60 61 state_ = previous_state; 62 } 63 64 void MessagePumpWin::Quit() { 65 DCHECK(state_); 66 state_->should_quit = true; 67 } 68 69 //----------------------------------------------------------------------------- 70 // MessagePumpWin protected: 71 72 int MessagePumpWin::GetCurrentDelay() const { 73 if (delayed_work_time_.is_null()) return -1; 74 75 // Be careful here. TimeDelta has a precision of microseconds, but we want a 76 // value in milliseconds. If there are 5.5ms left, should the delay be 5 or 77 // 6? It should be 6 to avoid executing delayed work too early. 78 double timeout = 79 ceil((delayed_work_time_ - TimeTicks::Now()).InMillisecondsF()); 80 81 // If this value is negative, then we need to run delayed work soon. 82 int delay = static_cast<int>(timeout); 83 if (delay < 0) delay = 0; 84 85 return delay; 86 } 87 88 //----------------------------------------------------------------------------- 89 // MessagePumpForUI public: 90 91 MessagePumpForUI::MessagePumpForUI() { InitMessageWnd(); } 92 93 MessagePumpForUI::~MessagePumpForUI() { 94 DestroyWindow(message_hwnd_); 95 UnregisterClass(kWndClass, GetModuleHandle(NULL)); 96 } 97 98 void MessagePumpForUI::ScheduleWork() { 99 if (InterlockedExchange(&have_work_, 1)) 100 return; // Someone else continued the pumping. 101 102 // Make sure the MessagePump does some work for us. 103 PostMessage(message_hwnd_, kMsgHaveWork, reinterpret_cast<WPARAM>(this), 0); 104 105 // In order to wake up any cross-process COM calls which may currently be 106 // pending on the main thread, we also have to post a UI message. 107 PostMessage(message_hwnd_, WM_NULL, 0, 0); 108 } 109 110 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { 111 // 112 // We would *like* to provide high resolution timers. Windows timers using 113 // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup 114 // mechanism because the application can enter modal windows loops where it 115 // is not running our MessageLoop; the only way to have our timers fire in 116 // these cases is to post messages there. 117 // 118 // To provide sub-10ms timers, we process timers directly from our run loop. 119 // For the common case, timers will be processed there as the run loop does 120 // its normal work. However, we *also* set the system timer so that WM_TIMER 121 // events fire. This mops up the case of timers not being able to work in 122 // modal message loops. It is possible for the SetTimer to pop and have no 123 // pending timers, because they could have already been processed by the 124 // run loop itself. 125 // 126 // We use a single SetTimer corresponding to the timer that will expire 127 // soonest. As new timers are created and destroyed, we update SetTimer. 128 // Getting a spurrious SetTimer event firing is benign, as we'll just be 129 // processing an empty timer queue. 130 // 131 delayed_work_time_ = delayed_work_time; 132 133 int delay_msec = GetCurrentDelay(); 134 DCHECK(delay_msec >= 0); 135 if (delay_msec < USER_TIMER_MINIMUM) delay_msec = USER_TIMER_MINIMUM; 136 137 // Create a WM_TIMER event that will wake us up to check for any pending 138 // timers (in case we are running within a nested, external sub-pump). 139 SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), delay_msec, NULL); 140 } 141 142 void MessagePumpForUI::PumpOutPendingPaintMessages() { 143 // If we are being called outside of the context of Run, then don't try to do 144 // any work. 145 if (!state_) return; 146 147 // Create a mini-message-pump to force immediate processing of only Windows 148 // WM_PAINT messages. Don't provide an infinite loop, but do enough peeking 149 // to get the job done. Actual common max is 4 peeks, but we'll be a little 150 // safe here. 151 const int kMaxPeekCount = 20; 152 int peek_count; 153 for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) { 154 MSG msg; 155 if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT)) break; 156 ProcessMessageHelper(msg); 157 if (state_->should_quit) break; 158 } 159 } 160 161 //----------------------------------------------------------------------------- 162 // MessagePumpForUI private: 163 164 // static 165 LRESULT CALLBACK MessagePumpForUI::WndProcThunk(HWND hwnd, UINT message, 166 WPARAM wparam, LPARAM lparam) { 167 switch (message) { 168 case kMsgHaveWork: 169 reinterpret_cast<MessagePumpForUI*>(wparam)->HandleWorkMessage(); 170 break; 171 case WM_TIMER: 172 reinterpret_cast<MessagePumpForUI*>(wparam)->HandleTimerMessage(); 173 break; 174 } 175 return DefWindowProc(hwnd, message, wparam, lparam); 176 } 177 178 void MessagePumpForUI::DoRunLoop() { 179 // IF this was just a simple PeekMessage() loop (servicing all possible work 180 // queues), then Windows would try to achieve the following order according 181 // to MSDN documentation about PeekMessage with no filter): 182 // * Sent messages 183 // * Posted messages 184 // * Sent messages (again) 185 // * WM_PAINT messages 186 // * WM_TIMER messages 187 // 188 // Summary: none of the above classes is starved, and sent messages has twice 189 // the chance of being processed (i.e., reduced service time). 190 191 for (;;) { 192 // If we do any work, we may create more messages etc., and more work may 193 // possibly be waiting in another task group. When we (for example) 194 // ProcessNextWindowsMessage(), there is a good chance there are still more 195 // messages waiting. On the other hand, when any of these methods return 196 // having done no work, then it is pretty unlikely that calling them again 197 // quickly will find any work to do. Finally, if they all say they had no 198 // work, then it is a good time to consider sleeping (waiting) for more 199 // work. 200 201 bool more_work_is_plausible = ProcessNextWindowsMessage(); 202 if (state_->should_quit) break; 203 204 more_work_is_plausible |= state_->delegate->DoWork(); 205 if (state_->should_quit) break; 206 207 more_work_is_plausible |= 208 state_->delegate->DoDelayedWork(&delayed_work_time_); 209 // If we did not process any delayed work, then we can assume that our 210 // existing WM_TIMER if any will fire when delayed work should run. We 211 // don't want to disturb that timer if it is already in flight. However, 212 // if we did do all remaining delayed work, then lets kill the WM_TIMER. 213 if (more_work_is_plausible && delayed_work_time_.is_null()) 214 KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); 215 if (state_->should_quit) break; 216 217 if (more_work_is_plausible) continue; 218 219 more_work_is_plausible = state_->delegate->DoIdleWork(); 220 if (state_->should_quit) break; 221 222 if (more_work_is_plausible) continue; 223 224 WaitForWork(); // Wait (sleep) until we have work to do again. 225 } 226 } 227 228 void MessagePumpForUI::InitMessageWnd() { 229 HINSTANCE hinst = GetModuleHandle(NULL); 230 231 WNDCLASSEX wc = {0}; 232 wc.cbSize = sizeof(wc); 233 wc.lpfnWndProc = WndProcThunk; 234 wc.hInstance = hinst; 235 wc.lpszClassName = kWndClass; 236 RegisterClassEx(&wc); 237 238 message_hwnd_ = 239 CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, 0); 240 DCHECK(message_hwnd_); 241 } 242 243 void MessagePumpForUI::WaitForWork() { 244 AUTO_PROFILER_LABEL("MessagePumpForUI::WaitForWork", IDLE); 245 246 // Wait until a message is available, up to the time needed by the timer 247 // manager to fire the next set of timers. 248 int delay = GetCurrentDelay(); 249 if (delay < 0) // Negative value means no timers waiting. 250 delay = INFINITE; 251 252 mozilla::widget::WinUtils::WaitForMessage(delay); 253 } 254 255 void MessagePumpForUI::HandleWorkMessage() { 256 // If we are being called outside of the context of Run, then don't try to do 257 // any work. This could correspond to a MessageBox call or something of that 258 // sort. 259 if (!state_) { 260 // Since we handled a kMsgHaveWork message, we must still update this flag. 261 InterlockedExchange(&have_work_, 0); 262 return; 263 } 264 265 // Let whatever would have run had we not been putting messages in the queue 266 // run now. This is an attempt to make our dummy message not starve other 267 // messages that may be in the Windows message queue. 268 ProcessPumpReplacementMessage(); 269 270 // Now give the delegate a chance to do some work. He'll let us know if he 271 // needs to do more work. 272 if (state_->delegate->DoWork()) ScheduleWork(); 273 } 274 275 void MessagePumpForUI::HandleTimerMessage() { 276 KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); 277 278 // If we are being called outside of the context of Run, then don't do 279 // anything. This could correspond to a MessageBox call or something of 280 // that sort. 281 if (!state_) return; 282 283 state_->delegate->DoDelayedWork(&delayed_work_time_); 284 if (!delayed_work_time_.is_null()) { 285 // A bit gratuitous to set delayed_work_time_ again, but oh well. 286 ScheduleDelayedWork(delayed_work_time_); 287 } 288 } 289 290 bool MessagePumpForUI::ProcessNextWindowsMessage() { 291 // If there are sent messages in the queue then PeekMessage internally 292 // dispatches the message and returns false. We return true in this 293 // case to ensure that the message loop peeks again instead of calling 294 // MsgWaitForMultipleObjectsEx again. 295 bool sent_messages_in_queue = false; 296 DWORD queue_status = ::GetQueueStatus(QS_SENDMESSAGE); 297 if (HIWORD(queue_status) & QS_SENDMESSAGE) sent_messages_in_queue = true; 298 299 MSG msg; 300 if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 301 return ProcessMessageHelper(msg); 302 303 return sent_messages_in_queue; 304 } 305 306 bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { 307 if (WM_QUIT == msg.message) { 308 // WM_QUIT is the standard way to exit a ::GetMessage() loop. Our 309 // MessageLoop has its own quit mechanism, so WM_QUIT is unexpected and 310 // should be ignored. 311 return true; 312 } 313 314 // While running our main message pump, we discard kMsgHaveWork messages. 315 if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_) 316 return ProcessPumpReplacementMessage(); 317 318 WillProcessMessage(msg); 319 320 if (state_->dispatcher) { 321 if (!state_->dispatcher->Dispatch(msg)) state_->should_quit = true; 322 } else { 323 ::TranslateMessage(&msg); 324 ::DispatchMessage(&msg); 325 } 326 327 DidProcessMessage(msg); 328 return true; 329 } 330 331 bool MessagePumpForUI::ProcessPumpReplacementMessage() { 332 // When we encounter a kMsgHaveWork message, this method is called to peek 333 // and process a replacement message, such as a WM_PAINT or WM_TIMER. The 334 // goal is to make the kMsgHaveWork as non-intrusive as possible, even though 335 // a continuous stream of such messages are posted. This method carefully 336 // peeks a message while there is no chance for a kMsgHaveWork to be pending, 337 // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to 338 // possibly be posted), and finally dispatches that peeked replacement. Note 339 // that the re-post of kMsgHaveWork may be asynchronous to this thread!! 340 341 MSG msg; 342 bool have_message = false; 343 if (MessageLoop::current()->os_modal_loop()) { 344 // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above. 345 have_message = ::PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) || 346 ::PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE); 347 } else { 348 have_message = (0 != ::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)); 349 350 if (have_message && msg.message == WM_NULL) 351 have_message = (0 != ::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)); 352 } 353 354 DCHECK(!have_message || kMsgHaveWork != msg.message || 355 msg.hwnd != message_hwnd_); 356 357 // Since we discarded a kMsgHaveWork message, we must update the flag. 358 int old_have_work = InterlockedExchange(&have_work_, 0); 359 DCHECK(old_have_work); 360 361 // We don't need a special time slice if we didn't have_message to process. 362 if (!have_message) return false; 363 364 if (WM_QUIT == msg.message) { 365 // If we're in a nested ::GetMessage() loop then we must let that loop see 366 // the WM_QUIT in order for it to exit. If we're in DoRunLoop then the re- 367 // posted WM_QUIT will be either ignored, or handled, by 368 // ProcessMessageHelper() called directly from ProcessNextWindowsMessage(). 369 ::PostQuitMessage(static_cast<int>(msg.wParam)); 370 // Note: we *must not* ScheduleWork() here as WM_QUIT is a low-priority 371 // message on Windows (it is only returned by ::PeekMessage() when idle) : 372 // https://blogs.msdn.microsoft.com/oldnewthing/20051104-33/?p=33453. As 373 // such posting a kMsgHaveWork message via ScheduleWork() would cause an 374 // infinite loop (kMsgHaveWork message handled first means we end up here 375 // again and repost WM_QUIT+ScheduleWork() again, etc.). Not leaving a 376 // kMsgHaveWork message behind however is also problematic as unwinding 377 // multiple layers of nested ::GetMessage() loops can result in starving 378 // application tasks. TODO(https://crbug.com/890016) : Fix this. 379 380 // The return value is mostly irrelevant but return true like we would after 381 // processing a QuitClosure() task. 382 return true; 383 } 384 385 // Guarantee we'll get another time slice in the case where we go into native 386 // windows code. This ScheduleWork() may hurt performance a tiny bit when 387 // tasks appear very infrequently, but when the event queue is busy, the 388 // kMsgHaveWork events get (percentage wise) rarer and rarer. 389 ScheduleWork(); 390 return ProcessMessageHelper(msg); 391 } 392 393 //----------------------------------------------------------------------------- 394 // MessagePumpForIO public: 395 396 MessagePumpForIO::MessagePumpForIO() { 397 port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1)); 398 DCHECK(port_.IsValid()); 399 } 400 401 void MessagePumpForIO::ScheduleWork() { 402 if (InterlockedExchange(&have_work_, 1)) 403 return; // Someone else continued the pumping. 404 405 // Make sure the MessagePump does some work for us. 406 BOOL ret = 407 PostQueuedCompletionStatus(port_, 0, reinterpret_cast<ULONG_PTR>(this), 408 reinterpret_cast<OVERLAPPED*>(this)); 409 DCHECK(ret); 410 } 411 412 void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { 413 // We know that we can't be blocked right now since this method can only be 414 // called on the same thread as Run, so we only need to update our record of 415 // how long to sleep when we do sleep. 416 delayed_work_time_ = delayed_work_time; 417 } 418 419 void MessagePumpForIO::RegisterIOHandler(HANDLE file_handle, 420 IOHandler* handler) { 421 ULONG_PTR key = reinterpret_cast<ULONG_PTR>(handler); 422 HANDLE port = CreateIoCompletionPort(file_handle, port_, key, 1); 423 DCHECK(port == port_.Get()); 424 } 425 426 //----------------------------------------------------------------------------- 427 // MessagePumpForIO private: 428 429 void MessagePumpForIO::DoRunLoop() { 430 for (;;) { 431 // If we do any work, we may create more messages etc., and more work may 432 // possibly be waiting in another task group. When we (for example) 433 // WaitForIOCompletion(), there is a good chance there are still more 434 // messages waiting. On the other hand, when any of these methods return 435 // having done no work, then it is pretty unlikely that calling them 436 // again quickly will find any work to do. Finally, if they all say they 437 // had no work, then it is a good time to consider sleeping (waiting) for 438 // more work. 439 440 bool more_work_is_plausible = state_->delegate->DoWork(); 441 if (state_->should_quit) break; 442 443 more_work_is_plausible |= WaitForIOCompletion(0, NULL); 444 if (state_->should_quit) break; 445 446 more_work_is_plausible |= 447 state_->delegate->DoDelayedWork(&delayed_work_time_); 448 if (state_->should_quit) break; 449 450 if (more_work_is_plausible) continue; 451 452 more_work_is_plausible = state_->delegate->DoIdleWork(); 453 if (state_->should_quit) break; 454 455 if (more_work_is_plausible) continue; 456 457 WaitForWork(); // Wait (sleep) until we have work to do again. 458 } 459 } 460 461 // Wait until IO completes, up to the time needed by the timer manager to fire 462 // the next set of timers. 463 void MessagePumpForIO::WaitForWork() { 464 // We do not support nested IO message loops. This is to avoid messy 465 // recursion problems. 466 DCHECK(state_->run_depth == 1) << "Cannot nest an IO message loop!"; 467 468 int timeout = GetCurrentDelay(); 469 if (timeout < 0) // Negative value means no timers waiting. 470 timeout = INFINITE; 471 472 WaitForIOCompletion(timeout, NULL); 473 } 474 475 bool MessagePumpForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) { 476 IOItem item; 477 if (completed_io_.empty() || !MatchCompletedIOItem(filter, &item)) { 478 // We have to ask the system for another IO completion. 479 if (!GetIOItem(timeout, &item)) return false; 480 481 if (ProcessInternalIOItem(item)) return true; 482 } 483 484 if (item.context->handler) { 485 if (filter && item.handler != filter) { 486 // Save this item for later 487 completed_io_.push_back(item); 488 } else { 489 DCHECK(item.context->handler == item.handler); 490 item.handler->OnIOCompleted(item.context, item.bytes_transfered, 491 item.error); 492 } 493 } else { 494 // The handler must be gone by now, just cleanup the mess. 495 delete item.context; 496 } 497 return true; 498 } 499 500 // Asks the OS for another IO completion result. 501 bool MessagePumpForIO::GetIOItem(DWORD timeout, IOItem* item) { 502 memset(item, 0, sizeof(*item)); 503 ULONG_PTR key = 0; 504 OVERLAPPED* overlapped = NULL; 505 BOOL success; 506 { 507 AUTO_PROFILER_LABEL("MessagePumpForIO::GetIOItem::Wait", IDLE); 508 #ifdef MOZ_GECKO_PROFILER 509 mozilla::Maybe<mozilla::AutoProfilerThreadSleep> profilerThreadSleep; 510 if (timeout != 0) { 511 profilerThreadSleep.emplace(); 512 } 513 #endif 514 success = GetQueuedCompletionStatus(port_.Get(), &item->bytes_transfered, 515 &key, &overlapped, timeout); 516 } 517 if (!success) { 518 if (!overlapped) return false; // Nothing in the queue. 519 item->error = GetLastError(); 520 item->bytes_transfered = 0; 521 } 522 523 item->handler = reinterpret_cast<IOHandler*>(key); 524 item->context = reinterpret_cast<IOContext*>(overlapped); 525 return true; 526 } 527 528 bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { 529 if (this == reinterpret_cast<MessagePumpForIO*>(item.context) && 530 this == reinterpret_cast<MessagePumpForIO*>(item.handler)) { 531 // This is our internal completion. 532 DCHECK(!item.bytes_transfered); 533 InterlockedExchange(&have_work_, 0); 534 return true; 535 } 536 return false; 537 } 538 539 // Returns a completion item that was previously received. 540 bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { 541 DCHECK(!completed_io_.empty()); 542 for (std::list<IOItem>::iterator it = completed_io_.begin(); 543 it != completed_io_.end(); ++it) { 544 if (!filter || it->handler == filter) { 545 *item = *it; 546 completed_io_.erase(it); 547 return true; 548 } 549 } 550 return false; 551 } 552 553 } // namespace base