| Index: base/message_pump_win.cc
|
| ===================================================================
|
| --- base/message_pump_win.cc (revision 2577)
|
| +++ base/message_pump_win.cc (working copy)
|
| @@ -35,38 +35,6 @@
|
| DestroyWindow(message_hwnd_);
|
| }
|
|
|
| -void MessagePumpWin::WatchObject(HANDLE object, Watcher* watcher) {
|
| - DCHECK(object);
|
| - DCHECK_NE(object, INVALID_HANDLE_VALUE);
|
| -
|
| - std::vector<HANDLE>::iterator it =
|
| - find(objects_.begin(), objects_.end(), object);
|
| - if (watcher) {
|
| - if (it == objects_.end()) {
|
| - static size_t warning_multiple = 1;
|
| - if (objects_.size() >= warning_multiple * MAXIMUM_WAIT_OBJECTS / 2) {
|
| - LOG(INFO) << "More than " << warning_multiple * MAXIMUM_WAIT_OBJECTS / 2
|
| - << " objects being watched";
|
| - // This DCHECK() is an artificial limitation, meant to warn us if we
|
| - // start creating too many objects. It can safely be raised to a higher
|
| - // level, and the program is designed to handle much larger values.
|
| - // Before raising this limit, make sure that there is a very good reason
|
| - // (in your debug testing) to be watching this many objects.
|
| - DCHECK(2 <= warning_multiple);
|
| - ++warning_multiple;
|
| - }
|
| - objects_.push_back(object);
|
| - watchers_.push_back(watcher);
|
| - } else {
|
| - watchers_[it - objects_.begin()] = watcher;
|
| - }
|
| - } else if (it != objects_.end()) {
|
| - std::vector<HANDLE>::difference_type index = it - objects_.begin();
|
| - objects_.erase(it);
|
| - watchers_.erase(watchers_.begin() + index);
|
| - }
|
| -}
|
| -
|
| void MessagePumpWin::AddObserver(Observer* observer) {
|
| observers_.AddObserver(observer);
|
| }
|
| @@ -176,7 +144,7 @@
|
| }
|
|
|
| //-----------------------------------------------------------------------------
|
| -// MessagePumpWin private:
|
| +// MessagePumpWin protected:
|
|
|
| // static
|
| LRESULT CALLBACK MessagePumpWin::WndProcThunk(
|
| @@ -244,8 +212,87 @@
|
| }
|
| }
|
|
|
| -void MessagePumpWin::DoRunLoop() {
|
| - // IF this was just a simple PeekMessage() loop (servicing all passible work
|
| +bool MessagePumpWin::ProcessNextWindowsMessage() {
|
| + MSG msg;
|
| + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
| + return ProcessMessageHelper(msg);
|
| + return false;
|
| +}
|
| +
|
| +bool MessagePumpWin::ProcessMessageHelper(const MSG& msg) {
|
| + if (WM_QUIT == msg.message) {
|
| + // Repost the QUIT message so that it will be retrieved by the primary
|
| + // GetMessage() loop.
|
| + state_->should_quit = true;
|
| + PostQuitMessage(static_cast<int>(msg.wParam));
|
| + return false;
|
| + }
|
| +
|
| + // While running our main message pump, we discard kMsgHaveWork messages.
|
| + if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_)
|
| + return ProcessPumpReplacementMessage();
|
| +
|
| + WillProcessMessage(msg);
|
| +
|
| + if (state_->dispatcher) {
|
| + if (!state_->dispatcher->Dispatch(msg))
|
| + state_->should_quit = true;
|
| + } else {
|
| + TranslateMessage(&msg);
|
| + DispatchMessage(&msg);
|
| + }
|
| +
|
| + DidProcessMessage(msg);
|
| + return true;
|
| +}
|
| +
|
| +bool MessagePumpWin::ProcessPumpReplacementMessage() {
|
| + // When we encounter a kMsgHaveWork message, this method is called to peek
|
| + // and process a replacement message, such as a WM_PAINT or WM_TIMER. The
|
| + // goal is to make the kMsgHaveWork as non-intrusive as possible, even though
|
| + // a continuous stream of such messages are posted. This method carefully
|
| + // peeks a message while there is no chance for a kMsgHaveWork to be pending,
|
| + // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to
|
| + // possibly be posted), and finally dispatches that peeked replacement. Note
|
| + // that the re-post of kMsgHaveWork may be asynchronous to this thread!!
|
| +
|
| + MSG msg;
|
| + bool have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
|
| + DCHECK(!have_message || kMsgHaveWork != msg.message ||
|
| + msg.hwnd != message_hwnd_);
|
| +
|
| + // Since we discarded a kMsgHaveWork message, we must update the flag.
|
| + InterlockedExchange(&have_work_, 0);
|
| +
|
| + // TODO(darin,jar): There is risk of being lost in a sub-pump within the call
|
| + // to ProcessMessageHelper, which could result in no longer getting a
|
| + // kMsgHaveWork message until the next out-of-band call to ScheduleWork.
|
| +
|
| + return have_message && ProcessMessageHelper(msg);
|
| +}
|
| +
|
| +int MessagePumpWin::GetCurrentDelay() const {
|
| + if (delayed_work_time_.is_null())
|
| + return -1;
|
| +
|
| + // Be careful here. TimeDelta has a precision of microseconds, but we want a
|
| + // value in milliseconds. If there are 5.5ms left, should the delay be 5 or
|
| + // 6? It should be 6 to avoid executing delayed work too early.
|
| + double timeout = ceil((delayed_work_time_ - Time::Now()).InMillisecondsF());
|
| +
|
| + // If this value is negative, then we need to run delayed work soon.
|
| + int delay = static_cast<int>(timeout);
|
| + if (delay < 0)
|
| + delay = 0;
|
| +
|
| + return delay;
|
| +}
|
| +
|
| +//-----------------------------------------------------------------------------
|
| +// MessagePumpForUI private:
|
| +
|
| +void MessagePumpForUI::DoRunLoop() {
|
| + // IF this was just a simple PeekMessage() loop (servicing all possible work
|
| // queues), then Windows would try to achieve the following order according
|
| // to MSDN documentation about PeekMessage with no filter):
|
| // * Sent messages
|
| @@ -261,6 +308,118 @@
|
| // If we do any work, we may create more messages etc., and more work may
|
| // possibly be waiting in another task group. When we (for example)
|
| // ProcessNextWindowsMessage(), there is a good chance there are still more
|
| + // messages waiting. On the other hand, when any of these methods return
|
| + // having done no work, then it is pretty unlikely that calling them again
|
| + // quickly will find any work to do. Finally, if they all say they had no
|
| + // work, then it is a good time to consider sleeping (waiting) for more
|
| + // work.
|
| +
|
| + bool more_work_is_plausible = ProcessNextWindowsMessage();
|
| + if (state_->should_quit)
|
| + break;
|
| +
|
| + more_work_is_plausible |= state_->delegate->DoWork();
|
| + if (state_->should_quit)
|
| + break;
|
| +
|
| + more_work_is_plausible |=
|
| + state_->delegate->DoDelayedWork(&delayed_work_time_);
|
| + // If we did not process any delayed work, then we can assume that our
|
| + // existing WM_TIMER if any will fire when delayed work should run. We
|
| + // don't want to disturb that timer if it is already in flight. However,
|
| + // if we did do all remaining delayed work, then lets kill the WM_TIMER.
|
| + if (more_work_is_plausible && delayed_work_time_.is_null())
|
| + KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
|
| + if (state_->should_quit)
|
| + break;
|
| +
|
| + if (more_work_is_plausible)
|
| + continue;
|
| +
|
| + more_work_is_plausible = state_->delegate->DoIdleWork();
|
| + if (state_->should_quit)
|
| + break;
|
| +
|
| + if (more_work_is_plausible)
|
| + continue;
|
| +
|
| + WaitForWork(); // Wait (sleep) until we have work to do again.
|
| + }
|
| +}
|
| +
|
| +void MessagePumpForUI::WaitForWork() {
|
| + // Wait until a message is available, up to the time needed by the timer
|
| + // manager to fire the next set of timers.
|
| + int delay = GetCurrentDelay();
|
| + if (delay < 0) // Negative value means no timers waiting.
|
| + delay = INFINITE;
|
| +
|
| + DWORD result;
|
| + result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT,
|
| + MWMO_INPUTAVAILABLE);
|
| +
|
| + if (WAIT_OBJECT_0 == result)
|
| + return; // A WM_* message is available.
|
| +
|
| + DCHECK_NE(WAIT_FAILED, result) << GetLastError();
|
| +}
|
| +
|
| +//-----------------------------------------------------------------------------
|
| +// MessagePumpForIO public:
|
| +
|
| +void MessagePumpForIO::WatchObject(HANDLE object, Watcher* watcher) {
|
| + DCHECK(object);
|
| + DCHECK_NE(object, INVALID_HANDLE_VALUE);
|
| +
|
| + std::vector<HANDLE>::iterator it =
|
| + find(objects_.begin(), objects_.end(), object);
|
| + if (watcher) {
|
| + if (it == objects_.end()) {
|
| + static size_t warning_multiple = 1;
|
| + if (objects_.size() >= warning_multiple * MAXIMUM_WAIT_OBJECTS / 2) {
|
| + LOG(INFO) << "More than " << warning_multiple * MAXIMUM_WAIT_OBJECTS / 2
|
| + << " objects being watched";
|
| + // This DCHECK() is an artificial limitation, meant to warn us if we
|
| + // start creating too many objects. It can safely be raised to a higher
|
| + // level, and the program is designed to handle much larger values.
|
| + // Before raising this limit, make sure that there is a very good reason
|
| + // (in your debug testing) to be watching this many objects.
|
| + DCHECK(2 <= warning_multiple);
|
| + ++warning_multiple;
|
| + }
|
| + objects_.push_back(object);
|
| + watchers_.push_back(watcher);
|
| + } else {
|
| + watchers_[it - objects_.begin()] = watcher;
|
| + }
|
| + } else if (it != objects_.end()) {
|
| + std::vector<HANDLE>::difference_type index = it - objects_.begin();
|
| + objects_.erase(it);
|
| + watchers_.erase(watchers_.begin() + index);
|
| + }
|
| +}
|
| +
|
| +
|
| +//-----------------------------------------------------------------------------
|
| +// MessagePumpForIO private:
|
| +
|
| +void MessagePumpForIO::DoRunLoop() {
|
| + // IF this was just a simple PeekMessage() loop (servicing all possible work
|
| + // queues), then Windows would try to achieve the following order according
|
| + // to MSDN documentation about PeekMessage with no filter):
|
| + // * Sent messages
|
| + // * Posted messages
|
| + // * Sent messages (again)
|
| + // * WM_PAINT messages
|
| + // * WM_TIMER messages
|
| + //
|
| + // Summary: none of the above classes is starved, and sent messages has twice
|
| + // the chance of being processed (i.e., reduced service time).
|
| +
|
| + for (;;) {
|
| + // If we do any work, we may create more messages etc., and more work may
|
| + // possibly be waiting in another task group. When we (for example)
|
| + // ProcessNextWindowsMessage(), there is a good chance there are still more
|
| // messages waiting (same thing for ProcessNextObject(), which responds to
|
| // only one signaled object; etc.). On the other hand, when any of these
|
| // methods return having done no work, then it is pretty unlikely that
|
| @@ -316,7 +475,7 @@
|
| // open, etc.)
|
| static const int kMultipleWaitPollingInterval = 20;
|
|
|
| -void MessagePumpWin::WaitForWork() {
|
| +void MessagePumpForIO::WaitForWork() {
|
| // Wait until either an object is signaled or a message is available. Handle
|
| // (without returning) any APCs (only the IO thread currently has APCs.)
|
|
|
| @@ -386,68 +545,7 @@
|
| }
|
| }
|
|
|
| -bool MessagePumpWin::ProcessNextWindowsMessage() {
|
| - MSG msg;
|
| - if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
| - return ProcessMessageHelper(msg);
|
| - return false;
|
| -}
|
| -
|
| -bool MessagePumpWin::ProcessMessageHelper(const MSG& msg) {
|
| - if (WM_QUIT == msg.message) {
|
| - // Repost the QUIT message so that it will be retrieved by the primary
|
| - // GetMessage() loop.
|
| - state_->should_quit = true;
|
| - PostQuitMessage(static_cast<int>(msg.wParam));
|
| - return false;
|
| - }
|
| -
|
| - // While running our main message pump, we discard kMsgHaveWork messages.
|
| - if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_)
|
| - return ProcessPumpReplacementMessage();
|
| -
|
| - WillProcessMessage(msg);
|
| -
|
| - if (state_->dispatcher) {
|
| - if (!state_->dispatcher->Dispatch(msg))
|
| - state_->should_quit = true;
|
| - } else {
|
| - TranslateMessage(&msg);
|
| - DispatchMessage(&msg);
|
| - }
|
| -
|
| - DidProcessMessage(msg);
|
| - return true;
|
| -}
|
| -
|
| -bool MessagePumpWin::ProcessPumpReplacementMessage() {
|
| - // When we encounter a kMsgHaveWork message, this method is called to peek
|
| - // and process a replacement message, such as a WM_PAINT or WM_TIMER. The
|
| - // goal is to make the kMsgHaveWork as non-intrusive as possible, even though
|
| - // a continuous stream of such messages are posted. This method carefully
|
| - // peeks a message while there is no chance for a kMsgHaveWork to be pending,
|
| - // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to
|
| - // possibly be posted), and finally dispatches that peeked replacement. Note
|
| - // that the re-post of kMsgHaveWork may be asynchronous to this thread!!
|
| -
|
| - MSG msg;
|
| - bool have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
|
| - DCHECK(!have_message || kMsgHaveWork != msg.message ||
|
| - msg.hwnd != message_hwnd_);
|
| -
|
| - // Since we discarded a kMsgHaveWork message, we must update the flag.
|
| - InterlockedExchange(&have_work_, 0);
|
| -
|
| - // TODO(darin,jar): There is risk of being lost in a sub-pump within the call
|
| - // to ProcessMessageHelper, which could result in no longer getting a
|
| - // kMsgHaveWork message until the next out-of-band call to ScheduleWork.
|
| -
|
| - return have_message && ProcessMessageHelper(msg);
|
| -}
|
| -
|
| -// Note: MsgWaitMultipleObjects() can't take a nil list, and that is why I had
|
| -// to use SleepEx() to handle APCs when there were no objects.
|
| -bool MessagePumpWin::ProcessNextObject() {
|
| +bool MessagePumpForIO::ProcessNextObject() {
|
| size_t total_objs = objects_.size();
|
| if (!total_objs) {
|
| return false;
|
| @@ -481,7 +579,7 @@
|
| return false; // We serviced nothing.
|
| }
|
|
|
| -bool MessagePumpWin::SignalWatcher(size_t object_index) {
|
| +bool MessagePumpForIO::SignalWatcher(size_t object_index) {
|
| // Signal the watcher corresponding to the given index.
|
|
|
| DCHECK(objects_.size() > object_index);
|
| @@ -499,22 +597,4 @@
|
| return true;
|
| }
|
|
|
| -int MessagePumpWin::GetCurrentDelay() const {
|
| - if (delayed_work_time_.is_null())
|
| - return -1;
|
| -
|
| - // Be careful here. TimeDelta has a precision of microseconds, but we want a
|
| - // value in milliseconds. If there are 5.5ms left, should the delay be 5 or
|
| - // 6? It should be 6 to avoid executing delayed work too early.
|
| - double timeout = ceil((delayed_work_time_ - Time::Now()).InMillisecondsF());
|
| -
|
| - // If this value is negative, then we need to run delayed work soon.
|
| - int delay = static_cast<int>(timeout);
|
| - if (delay < 0)
|
| - delay = 0;
|
| -
|
| - return delay;
|
| -}
|
| -
|
| } // namespace base
|
| -
|
|
|