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 { |
22 | 23 |
23 enum MessageLoopProblems { | 24 enum MessageLoopProblems { |
24 MESSAGE_POST_ERROR, | 25 MESSAGE_POST_ERROR, |
25 COMPLETION_POST_ERROR, | 26 COMPLETION_POST_ERROR, |
26 SET_TIMER_ERROR, | 27 SET_TIMER_ERROR, |
27 MESSAGE_LOOP_PROBLEM_MAX, | 28 MESSAGE_LOOP_PROBLEM_MAX, |
28 }; | 29 }; |
29 | 30 |
30 } // namespace | 31 } // namespace |
31 | 32 |
32 static const wchar_t kWndClassFormat[] = L"Chrome_MessagePumpWindow_%p"; | 33 static const wchar_t kWndClassFormat[] = L"Chrome_MessagePumpWindow_%p"; |
33 | 34 |
34 // 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 |
35 // task (a series of such messages creates a continuous task pump). | 36 // task (a series of such messages creates a continuous task pump). |
36 static const int kMsgHaveWork = WM_USER + 1; | 37 static const int kMsgHaveWork = WM_USER + 1; |
37 | 38 |
| 39 // The application-defined code passed to the hook procedure. |
| 40 static const int kMessageFilterCode = 0x5001; |
| 41 |
38 //----------------------------------------------------------------------------- | 42 //----------------------------------------------------------------------------- |
39 // MessagePumpWin public: | 43 // MessagePumpWin public: |
40 | 44 |
41 void MessagePumpWin::Run(Delegate* delegate) { | 45 void MessagePumpWin::Run(Delegate* delegate) { |
42 RunState s; | 46 RunState s; |
43 s.delegate = delegate; | 47 s.delegate = delegate; |
44 s.should_quit = false; | 48 s.should_quit = false; |
45 s.run_depth = state_ ? state_->run_depth + 1 : 1; | 49 s.run_depth = state_ ? state_->run_depth + 1 : 1; |
46 | 50 |
47 RunState* previous_state = state_; | 51 RunState* previous_state = state_; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
86 : atom_(0) { | 90 : atom_(0) { |
87 InitMessageWnd(); | 91 InitMessageWnd(); |
88 } | 92 } |
89 | 93 |
90 MessagePumpForUI::~MessagePumpForUI() { | 94 MessagePumpForUI::~MessagePumpForUI() { |
91 DestroyWindow(message_hwnd_); | 95 DestroyWindow(message_hwnd_); |
92 UnregisterClass(MAKEINTATOM(atom_), CURRENT_MODULE()); | 96 UnregisterClass(MAKEINTATOM(atom_), CURRENT_MODULE()); |
93 } | 97 } |
94 | 98 |
95 void MessagePumpForUI::ScheduleWork() { | 99 void MessagePumpForUI::ScheduleWork() { |
96 if (InterlockedExchange(&have_work_, 1)) | 100 if (InterlockedExchange(&work_state_, HAVE_WORK) != READY) |
97 return; // Someone else continued the pumping. | 101 return; // Someone else continued the pumping. |
98 | 102 |
99 // Make sure the MessagePump does some work for us. | 103 // Make sure the MessagePump does some work for us. |
100 BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork, | 104 BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork, |
101 reinterpret_cast<WPARAM>(this), 0); | 105 reinterpret_cast<WPARAM>(this), 0); |
102 if (ret) | 106 if (ret) |
103 return; // There was room in the Window Message queue. | 107 return; // There was room in the Window Message queue. |
104 | 108 |
105 // We have failed to insert a have-work message, so there is a chance that we | 109 // 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 | 110 // 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 | 111 // 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. :-( | 112 // 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 | 113 // 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 | 114 // 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 | 115 // recovery. Nested loops are pretty transient (we think), so this will |
112 // probably be recoverable. | 116 // probably be recoverable. |
113 InterlockedExchange(&have_work_, 0); // Clarify that we didn't really insert. | 117 |
| 118 // Clarify that we didn't really insert. |
| 119 InterlockedExchange(&work_state_, READY); |
114 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR, | 120 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR, |
115 MESSAGE_LOOP_PROBLEM_MAX); | 121 MESSAGE_LOOP_PROBLEM_MAX); |
116 } | 122 } |
117 | 123 |
118 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { | 124 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { |
119 delayed_work_time_ = delayed_work_time; | 125 delayed_work_time_ = delayed_work_time; |
120 RescheduleTimer(); | 126 RescheduleTimer(); |
121 } | 127 } |
122 | 128 |
123 //----------------------------------------------------------------------------- | 129 //----------------------------------------------------------------------------- |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
246 | 252 |
247 DCHECK_NE(WAIT_FAILED, result) << GetLastError(); | 253 DCHECK_NE(WAIT_FAILED, result) << GetLastError(); |
248 } | 254 } |
249 | 255 |
250 void MessagePumpForUI::HandleWorkMessage() { | 256 void MessagePumpForUI::HandleWorkMessage() { |
251 // If we are being called outside of the context of Run, then don't try to do | 257 // 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 | 258 // any work. This could correspond to a MessageBox call or something of that |
253 // sort. | 259 // sort. |
254 if (!state_) { | 260 if (!state_) { |
255 // Since we handled a kMsgHaveWork message, we must still update this flag. | 261 // Since we handled a kMsgHaveWork message, we must still update this flag. |
256 InterlockedExchange(&have_work_, 0); | 262 InterlockedExchange(&work_state_, READY); |
257 return; | 263 return; |
258 } | 264 } |
259 | 265 |
260 // Let whatever would have run had we not been putting messages in the queue | 266 // 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 | 267 // run now. This is an attempt to make our dummy message not starve other |
262 // messages that may be in the Windows message queue. | 268 // messages that may be in the Windows message queue. |
263 ProcessPumpReplacementMessage(); | 269 ProcessPumpReplacementMessage(); |
264 | 270 |
265 // Now give the delegate a chance to do some work. He'll let us know if he | 271 // Now give the delegate a chance to do some work. He'll let us know if he |
266 // needs to do more work. | 272 // 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) || | 395 have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) || |
390 PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE); | 396 PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE); |
391 } else { | 397 } else { |
392 have_message = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE; | 398 have_message = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE; |
393 } | 399 } |
394 | 400 |
395 DCHECK(!have_message || kMsgHaveWork != msg.message || | 401 DCHECK(!have_message || kMsgHaveWork != msg.message || |
396 msg.hwnd != message_hwnd_); | 402 msg.hwnd != message_hwnd_); |
397 | 403 |
398 // Since we discarded a kMsgHaveWork message, we must update the flag. | 404 // Since we discarded a kMsgHaveWork message, we must update the flag. |
399 int old_have_work = InterlockedExchange(&have_work_, 0); | 405 int old_work_state_ = InterlockedExchange(&work_state_, READY); |
400 DCHECK(old_have_work); | 406 DCHECK_EQ(HAVE_WORK, old_work_state_); |
401 | 407 |
402 // We don't need a special time slice if we didn't have_message to process. | 408 // We don't need a special time slice if we didn't have_message to process. |
403 if (!have_message) | 409 if (!have_message) |
404 return false; | 410 return false; |
405 | 411 |
406 // Guarantee we'll get another time slice in the case where we go into native | 412 // 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 | 413 // windows code. This ScheduleWork() may hurt performance a tiny bit when |
408 // tasks appear very infrequently, but when the event queue is busy, the | 414 // tasks appear very infrequently, but when the event queue is busy, the |
409 // kMsgHaveWork events get (percentage wise) rarer and rarer. | 415 // kMsgHaveWork events get (percentage wise) rarer and rarer. |
410 ScheduleWork(); | 416 ScheduleWork(); |
411 return ProcessMessageHelper(msg); | 417 return ProcessMessageHelper(msg); |
412 } | 418 } |
413 | 419 |
414 //----------------------------------------------------------------------------- | 420 //----------------------------------------------------------------------------- |
| 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 //----------------------------------------------------------------------------- |
415 // MessagePumpForIO public: | 558 // MessagePumpForIO public: |
416 | 559 |
417 MessagePumpForIO::MessagePumpForIO() { | 560 MessagePumpForIO::MessagePumpForIO() { |
418 port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1)); | 561 port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1)); |
419 DCHECK(port_.IsValid()); | 562 DCHECK(port_.IsValid()); |
420 } | 563 } |
421 | 564 |
422 MessagePumpForIO::~MessagePumpForIO() { | 565 MessagePumpForIO::~MessagePumpForIO() { |
423 } | 566 } |
424 | 567 |
425 void MessagePumpForIO::ScheduleWork() { | 568 void MessagePumpForIO::ScheduleWork() { |
426 if (InterlockedExchange(&have_work_, 1)) | 569 if (InterlockedExchange(&work_state_, HAVE_WORK) != READY) |
427 return; // Someone else continued the pumping. | 570 return; // Someone else continued the pumping. |
428 | 571 |
429 // Make sure the MessagePump does some work for us. | 572 // Make sure the MessagePump does some work for us. |
430 BOOL ret = PostQueuedCompletionStatus(port_.Get(), 0, | 573 BOOL ret = PostQueuedCompletionStatus(port_.Get(), 0, |
431 reinterpret_cast<ULONG_PTR>(this), | 574 reinterpret_cast<ULONG_PTR>(this), |
432 reinterpret_cast<OVERLAPPED*>(this)); | 575 reinterpret_cast<OVERLAPPED*>(this)); |
433 if (ret) | 576 if (ret) |
434 return; // Post worked perfectly. | 577 return; // Post worked perfectly. |
435 | 578 |
436 // See comment in MessagePumpForUI::ScheduleWork() for this error recovery. | 579 // See comment in MessagePumpForUI::ScheduleWork() for this error recovery. |
437 InterlockedExchange(&have_work_, 0); // Clarify that we didn't succeed. | 580 InterlockedExchange(&work_state_, READY); // Clarify that we didn't succeed. |
438 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR, | 581 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR, |
439 MESSAGE_LOOP_PROBLEM_MAX); | 582 MESSAGE_LOOP_PROBLEM_MAX); |
440 } | 583 } |
441 | 584 |
442 void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { | 585 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 | 586 // 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 | 587 // 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. | 588 // how long to sleep when we do sleep. |
446 delayed_work_time_ = delayed_work_time; | 589 delayed_work_time_ = delayed_work_time; |
447 } | 590 } |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
572 item->handler = KeyToHandler(key, &item->has_valid_io_context); | 715 item->handler = KeyToHandler(key, &item->has_valid_io_context); |
573 item->context = reinterpret_cast<IOContext*>(overlapped); | 716 item->context = reinterpret_cast<IOContext*>(overlapped); |
574 return true; | 717 return true; |
575 } | 718 } |
576 | 719 |
577 bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { | 720 bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { |
578 if (reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.context) && | 721 if (reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.context) && |
579 reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.handler)) { | 722 reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.handler)) { |
580 // This is our internal completion. | 723 // This is our internal completion. |
581 DCHECK(!item.bytes_transfered); | 724 DCHECK(!item.bytes_transfered); |
582 InterlockedExchange(&have_work_, 0); | 725 InterlockedExchange(&work_state_, READY); |
583 return true; | 726 return true; |
584 } | 727 } |
585 return false; | 728 return false; |
586 } | 729 } |
587 | 730 |
588 // Returns a completion item that was previously received. | 731 // Returns a completion item that was previously received. |
589 bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { | 732 bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { |
590 DCHECK(!completed_io_.empty()); | 733 DCHECK(!completed_io_.empty()); |
591 for (std::list<IOItem>::iterator it = completed_io_.begin(); | 734 for (std::list<IOItem>::iterator it = completed_io_.begin(); |
592 it != completed_io_.end(); ++it) { | 735 it != completed_io_.end(); ++it) { |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
633 | 776 |
634 // static | 777 // static |
635 MessagePumpForIO::IOHandler* MessagePumpForIO::KeyToHandler( | 778 MessagePumpForIO::IOHandler* MessagePumpForIO::KeyToHandler( |
636 ULONG_PTR key, | 779 ULONG_PTR key, |
637 bool* has_valid_io_context) { | 780 bool* has_valid_io_context) { |
638 *has_valid_io_context = ((key & 1) == 0); | 781 *has_valid_io_context = ((key & 1) == 0); |
639 return reinterpret_cast<IOHandler*>(key & ~static_cast<ULONG_PTR>(1)); | 782 return reinterpret_cast<IOHandler*>(key & ~static_cast<ULONG_PTR>(1)); |
640 } | 783 } |
641 | 784 |
642 } // namespace base | 785 } // namespace base |
OLD | NEW |