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 |