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