| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/message_loop/message_pump_win.h" | 5 #include "base/message_loop/message_pump_win.h" |
| 6 | 6 |
| 7 #include <math.h> | 7 #include <math.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include <limits> | 10 #include <limits> |
| 11 | 11 |
| 12 #include "base/memory/ptr_util.h" |
| 13 #include "base/message_loop/message_loop.h" |
| 12 #include "base/metrics/histogram.h" | 14 #include "base/metrics/histogram.h" |
| 13 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
| 14 #include "base/trace_event/trace_event.h" | 16 #include "base/trace_event/trace_event.h" |
| 15 #include "base/win/current_module.h" | 17 #include "base/win/current_module.h" |
| 16 #include "base/win/wrapped_window_proc.h" | 18 #include "base/win/wrapped_window_proc.h" |
| 17 | 19 |
| 18 namespace base { | 20 namespace base { |
| 19 | 21 |
| 20 namespace { | 22 namespace { |
| 21 | 23 |
| 22 enum MessageLoopProblems { | 24 enum MessageLoopProblems { |
| 23 MESSAGE_POST_ERROR, | 25 MESSAGE_POST_ERROR, |
| 24 COMPLETION_POST_ERROR, | 26 COMPLETION_POST_ERROR, |
| 25 SET_TIMER_ERROR, | 27 SET_TIMER_ERROR, |
| 26 MESSAGE_LOOP_PROBLEM_MAX, | 28 MESSAGE_LOOP_PROBLEM_MAX, |
| 27 }; | 29 }; |
| 28 | 30 |
| 29 } // namespace | 31 } // namespace |
| 30 | 32 |
| 31 static const wchar_t kWndClassFormat[] = L"Chrome_MessagePumpWindow_%p"; | 33 static const wchar_t kWndClassFormat[] = L"Chrome_MessagePumpWindow_%p"; |
| 32 | 34 |
| 33 // Message sent to get an additional time slice for pumping (processing) another | 35 // Message sent to get an additional time slice for pumping (processing) another |
| 34 // task (a series of such messages creates a continuous task pump). | 36 // task (a series of such messages creates a continuous task pump). |
| 35 static const int kMsgHaveWork = WM_USER + 1; | 37 static const int kMsgHaveWork = WM_USER + 1; |
| 36 | 38 |
| 39 // The application-defined code passed to the hook procedure. |
| 40 static const int kMessageFilterCode = 0x5001; |
| 41 |
| 37 //----------------------------------------------------------------------------- | 42 //----------------------------------------------------------------------------- |
| 38 // MessagePumpWin public: | 43 // MessagePumpWin public: |
| 39 | 44 |
| 40 void MessagePumpWin::Run(Delegate* delegate) { | 45 void MessagePumpWin::Run(Delegate* delegate) { |
| 41 RunState s; | 46 RunState s; |
| 42 s.delegate = delegate; | 47 s.delegate = delegate; |
| 43 s.should_quit = false; | 48 s.should_quit = false; |
| 44 s.run_depth = state_ ? state_->run_depth + 1 : 1; | 49 s.run_depth = state_ ? state_->run_depth + 1 : 1; |
| 45 | 50 |
| 46 // TODO(stanisc): crbug.com/596190: Remove this code once the bug is fixed. | 51 // TODO(stanisc): crbug.com/596190: Remove this code once the bug is fixed. |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 89 : atom_(0) { | 94 : atom_(0) { |
| 90 InitMessageWnd(); | 95 InitMessageWnd(); |
| 91 } | 96 } |
| 92 | 97 |
| 93 MessagePumpForUI::~MessagePumpForUI() { | 98 MessagePumpForUI::~MessagePumpForUI() { |
| 94 DestroyWindow(message_hwnd_); | 99 DestroyWindow(message_hwnd_); |
| 95 UnregisterClass(MAKEINTATOM(atom_), CURRENT_MODULE()); | 100 UnregisterClass(MAKEINTATOM(atom_), CURRENT_MODULE()); |
| 96 } | 101 } |
| 97 | 102 |
| 98 void MessagePumpForUI::ScheduleWork() { | 103 void MessagePumpForUI::ScheduleWork() { |
| 99 if (InterlockedExchange(&have_work_, 1)) | 104 if (InterlockedExchange(&work_state_, HAVE_WORK) != READY) |
| 100 return; // Someone else continued the pumping. | 105 return; // Someone else continued the pumping. |
| 101 | 106 |
| 102 // Make sure the MessagePump does some work for us. | 107 // Make sure the MessagePump does some work for us. |
| 103 BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork, | 108 BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork, |
| 104 reinterpret_cast<WPARAM>(this), 0); | 109 reinterpret_cast<WPARAM>(this), 0); |
| 105 if (ret) | 110 if (ret) |
| 106 return; // There was room in the Window Message queue. | 111 return; // There was room in the Window Message queue. |
| 107 | 112 |
| 108 // We have failed to insert a have-work message, so there is a chance that we | 113 // We have failed to insert a have-work message, so there is a chance that we |
| 109 // will starve tasks/timers while sitting in a nested message loop. Nested | 114 // will starve tasks/timers while sitting in a nested message loop. Nested |
| 110 // loops only look at Windows Message queues, and don't look at *our* task | 115 // loops only look at Windows Message queues, and don't look at *our* task |
| 111 // queues, etc., so we might not get a time slice in such. :-( | 116 // queues, etc., so we might not get a time slice in such. :-( |
| 112 // We could abort here, but the fear is that this failure mode is plausibly | 117 // We could abort here, but the fear is that this failure mode is plausibly |
| 113 // common (queue is full, of about 2000 messages), so we'll do a near-graceful | 118 // common (queue is full, of about 2000 messages), so we'll do a near-graceful |
| 114 // recovery. Nested loops are pretty transient (we think), so this will | 119 // recovery. Nested loops are pretty transient (we think), so this will |
| 115 // probably be recoverable. | 120 // probably be recoverable. |
| 116 InterlockedExchange(&have_work_, 0); // Clarify that we didn't really insert. | 121 |
| 122 // Clarify that we didn't really insert. |
| 123 InterlockedExchange(&work_state_, READY); |
| 117 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR, | 124 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR, |
| 118 MESSAGE_LOOP_PROBLEM_MAX); | 125 MESSAGE_LOOP_PROBLEM_MAX); |
| 119 state_->schedule_work_error_count++; | 126 state_->schedule_work_error_count++; |
| 120 state_->last_schedule_work_error_time = Time::Now(); | 127 state_->last_schedule_work_error_time = Time::Now(); |
| 121 } | 128 } |
| 122 | 129 |
| 123 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { | 130 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { |
| 124 delayed_work_time_ = delayed_work_time; | 131 delayed_work_time_ = delayed_work_time; |
| 125 RescheduleTimer(); | 132 RescheduleTimer(); |
| 126 } | 133 } |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 260 DCHECK_NE(WAIT_FAILED, result) << GetLastError(); | 267 DCHECK_NE(WAIT_FAILED, result) << GetLastError(); |
| 261 } | 268 } |
| 262 } | 269 } |
| 263 | 270 |
| 264 void MessagePumpForUI::HandleWorkMessage() { | 271 void MessagePumpForUI::HandleWorkMessage() { |
| 265 // If we are being called outside of the context of Run, then don't try to do | 272 // If we are being called outside of the context of Run, then don't try to do |
| 266 // any work. This could correspond to a MessageBox call or something of that | 273 // any work. This could correspond to a MessageBox call or something of that |
| 267 // sort. | 274 // sort. |
| 268 if (!state_) { | 275 if (!state_) { |
| 269 // Since we handled a kMsgHaveWork message, we must still update this flag. | 276 // Since we handled a kMsgHaveWork message, we must still update this flag. |
| 270 InterlockedExchange(&have_work_, 0); | 277 InterlockedExchange(&work_state_, READY); |
| 271 return; | 278 return; |
| 272 } | 279 } |
| 273 | 280 |
| 274 // Let whatever would have run had we not been putting messages in the queue | 281 // Let whatever would have run had we not been putting messages in the queue |
| 275 // run now. This is an attempt to make our dummy message not starve other | 282 // run now. This is an attempt to make our dummy message not starve other |
| 276 // messages that may be in the Windows message queue. | 283 // messages that may be in the Windows message queue. |
| 277 ProcessPumpReplacementMessage(); | 284 ProcessPumpReplacementMessage(); |
| 278 | 285 |
| 279 // Now give the delegate a chance to do some work. He'll let us know if he | 286 // Now give the delegate a chance to do some work. He'll let us know if he |
| 280 // needs to do more work. | 287 // needs to do more work. |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 394 // asynchronous to this thread!! | 401 // asynchronous to this thread!! |
| 395 | 402 |
| 396 MSG msg; | 403 MSG msg; |
| 397 const bool have_message = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE; | 404 const bool have_message = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE; |
| 398 | 405 |
| 399 // Expect no message or a message different than kMsgHaveWork. | 406 // Expect no message or a message different than kMsgHaveWork. |
| 400 DCHECK(!have_message || kMsgHaveWork != msg.message || | 407 DCHECK(!have_message || kMsgHaveWork != msg.message || |
| 401 msg.hwnd != message_hwnd_); | 408 msg.hwnd != message_hwnd_); |
| 402 | 409 |
| 403 // Since we discarded a kMsgHaveWork message, we must update the flag. | 410 // Since we discarded a kMsgHaveWork message, we must update the flag. |
| 404 int old_have_work = InterlockedExchange(&have_work_, 0); | 411 int old_work_state_ = InterlockedExchange(&work_state_, READY); |
| 405 DCHECK(old_have_work); | 412 DCHECK_EQ(HAVE_WORK, old_work_state_); |
| 406 | 413 |
| 407 // We don't need a special time slice if we didn't have_message to process. | 414 // We don't need a special time slice if we didn't have_message to process. |
| 408 if (!have_message) | 415 if (!have_message) |
| 409 return false; | 416 return false; |
| 410 | 417 |
| 411 // Guarantee we'll get another time slice in the case where we go into native | 418 // Guarantee we'll get another time slice in the case where we go into native |
| 412 // windows code. This ScheduleWork() may hurt performance a tiny bit when | 419 // windows code. This ScheduleWork() may hurt performance a tiny bit when |
| 413 // tasks appear very infrequently, but when the event queue is busy, the | 420 // tasks appear very infrequently, but when the event queue is busy, the |
| 414 // kMsgHaveWork events get (percentage wise) rarer and rarer. | 421 // kMsgHaveWork events get (percentage wise) rarer and rarer. |
| 415 ScheduleWork(); | 422 ScheduleWork(); |
| 416 return ProcessMessageHelper(msg); | 423 return ProcessMessageHelper(msg); |
| 417 } | 424 } |
| 418 | 425 |
| 419 //----------------------------------------------------------------------------- | 426 //----------------------------------------------------------------------------- |
| 427 // MessagePumpForGpu public: |
| 428 |
| 429 MessagePumpForGpu::MessagePumpForGpu() |
| 430 : event_(CreateEvent(nullptr, FALSE, FALSE, nullptr)) {} |
| 431 |
| 432 MessagePumpForGpu::~MessagePumpForGpu() { |
| 433 CloseHandle(event_); |
| 434 } |
| 435 |
| 436 // static |
| 437 void MessagePumpForGpu::InitFactory() { |
| 438 bool init_result = MessageLoop::InitMessagePumpForUIFactory( |
| 439 &MessagePumpForGpu::CreateMessagePumpForGpu); |
| 440 DCHECK(init_result); |
| 441 } |
| 442 |
| 443 // static |
| 444 std::unique_ptr<MessagePump> MessagePumpForGpu::CreateMessagePumpForGpu() { |
| 445 return WrapUnique<MessagePump>(new MessagePumpForGpu); |
| 446 } |
| 447 |
| 448 void MessagePumpForGpu::ScheduleWork() { |
| 449 if (InterlockedExchange(&work_state_, HAVE_WORK) != READY) |
| 450 return; // Someone else continued the pumping. |
| 451 |
| 452 // Make sure the MessagePump does some work for us. |
| 453 SetEvent(event_); |
| 454 } |
| 455 |
| 456 void MessagePumpForGpu::ScheduleDelayedWork( |
| 457 const TimeTicks& delayed_work_time) { |
| 458 // We know that we can't be blocked right now since this method can only be |
| 459 // called on the same thread as Run, so we only need to update our record of |
| 460 // how long to sleep when we do sleep. |
| 461 delayed_work_time_ = delayed_work_time; |
| 462 } |
| 463 |
| 464 //----------------------------------------------------------------------------- |
| 465 // MessagePumpForGpu private: |
| 466 |
| 467 void MessagePumpForGpu::DoRunLoop() { |
| 468 while (!state_->should_quit) { |
| 469 // Indicate that the loop is handling the work. |
| 470 // If there is a race condition between switching to WORKING state here and |
| 471 // the producer thread setting the HAVE_WORK state after exiting the wait, |
| 472 // the event might remain in the signalled state. That might be less than |
| 473 // optimal but wouldn't result in failing to handle the work. |
| 474 InterlockedExchange(&work_state_, WORKING); |
| 475 |
| 476 bool more_work_is_plausible = ProcessNextMessage(); |
| 477 if (state_->should_quit) |
| 478 break; |
| 479 |
| 480 more_work_is_plausible |= state_->delegate->DoWork(); |
| 481 if (state_->should_quit) |
| 482 break; |
| 483 |
| 484 more_work_is_plausible |= |
| 485 state_->delegate->DoDelayedWork(&delayed_work_time_); |
| 486 if (state_->should_quit) |
| 487 break; |
| 488 |
| 489 if (more_work_is_plausible) |
| 490 continue; |
| 491 |
| 492 more_work_is_plausible = state_->delegate->DoIdleWork(); |
| 493 if (state_->should_quit) |
| 494 break; |
| 495 |
| 496 if (more_work_is_plausible) |
| 497 continue; |
| 498 |
| 499 // Switch that working state to READY to indicate that the loop is |
| 500 // waiting for accepting new work if it is still in WORKING state and hasn't |
| 501 // been signalled. Otherwise if it is in HAVE_WORK state skip the wait |
| 502 // and proceed to handing the work. |
| 503 if (InterlockedCompareExchange(&work_state_, READY, WORKING) == HAVE_WORK) |
| 504 continue; // Skip wait, more work was requested. |
| 505 |
| 506 WaitForWork(); // Wait (sleep) until we have work to do again. |
| 507 } |
| 508 } |
| 509 |
| 510 void MessagePumpForGpu::WaitForWork() { |
| 511 // Wait until a message is available, up to the time needed by the timer |
| 512 // manager to fire the next set of timers. |
| 513 int delay; |
| 514 |
| 515 // The while loop handles the situation where on Windows 7 and later versions |
| 516 // MsgWaitForMultipleObjectsEx might time out slightly earlier (less than one |
| 517 // ms) than the specified |delay|. In that situation it is more optimal to |
| 518 // just wait again rather than waste a DoRunLoop cycle. |
| 519 while ((delay = GetCurrentDelay()) != 0) { |
| 520 if (delay < 0) // Negative value means no timers waiting. |
| 521 delay = INFINITE; |
| 522 |
| 523 DWORD result = |
| 524 MsgWaitForMultipleObjectsEx(1, &event_, delay, QS_ALLINPUT, 0); |
| 525 if (result == WAIT_OBJECT_0) { |
| 526 // Work available. |
| 527 return; |
| 528 } else if (result == WAIT_OBJECT_0 + 1) { |
| 529 // Message available. Keep waiting if this message isn't for this thread. |
| 530 MSG msg; |
| 531 if (PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE)) |
| 532 return; |
| 533 } |
| 534 |
| 535 DCHECK_NE(WAIT_FAILED, result) << GetLastError(); |
| 536 } |
| 537 } |
| 538 |
| 539 bool MessagePumpForGpu::ProcessNextMessage() { |
| 540 MSG msg; |
| 541 if (!PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) |
| 542 return false; |
| 543 |
| 544 if (msg.message == WM_QUIT) { |
| 545 // Repost the QUIT message so that it will be retrieved by the primary |
| 546 // GetMessage() loop. |
| 547 state_->should_quit = true; |
| 548 PostQuitMessage(static_cast<int>(msg.wParam)); |
| 549 return false; |
| 550 } |
| 551 |
| 552 if (!CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode)) { |
| 553 TranslateMessage(&msg); |
| 554 DispatchMessage(&msg); |
| 555 } |
| 556 |
| 557 return true; |
| 558 } |
| 559 |
| 560 //----------------------------------------------------------------------------- |
| 420 // MessagePumpForIO public: | 561 // MessagePumpForIO public: |
| 421 | 562 |
| 422 MessagePumpForIO::IOContext::IOContext() { | 563 MessagePumpForIO::IOContext::IOContext() { |
| 423 memset(&overlapped, 0, sizeof(overlapped)); | 564 memset(&overlapped, 0, sizeof(overlapped)); |
| 424 } | 565 } |
| 425 | 566 |
| 426 MessagePumpForIO::MessagePumpForIO() { | 567 MessagePumpForIO::MessagePumpForIO() { |
| 427 port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1)); | 568 port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1)); |
| 428 DCHECK(port_.IsValid()); | 569 DCHECK(port_.IsValid()); |
| 429 } | 570 } |
| 430 | 571 |
| 431 MessagePumpForIO::~MessagePumpForIO() = default; | 572 MessagePumpForIO::~MessagePumpForIO() = default; |
| 432 | 573 |
| 433 void MessagePumpForIO::ScheduleWork() { | 574 void MessagePumpForIO::ScheduleWork() { |
| 434 if (InterlockedExchange(&have_work_, 1)) | 575 if (InterlockedExchange(&work_state_, HAVE_WORK) != READY) |
| 435 return; // Someone else continued the pumping. | 576 return; // Someone else continued the pumping. |
| 436 | 577 |
| 437 // Make sure the MessagePump does some work for us. | 578 // Make sure the MessagePump does some work for us. |
| 438 BOOL ret = PostQueuedCompletionStatus(port_.Get(), 0, | 579 BOOL ret = PostQueuedCompletionStatus(port_.Get(), 0, |
| 439 reinterpret_cast<ULONG_PTR>(this), | 580 reinterpret_cast<ULONG_PTR>(this), |
| 440 reinterpret_cast<OVERLAPPED*>(this)); | 581 reinterpret_cast<OVERLAPPED*>(this)); |
| 441 if (ret) | 582 if (ret) |
| 442 return; // Post worked perfectly. | 583 return; // Post worked perfectly. |
| 443 | 584 |
| 444 // See comment in MessagePumpForUI::ScheduleWork() for this error recovery. | 585 // See comment in MessagePumpForUI::ScheduleWork() for this error recovery. |
| 445 InterlockedExchange(&have_work_, 0); // Clarify that we didn't succeed. | 586 InterlockedExchange(&work_state_, READY); // Clarify that we didn't succeed. |
| 446 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR, | 587 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR, |
| 447 MESSAGE_LOOP_PROBLEM_MAX); | 588 MESSAGE_LOOP_PROBLEM_MAX); |
| 448 state_->schedule_work_error_count++; | 589 state_->schedule_work_error_count++; |
| 449 state_->last_schedule_work_error_time = Time::Now(); | 590 state_->last_schedule_work_error_time = Time::Now(); |
| 450 } | 591 } |
| 451 | 592 |
| 452 void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { | 593 void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { |
| 453 // We know that we can't be blocked right now since this method can only be | 594 // We know that we can't be blocked right now since this method can only be |
| 454 // called on the same thread as Run, so we only need to update our record of | 595 // called on the same thread as Run, so we only need to update our record of |
| 455 // how long to sleep when we do sleep. | 596 // how long to sleep when we do sleep. |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 566 item->handler = reinterpret_cast<IOHandler*>(key); | 707 item->handler = reinterpret_cast<IOHandler*>(key); |
| 567 item->context = reinterpret_cast<IOContext*>(overlapped); | 708 item->context = reinterpret_cast<IOContext*>(overlapped); |
| 568 return true; | 709 return true; |
| 569 } | 710 } |
| 570 | 711 |
| 571 bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { | 712 bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { |
| 572 if (reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.context) && | 713 if (reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.context) && |
| 573 reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.handler)) { | 714 reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.handler)) { |
| 574 // This is our internal completion. | 715 // This is our internal completion. |
| 575 DCHECK(!item.bytes_transfered); | 716 DCHECK(!item.bytes_transfered); |
| 576 InterlockedExchange(&have_work_, 0); | 717 InterlockedExchange(&work_state_, READY); |
| 577 return true; | 718 return true; |
| 578 } | 719 } |
| 579 return false; | 720 return false; |
| 580 } | 721 } |
| 581 | 722 |
| 582 // Returns a completion item that was previously received. | 723 // Returns a completion item that was previously received. |
| 583 bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { | 724 bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { |
| 584 DCHECK(!completed_io_.empty()); | 725 DCHECK(!completed_io_.empty()); |
| 585 for (std::list<IOItem>::iterator it = completed_io_.begin(); | 726 for (std::list<IOItem>::iterator it = completed_io_.begin(); |
| 586 it != completed_io_.end(); ++it) { | 727 it != completed_io_.end(); ++it) { |
| 587 if (!filter || it->handler == filter) { | 728 if (!filter || it->handler == filter) { |
| 588 *item = *it; | 729 *item = *it; |
| 589 completed_io_.erase(it); | 730 completed_io_.erase(it); |
| 590 return true; | 731 return true; |
| 591 } | 732 } |
| 592 } | 733 } |
| 593 return false; | 734 return false; |
| 594 } | 735 } |
| 595 | 736 |
| 596 } // namespace base | 737 } // namespace base |
| OLD | NEW |