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