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" | |
12 #include "base/message_loop/message_loop.h" | 13 #include "base/message_loop/message_loop.h" |
13 #include "base/metrics/histogram.h" | 14 #include "base/metrics/histogram.h" |
14 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
15 #include "base/trace_event/trace_event.h" | 16 #include "base/trace_event/trace_event.h" |
16 #include "base/win/current_module.h" | 17 #include "base/win/current_module.h" |
17 #include "base/win/wrapped_window_proc.h" | 18 #include "base/win/wrapped_window_proc.h" |
18 | 19 |
19 namespace base { | 20 namespace base { |
20 | 21 |
21 namespace { | 22 namespace { |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
86 : atom_(0) { | 87 : atom_(0) { |
87 InitMessageWnd(); | 88 InitMessageWnd(); |
88 } | 89 } |
89 | 90 |
90 MessagePumpForUI::~MessagePumpForUI() { | 91 MessagePumpForUI::~MessagePumpForUI() { |
91 DestroyWindow(message_hwnd_); | 92 DestroyWindow(message_hwnd_); |
92 UnregisterClass(MAKEINTATOM(atom_), CURRENT_MODULE()); | 93 UnregisterClass(MAKEINTATOM(atom_), CURRENT_MODULE()); |
93 } | 94 } |
94 | 95 |
95 void MessagePumpForUI::ScheduleWork() { | 96 void MessagePumpForUI::ScheduleWork() { |
96 if (InterlockedExchange(&have_work_, 1)) | 97 if (InterlockedExchange(&work_state_, HAVE_WORK) != READY) |
97 return; // Someone else continued the pumping. | 98 return; // Someone else continued the pumping. |
98 | 99 |
99 // Make sure the MessagePump does some work for us. | 100 // Make sure the MessagePump does some work for us. |
100 BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork, | 101 BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork, |
101 reinterpret_cast<WPARAM>(this), 0); | 102 reinterpret_cast<WPARAM>(this), 0); |
102 if (ret) | 103 if (ret) |
103 return; // There was room in the Window Message queue. | 104 return; // There was room in the Window Message queue. |
104 | 105 |
105 // 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 |
106 // 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 |
107 // 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 |
108 // 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. :-( |
109 // 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 |
110 // 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 |
111 // recovery. Nested loops are pretty transient (we think), so this will | 112 // recovery. Nested loops are pretty transient (we think), so this will |
112 // probably be recoverable. | 113 // probably be recoverable. |
113 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); | |
114 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR, | 117 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR, |
115 MESSAGE_LOOP_PROBLEM_MAX); | 118 MESSAGE_LOOP_PROBLEM_MAX); |
116 } | 119 } |
117 | 120 |
118 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { | 121 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { |
119 delayed_work_time_ = delayed_work_time; | 122 delayed_work_time_ = delayed_work_time; |
120 RescheduleTimer(); | 123 RescheduleTimer(); |
121 } | 124 } |
122 | 125 |
123 //----------------------------------------------------------------------------- | 126 //----------------------------------------------------------------------------- |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
246 | 249 |
247 DCHECK_NE(WAIT_FAILED, result) << GetLastError(); | 250 DCHECK_NE(WAIT_FAILED, result) << GetLastError(); |
248 } | 251 } |
249 | 252 |
250 void MessagePumpForUI::HandleWorkMessage() { | 253 void MessagePumpForUI::HandleWorkMessage() { |
251 // 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 |
252 // 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 |
253 // sort. | 256 // sort. |
254 if (!state_) { | 257 if (!state_) { |
255 // 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. |
256 InterlockedExchange(&have_work_, 0); | 259 InterlockedExchange(&work_state_, READY); |
257 return; | 260 return; |
258 } | 261 } |
259 | 262 |
260 // 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 |
261 // 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 |
262 // messages that may be in the Windows message queue. | 265 // messages that may be in the Windows message queue. |
263 ProcessPumpReplacementMessage(); | 266 ProcessPumpReplacementMessage(); |
264 | 267 |
265 // 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 |
266 // needs to do more work. | 269 // needs to do more work. |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
389 have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) || | 392 have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) || |
390 PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE); | 393 PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE); |
391 } else { | 394 } else { |
392 have_message = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE; | 395 have_message = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE; |
393 } | 396 } |
394 | 397 |
395 DCHECK(!have_message || kMsgHaveWork != msg.message || | 398 DCHECK(!have_message || kMsgHaveWork != msg.message || |
396 msg.hwnd != message_hwnd_); | 399 msg.hwnd != message_hwnd_); |
397 | 400 |
398 // Since we discarded a kMsgHaveWork message, we must update the flag. | 401 // Since we discarded a kMsgHaveWork message, we must update the flag. |
399 int old_have_work = InterlockedExchange(&have_work_, 0); | 402 int old_work_state_ = InterlockedExchange(&work_state_, READY); |
400 DCHECK(old_have_work); | 403 DCHECK_EQ(HAVE_WORK, old_work_state_); |
401 | 404 |
402 // 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. |
403 if (!have_message) | 406 if (!have_message) |
404 return false; | 407 return false; |
405 | 408 |
406 // 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 |
407 // windows code. This ScheduleWork() may hurt performance a tiny bit when | 410 // windows code. This ScheduleWork() may hurt performance a tiny bit when |
408 // 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 |
409 // kMsgHaveWork events get (percentage wise) rarer and rarer. | 412 // kMsgHaveWork events get (percentage wise) rarer and rarer. |
410 ScheduleWork(); | 413 ScheduleWork(); |
411 return ProcessMessageHelper(msg); | 414 return ProcessMessageHelper(msg); |
412 } | 415 } |
413 | 416 |
414 //----------------------------------------------------------------------------- | 417 //----------------------------------------------------------------------------- |
418 // MessagePumpForGpu public: | |
419 | |
420 MessagePumpForGpu::MessagePumpForGpu() : thread_id_(GetCurrentThreadId()) { | |
421 // Init the message queue. | |
422 MSG msg; | |
423 PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE); | |
iyengar
2016/04/11 22:18:21
Is this needed?
stanisc
2016/04/11 23:57:32
Yes, this is needed to ensure that the message que
| |
424 } | |
425 | |
426 MessagePumpForGpu::~MessagePumpForGpu() {} | |
427 | |
428 // static | |
429 void MessagePumpForGpu::InitFactory() { | |
430 bool init_result = MessageLoop::InitMessagePumpForUIFactory( | |
431 &MessagePumpForGpu::CreateMessagePumpForGpu); | |
432 DCHECK(init_result); | |
433 } | |
434 | |
435 // static | |
436 std::unique_ptr<MessagePump> MessagePumpForGpu::CreateMessagePumpForGpu() { | |
437 return WrapUnique<MessagePump>(new MessagePumpForGpu); | |
438 } | |
439 | |
440 void MessagePumpForGpu::ScheduleWork() { | |
441 if (InterlockedExchange(&work_state_, HAVE_WORK) != READY) | |
442 return; // Someone else continued the pumping. | |
443 | |
444 // Make sure the MessagePump does some work for us. | |
445 BOOL ret = PostThreadMessage(thread_id_, kMsgHaveWork, 0, 0); | |
446 if (ret) | |
447 return; // There was room in the Window Message queue. | |
448 | |
449 // See comment in MessagePumpForUI::ScheduleWork. | |
450 InterlockedExchange(&work_state_, READY); | |
451 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR, | |
452 MESSAGE_LOOP_PROBLEM_MAX); | |
453 } | |
454 | |
455 void MessagePumpForGpu::ScheduleDelayedWork( | |
456 const TimeTicks& delayed_work_time) { | |
457 // We know that we can't be blocked right now since this method can only be | |
458 // called on the same thread as Run, so we only need to update our record of | |
459 // how long to sleep when we do sleep. | |
460 delayed_work_time_ = delayed_work_time; | |
461 } | |
462 | |
463 //----------------------------------------------------------------------------- | |
464 // MessagePumpForGpu private: | |
465 | |
466 void MessagePumpForGpu::DoRunLoop() { | |
467 while (!state_->should_quit) { | |
468 // Indicate that the loop is handling the work. | |
469 // If there is a race condition between switching to WORKING state here and | |
470 // the producer thread setting the HAVE_WORK state after exiting the wait, | |
471 // the event might remain in the signalled state. That might be less than | |
472 // optimal but wouldn't result in failing to handle the work. | |
473 InterlockedExchange(&work_state_, WORKING); | |
474 | |
475 bool more_work_is_plausible = state_->delegate->DoWork(); | |
iyengar
2016/04/11 22:18:21
This function has a lot in common with the Message
stanisc
2016/04/11 23:57:32
I tried an approach with a common base class - see
| |
476 if (state_->should_quit) | |
477 break; | |
478 | |
479 more_work_is_plausible |= | |
480 state_->delegate->DoDelayedWork(&delayed_work_time_); | |
481 if (state_->should_quit) | |
482 break; | |
483 | |
484 if (more_work_is_plausible) | |
485 continue; | |
486 | |
487 more_work_is_plausible = state_->delegate->DoIdleWork(); | |
488 if (state_->should_quit) | |
489 break; | |
490 | |
491 if (more_work_is_plausible) | |
492 continue; | |
493 | |
494 // Switch that working state to READY to indicate that the loop is | |
495 // waiting for accepting new work if it is still in WORKING state and hasn't | |
496 // been signalled. Otherwise if it is in HAVE_WORK state skip the wait | |
497 // and proceed to handing the work. | |
498 if (InterlockedCompareExchange(&work_state_, READY, WORKING) == HAVE_WORK) | |
499 continue; // Skip wait, more work was requested. | |
500 | |
501 WaitForWork(); // Wait (sleep) until we have work to do again. | |
502 } | |
503 } | |
504 | |
505 void MessagePumpForGpu::WaitForWork() { | |
506 // Wait until a message is available, up to the time needed by the timer | |
507 // manager to fire the next set of timers. | |
508 int delay; | |
509 | |
510 // The while loop handles the situation where on Windows 7 and later versions | |
511 // MsgWaitForMultipleObjectsEx might time out slightly earlier (less than one | |
512 // ms) than the specified |delay|. In that situation it is more optimal to | |
513 // just wait again rather than waste a DoRunLoop cycle. | |
514 while ((delay = GetCurrentDelay()) != 0) { | |
515 if (delay < 0) // Negative value means no timers waiting. | |
516 delay = INFINITE; | |
517 | |
518 DWORD result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT, 0); | |
519 if (result == WAIT_OBJECT_0) { | |
520 // A WM_* message is available. | |
521 if (ProcessMessages()) | |
522 return; | |
523 } | |
524 | |
525 DCHECK_NE(WAIT_FAILED, result) << GetLastError(); | |
526 } | |
527 } | |
528 | |
529 bool MessagePumpForGpu::ProcessMessages() { | |
530 MSG msg; | |
531 bool have_work = false; | |
532 while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { | |
533 if (msg.message == WM_QUIT) { | |
iyengar
2016/04/11 22:18:21
We should be calling the message filters if any in
stanisc
2016/04/11 23:57:32
Do you think calling CallMsgFilter would do any go
ananta
2016/04/12 00:00:47
You never know who could inject into your process.
stanisc
2016/04/12 21:59:34
OK, I've added a call to CallMsgFilter below. I di
| |
534 // Repost the QUIT message so that it will be retrieved by the primary | |
535 // GetMessage() loop. | |
536 state_->should_quit = true; | |
537 PostQuitMessage(static_cast<int>(msg.wParam)); | |
538 return true; | |
539 } | |
540 | |
541 if (msg.hwnd == nullptr && msg.message == kMsgHaveWork) { | |
542 have_work = true; | |
543 } else { | |
544 TranslateMessage(&msg); | |
545 DispatchMessage(&msg); | |
546 } | |
547 } | |
548 | |
549 return have_work; | |
550 } | |
551 | |
552 //----------------------------------------------------------------------------- | |
415 // MessagePumpForIO public: | 553 // MessagePumpForIO public: |
416 | 554 |
417 MessagePumpForIO::MessagePumpForIO() { | 555 MessagePumpForIO::MessagePumpForIO() { |
418 port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1)); | 556 port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1)); |
419 DCHECK(port_.IsValid()); | 557 DCHECK(port_.IsValid()); |
420 } | 558 } |
421 | 559 |
422 MessagePumpForIO::~MessagePumpForIO() { | 560 MessagePumpForIO::~MessagePumpForIO() { |
423 } | 561 } |
424 | 562 |
425 void MessagePumpForIO::ScheduleWork() { | 563 void MessagePumpForIO::ScheduleWork() { |
426 if (InterlockedExchange(&have_work_, 1)) | 564 if (InterlockedExchange(&work_state_, HAVE_WORK) != READY) |
427 return; // Someone else continued the pumping. | 565 return; // Someone else continued the pumping. |
428 | 566 |
429 // Make sure the MessagePump does some work for us. | 567 // Make sure the MessagePump does some work for us. |
430 BOOL ret = PostQueuedCompletionStatus(port_.Get(), 0, | 568 BOOL ret = PostQueuedCompletionStatus(port_.Get(), 0, |
431 reinterpret_cast<ULONG_PTR>(this), | 569 reinterpret_cast<ULONG_PTR>(this), |
432 reinterpret_cast<OVERLAPPED*>(this)); | 570 reinterpret_cast<OVERLAPPED*>(this)); |
433 if (ret) | 571 if (ret) |
434 return; // Post worked perfectly. | 572 return; // Post worked perfectly. |
435 | 573 |
436 // See comment in MessagePumpForUI::ScheduleWork() for this error recovery. | 574 // See comment in MessagePumpForUI::ScheduleWork() for this error recovery. |
437 InterlockedExchange(&have_work_, 0); // Clarify that we didn't succeed. | 575 InterlockedExchange(&work_state_, READY); // Clarify that we didn't succeed. |
438 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR, | 576 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR, |
439 MESSAGE_LOOP_PROBLEM_MAX); | 577 MESSAGE_LOOP_PROBLEM_MAX); |
440 } | 578 } |
441 | 579 |
442 void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { | 580 void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { |
443 // We know that we can't be blocked right now since this method can only be | 581 // We know that we can't be blocked right now since this method can only be |
444 // called on the same thread as Run, so we only need to update our record of | 582 // called on the same thread as Run, so we only need to update our record of |
445 // how long to sleep when we do sleep. | 583 // how long to sleep when we do sleep. |
446 delayed_work_time_ = delayed_work_time; | 584 delayed_work_time_ = delayed_work_time; |
447 } | 585 } |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
572 item->handler = KeyToHandler(key, &item->has_valid_io_context); | 710 item->handler = KeyToHandler(key, &item->has_valid_io_context); |
573 item->context = reinterpret_cast<IOContext*>(overlapped); | 711 item->context = reinterpret_cast<IOContext*>(overlapped); |
574 return true; | 712 return true; |
575 } | 713 } |
576 | 714 |
577 bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { | 715 bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { |
578 if (reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.context) && | 716 if (reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.context) && |
579 reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.handler)) { | 717 reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.handler)) { |
580 // This is our internal completion. | 718 // This is our internal completion. |
581 DCHECK(!item.bytes_transfered); | 719 DCHECK(!item.bytes_transfered); |
582 InterlockedExchange(&have_work_, 0); | 720 InterlockedExchange(&work_state_, READY); |
583 return true; | 721 return true; |
584 } | 722 } |
585 return false; | 723 return false; |
586 } | 724 } |
587 | 725 |
588 // Returns a completion item that was previously received. | 726 // Returns a completion item that was previously received. |
589 bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { | 727 bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { |
590 DCHECK(!completed_io_.empty()); | 728 DCHECK(!completed_io_.empty()); |
591 for (std::list<IOItem>::iterator it = completed_io_.begin(); | 729 for (std::list<IOItem>::iterator it = completed_io_.begin(); |
592 it != completed_io_.end(); ++it) { | 730 it != completed_io_.end(); ++it) { |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
633 | 771 |
634 // static | 772 // static |
635 MessagePumpForIO::IOHandler* MessagePumpForIO::KeyToHandler( | 773 MessagePumpForIO::IOHandler* MessagePumpForIO::KeyToHandler( |
636 ULONG_PTR key, | 774 ULONG_PTR key, |
637 bool* has_valid_io_context) { | 775 bool* has_valid_io_context) { |
638 *has_valid_io_context = ((key & 1) == 0); | 776 *has_valid_io_context = ((key & 1) == 0); |
639 return reinterpret_cast<IOHandler*>(key & ~static_cast<ULONG_PTR>(1)); | 777 return reinterpret_cast<IOHandler*>(key & ~static_cast<ULONG_PTR>(1)); |
640 } | 778 } |
641 | 779 |
642 } // namespace base | 780 } // namespace base |
OLD | NEW |