ipc/chromium/src/base/message_pump_win.cc

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/ipc/chromium/src/base/message_pump_win.cc	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,574 @@
     1.4 +// Copyright (c) 2009 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +#include "base/message_pump_win.h"
     1.9 +
    1.10 +#include <math.h>
    1.11 +
    1.12 +#include "base/message_loop.h"
    1.13 +#include "base/histogram.h"
    1.14 +#include "base/win_util.h"
    1.15 +
    1.16 +using base::Time;
    1.17 +
    1.18 +namespace base {
    1.19 +
    1.20 +static const wchar_t kWndClass[] = L"Chrome_MessagePumpWindow";
    1.21 +
    1.22 +// Message sent to get an additional time slice for pumping (processing) another
    1.23 +// task (a series of such messages creates a continuous task pump).
    1.24 +static const int kMsgHaveWork = WM_USER + 1;
    1.25 +
    1.26 +//-----------------------------------------------------------------------------
    1.27 +// MessagePumpWin public:
    1.28 +
    1.29 +void MessagePumpWin::AddObserver(Observer* observer) {
    1.30 +  observers_.AddObserver(observer);
    1.31 +}
    1.32 +
    1.33 +void MessagePumpWin::RemoveObserver(Observer* observer) {
    1.34 +  observers_.RemoveObserver(observer);
    1.35 +}
    1.36 +
    1.37 +void MessagePumpWin::WillProcessMessage(const MSG& msg) {
    1.38 +  FOR_EACH_OBSERVER(Observer, observers_, WillProcessMessage(msg));
    1.39 +}
    1.40 +
    1.41 +void MessagePumpWin::DidProcessMessage(const MSG& msg) {
    1.42 +  FOR_EACH_OBSERVER(Observer, observers_, DidProcessMessage(msg));
    1.43 +}
    1.44 +
    1.45 +void MessagePumpWin::RunWithDispatcher(
    1.46 +    Delegate* delegate, Dispatcher* dispatcher) {
    1.47 +  RunState s;
    1.48 +  s.delegate = delegate;
    1.49 +  s.dispatcher = dispatcher;
    1.50 +  s.should_quit = false;
    1.51 +  s.run_depth = state_ ? state_->run_depth + 1 : 1;
    1.52 +
    1.53 +  RunState* previous_state = state_;
    1.54 +  state_ = &s;
    1.55 +
    1.56 +  DoRunLoop();
    1.57 +
    1.58 +  state_ = previous_state;
    1.59 +}
    1.60 +
    1.61 +void MessagePumpWin::Quit() {
    1.62 +  DCHECK(state_);
    1.63 +  state_->should_quit = true;
    1.64 +}
    1.65 +
    1.66 +//-----------------------------------------------------------------------------
    1.67 +// MessagePumpWin protected:
    1.68 +
    1.69 +int MessagePumpWin::GetCurrentDelay() const {
    1.70 +  if (delayed_work_time_.is_null())
    1.71 +    return -1;
    1.72 +
    1.73 +  // Be careful here.  TimeDelta has a precision of microseconds, but we want a
    1.74 +  // value in milliseconds.  If there are 5.5ms left, should the delay be 5 or
    1.75 +  // 6?  It should be 6 to avoid executing delayed work too early.
    1.76 +  double timeout =
    1.77 +      ceil((delayed_work_time_ - TimeTicks::Now()).InMillisecondsF());
    1.78 +
    1.79 +  // If this value is negative, then we need to run delayed work soon.
    1.80 +  int delay = static_cast<int>(timeout);
    1.81 +  if (delay < 0)
    1.82 +    delay = 0;
    1.83 +
    1.84 +  return delay;
    1.85 +}
    1.86 +
    1.87 +//-----------------------------------------------------------------------------
    1.88 +// MessagePumpForUI public:
    1.89 +
    1.90 +MessagePumpForUI::MessagePumpForUI() {
    1.91 +  InitMessageWnd();
    1.92 +}
    1.93 +
    1.94 +MessagePumpForUI::~MessagePumpForUI() {
    1.95 +  DestroyWindow(message_hwnd_);
    1.96 +  UnregisterClass(kWndClass, GetModuleHandle(NULL));
    1.97 +}
    1.98 +
    1.99 +void MessagePumpForUI::ScheduleWork() {
   1.100 +  if (InterlockedExchange(&have_work_, 1))
   1.101 +    return;  // Someone else continued the pumping.
   1.102 +
   1.103 +  // Make sure the MessagePump does some work for us.
   1.104 +  PostMessage(message_hwnd_, kMsgHaveWork, reinterpret_cast<WPARAM>(this), 0);
   1.105 +
   1.106 +  // In order to wake up any cross-process COM calls which may currently be
   1.107 +  // pending on the main thread, we also have to post a UI message.
   1.108 +  PostMessage(message_hwnd_, WM_NULL, 0, 0);
   1.109 +}
   1.110 +
   1.111 +void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
   1.112 +  //
   1.113 +  // We would *like* to provide high resolution timers.  Windows timers using
   1.114 +  // SetTimer() have a 10ms granularity.  We have to use WM_TIMER as a wakeup
   1.115 +  // mechanism because the application can enter modal windows loops where it
   1.116 +  // is not running our MessageLoop; the only way to have our timers fire in
   1.117 +  // these cases is to post messages there.
   1.118 +  //
   1.119 +  // To provide sub-10ms timers, we process timers directly from our run loop.
   1.120 +  // For the common case, timers will be processed there as the run loop does
   1.121 +  // its normal work.  However, we *also* set the system timer so that WM_TIMER
   1.122 +  // events fire.  This mops up the case of timers not being able to work in
   1.123 +  // modal message loops.  It is possible for the SetTimer to pop and have no
   1.124 +  // pending timers, because they could have already been processed by the
   1.125 +  // run loop itself.
   1.126 +  //
   1.127 +  // We use a single SetTimer corresponding to the timer that will expire
   1.128 +  // soonest.  As new timers are created and destroyed, we update SetTimer.
   1.129 +  // Getting a spurrious SetTimer event firing is benign, as we'll just be
   1.130 +  // processing an empty timer queue.
   1.131 +  //
   1.132 +  delayed_work_time_ = delayed_work_time;
   1.133 +
   1.134 +  int delay_msec = GetCurrentDelay();
   1.135 +  DCHECK(delay_msec >= 0);
   1.136 +  if (delay_msec < USER_TIMER_MINIMUM)
   1.137 +    delay_msec = USER_TIMER_MINIMUM;
   1.138 +
   1.139 +  // Create a WM_TIMER event that will wake us up to check for any pending
   1.140 +  // timers (in case we are running within a nested, external sub-pump).
   1.141 +  SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), delay_msec, NULL);
   1.142 +}
   1.143 +
   1.144 +void MessagePumpForUI::PumpOutPendingPaintMessages() {
   1.145 +  // If we are being called outside of the context of Run, then don't try to do
   1.146 +  // any work.
   1.147 +  if (!state_)
   1.148 +    return;
   1.149 +
   1.150 +  // Create a mini-message-pump to force immediate processing of only Windows
   1.151 +  // WM_PAINT messages.  Don't provide an infinite loop, but do enough peeking
   1.152 +  // to get the job done.  Actual common max is 4 peeks, but we'll be a little
   1.153 +  // safe here.
   1.154 +  const int kMaxPeekCount = 20;
   1.155 +  bool win2k = win_util::GetWinVersion() <= win_util::WINVERSION_2000;
   1.156 +  int peek_count;
   1.157 +  for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) {
   1.158 +    MSG msg;
   1.159 +    if (win2k) {
   1.160 +      if (!PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE))
   1.161 +        break;
   1.162 +    } else {
   1.163 +      if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT))
   1.164 +        break;
   1.165 +    }
   1.166 +    ProcessMessageHelper(msg);
   1.167 +    if (state_->should_quit)  // Handle WM_QUIT.
   1.168 +      break;
   1.169 +  }
   1.170 +  // Histogram what was really being used, to help to adjust kMaxPeekCount.
   1.171 +  DHISTOGRAM_COUNTS("Loop.PumpOutPendingPaintMessages Peeks", peek_count);
   1.172 +}
   1.173 +
   1.174 +//-----------------------------------------------------------------------------
   1.175 +// MessagePumpForUI private:
   1.176 +
   1.177 +// static
   1.178 +LRESULT CALLBACK MessagePumpForUI::WndProcThunk(
   1.179 +    HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
   1.180 +  switch (message) {
   1.181 +    case kMsgHaveWork:
   1.182 +      reinterpret_cast<MessagePumpForUI*>(wparam)->HandleWorkMessage();
   1.183 +      break;
   1.184 +    case WM_TIMER:
   1.185 +      reinterpret_cast<MessagePumpForUI*>(wparam)->HandleTimerMessage();
   1.186 +      break;
   1.187 +  }
   1.188 +  return DefWindowProc(hwnd, message, wparam, lparam);
   1.189 +}
   1.190 +
   1.191 +void MessagePumpForUI::DoRunLoop() {
   1.192 +  // IF this was just a simple PeekMessage() loop (servicing all possible work
   1.193 +  // queues), then Windows would try to achieve the following order according
   1.194 +  // to MSDN documentation about PeekMessage with no filter):
   1.195 +  //    * Sent messages
   1.196 +  //    * Posted messages
   1.197 +  //    * Sent messages (again)
   1.198 +  //    * WM_PAINT messages
   1.199 +  //    * WM_TIMER messages
   1.200 +  //
   1.201 +  // Summary: none of the above classes is starved, and sent messages has twice
   1.202 +  // the chance of being processed (i.e., reduced service time).
   1.203 +
   1.204 +  for (;;) {
   1.205 +    // If we do any work, we may create more messages etc., and more work may
   1.206 +    // possibly be waiting in another task group.  When we (for example)
   1.207 +    // ProcessNextWindowsMessage(), there is a good chance there are still more
   1.208 +    // messages waiting.  On the other hand, when any of these methods return
   1.209 +    // having done no work, then it is pretty unlikely that calling them again
   1.210 +    // quickly will find any work to do.  Finally, if they all say they had no
   1.211 +    // work, then it is a good time to consider sleeping (waiting) for more
   1.212 +    // work.
   1.213 +
   1.214 +    bool more_work_is_plausible = ProcessNextWindowsMessage();
   1.215 +    if (state_->should_quit)
   1.216 +      break;
   1.217 +
   1.218 +    more_work_is_plausible |= state_->delegate->DoWork();
   1.219 +    if (state_->should_quit)
   1.220 +      break;
   1.221 +
   1.222 +    more_work_is_plausible |=
   1.223 +        state_->delegate->DoDelayedWork(&delayed_work_time_);
   1.224 +    // If we did not process any delayed work, then we can assume that our
   1.225 +    // existing WM_TIMER if any will fire when delayed work should run.  We
   1.226 +    // don't want to disturb that timer if it is already in flight.  However,
   1.227 +    // if we did do all remaining delayed work, then lets kill the WM_TIMER.
   1.228 +    if (more_work_is_plausible && delayed_work_time_.is_null())
   1.229 +      KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
   1.230 +    if (state_->should_quit)
   1.231 +      break;
   1.232 +
   1.233 +    if (more_work_is_plausible)
   1.234 +      continue;
   1.235 +
   1.236 +    more_work_is_plausible = state_->delegate->DoIdleWork();
   1.237 +    if (state_->should_quit)
   1.238 +      break;
   1.239 +
   1.240 +    if (more_work_is_plausible)
   1.241 +      continue;
   1.242 +
   1.243 +    WaitForWork();  // Wait (sleep) until we have work to do again.
   1.244 +  }
   1.245 +}
   1.246 +
   1.247 +void MessagePumpForUI::InitMessageWnd() {
   1.248 +  HINSTANCE hinst = GetModuleHandle(NULL);
   1.249 +
   1.250 +  WNDCLASSEX wc = {0};
   1.251 +  wc.cbSize = sizeof(wc);
   1.252 +  wc.lpfnWndProc = WndProcThunk;
   1.253 +  wc.hInstance = hinst;
   1.254 +  wc.lpszClassName = kWndClass;
   1.255 +  RegisterClassEx(&wc);
   1.256 +
   1.257 +  message_hwnd_ =
   1.258 +      CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, 0);
   1.259 +  DCHECK(message_hwnd_);
   1.260 +}
   1.261 +
   1.262 +void MessagePumpForUI::WaitForWork() {
   1.263 +  // Wait until a message is available, up to the time needed by the timer
   1.264 +  // manager to fire the next set of timers.
   1.265 +  int delay = GetCurrentDelay();
   1.266 +  if (delay < 0)  // Negative value means no timers waiting.
   1.267 +    delay = INFINITE;
   1.268 +
   1.269 +  DWORD result;
   1.270 +  result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT,
   1.271 +                                       MWMO_INPUTAVAILABLE);
   1.272 +
   1.273 +  if (WAIT_OBJECT_0 == result) {
   1.274 +    // A WM_* message is available.
   1.275 +    // If a parent child relationship exists between windows across threads
   1.276 +    // then their thread inputs are implicitly attached.
   1.277 +    // This causes the MsgWaitForMultipleObjectsEx API to return indicating
   1.278 +    // that messages are ready for processing (specifically mouse messages
   1.279 +    // intended for the child window. Occurs if the child window has capture)
   1.280 +    // The subsequent PeekMessages call fails to return any messages thus
   1.281 +    // causing us to enter a tight loop at times.
   1.282 +    // The WaitMessage call below is a workaround to give the child window
   1.283 +    // sometime to process its input messages.
   1.284 +    MSG msg = {0};
   1.285 +    DWORD queue_status = GetQueueStatus(QS_MOUSE);
   1.286 +    if (HIWORD(queue_status) & QS_MOUSE &&
   1.287 +       !PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE)) {
   1.288 +      WaitMessage();
   1.289 +    }
   1.290 +    return;
   1.291 +  }
   1.292 +
   1.293 +  DCHECK_NE(WAIT_FAILED, result) << GetLastError();
   1.294 +}
   1.295 +
   1.296 +void MessagePumpForUI::HandleWorkMessage() {
   1.297 +  // If we are being called outside of the context of Run, then don't try to do
   1.298 +  // any work.  This could correspond to a MessageBox call or something of that
   1.299 +  // sort.
   1.300 +  if (!state_) {
   1.301 +    // Since we handled a kMsgHaveWork message, we must still update this flag.
   1.302 +    InterlockedExchange(&have_work_, 0);
   1.303 +    return;
   1.304 +  }
   1.305 +
   1.306 +  // Let whatever would have run had we not been putting messages in the queue
   1.307 +  // run now.  This is an attempt to make our dummy message not starve other
   1.308 +  // messages that may be in the Windows message queue.
   1.309 +  ProcessPumpReplacementMessage();
   1.310 +
   1.311 +  // Now give the delegate a chance to do some work.  He'll let us know if he
   1.312 +  // needs to do more work.
   1.313 +  if (state_->delegate->DoWork())
   1.314 +    ScheduleWork();
   1.315 +}
   1.316 +
   1.317 +void MessagePumpForUI::HandleTimerMessage() {
   1.318 +  KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
   1.319 +
   1.320 +  // If we are being called outside of the context of Run, then don't do
   1.321 +  // anything.  This could correspond to a MessageBox call or something of
   1.322 +  // that sort.
   1.323 +  if (!state_)
   1.324 +    return;
   1.325 +
   1.326 +  state_->delegate->DoDelayedWork(&delayed_work_time_);
   1.327 +  if (!delayed_work_time_.is_null()) {
   1.328 +    // A bit gratuitous to set delayed_work_time_ again, but oh well.
   1.329 +    ScheduleDelayedWork(delayed_work_time_);
   1.330 +  }
   1.331 +}
   1.332 +
   1.333 +bool MessagePumpForUI::ProcessNextWindowsMessage() {
   1.334 +  // If there are sent messages in the queue then PeekMessage internally
   1.335 +  // dispatches the message and returns false. We return true in this
   1.336 +  // case to ensure that the message loop peeks again instead of calling
   1.337 +  // MsgWaitForMultipleObjectsEx again.
   1.338 +  bool sent_messages_in_queue = false;
   1.339 +  DWORD queue_status = GetQueueStatus(QS_SENDMESSAGE);
   1.340 +  if (HIWORD(queue_status) & QS_SENDMESSAGE)
   1.341 +    sent_messages_in_queue = true;
   1.342 +
   1.343 +  MSG msg;
   1.344 +  if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
   1.345 +    return ProcessMessageHelper(msg);
   1.346 +
   1.347 +  return sent_messages_in_queue;
   1.348 +}
   1.349 +
   1.350 +bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) {
   1.351 +  if (WM_QUIT == msg.message) {
   1.352 +    // Repost the QUIT message so that it will be retrieved by the primary
   1.353 +    // GetMessage() loop.
   1.354 +    state_->should_quit = true;
   1.355 +    PostQuitMessage(static_cast<int>(msg.wParam));
   1.356 +    return false;
   1.357 +  }
   1.358 +
   1.359 +  // While running our main message pump, we discard kMsgHaveWork messages.
   1.360 +  if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_)
   1.361 +    return ProcessPumpReplacementMessage();
   1.362 +
   1.363 +  WillProcessMessage(msg);
   1.364 +
   1.365 +  if (state_->dispatcher) {
   1.366 +    if (!state_->dispatcher->Dispatch(msg))
   1.367 +      state_->should_quit = true;
   1.368 +  } else {
   1.369 +    TranslateMessage(&msg);
   1.370 +    DispatchMessage(&msg);
   1.371 +  }
   1.372 +
   1.373 +  DidProcessMessage(msg);
   1.374 +  return true;
   1.375 +}
   1.376 +
   1.377 +bool MessagePumpForUI::ProcessPumpReplacementMessage() {
   1.378 +  // When we encounter a kMsgHaveWork message, this method is called to peek
   1.379 +  // and process a replacement message, such as a WM_PAINT or WM_TIMER.  The
   1.380 +  // goal is to make the kMsgHaveWork as non-intrusive as possible, even though
   1.381 +  // a continuous stream of such messages are posted.  This method carefully
   1.382 +  // peeks a message while there is no chance for a kMsgHaveWork to be pending,
   1.383 +  // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to
   1.384 +  // possibly be posted), and finally dispatches that peeked replacement.  Note
   1.385 +  // that the re-post of kMsgHaveWork may be asynchronous to this thread!!
   1.386 +
   1.387 +  MSG msg;
   1.388 +  bool have_message = false;
   1.389 +  if (MessageLoop::current()->os_modal_loop()) {
   1.390 +    // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above.
   1.391 +    have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) ||
   1.392 +                   PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE);
   1.393 +  } else {
   1.394 +    have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
   1.395 +
   1.396 +    if (have_message && msg.message == WM_NULL)
   1.397 +      have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
   1.398 +  }
   1.399 +
   1.400 +  DCHECK(!have_message || kMsgHaveWork != msg.message ||
   1.401 +         msg.hwnd != message_hwnd_);
   1.402 +
   1.403 +  // Since we discarded a kMsgHaveWork message, we must update the flag.
   1.404 +  int old_have_work = InterlockedExchange(&have_work_, 0);
   1.405 +  DCHECK(old_have_work);
   1.406 +
   1.407 +  // We don't need a special time slice if we didn't have_message to process.
   1.408 +  if (!have_message)
   1.409 +    return false;
   1.410 +
   1.411 +  // Guarantee we'll get another time slice in the case where we go into native
   1.412 +  // windows code.   This ScheduleWork() may hurt performance a tiny bit when
   1.413 +  // tasks appear very infrequently, but when the event queue is busy, the
   1.414 +  // kMsgHaveWork events get (percentage wise) rarer and rarer.
   1.415 +  ScheduleWork();
   1.416 +  return ProcessMessageHelper(msg);
   1.417 +}
   1.418 +
   1.419 +//-----------------------------------------------------------------------------
   1.420 +// MessagePumpForIO public:
   1.421 +
   1.422 +MessagePumpForIO::MessagePumpForIO() {
   1.423 +  port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1));
   1.424 +  DCHECK(port_.IsValid());
   1.425 +}
   1.426 +
   1.427 +void MessagePumpForIO::ScheduleWork() {
   1.428 +  if (InterlockedExchange(&have_work_, 1))
   1.429 +    return;  // Someone else continued the pumping.
   1.430 +
   1.431 +  // Make sure the MessagePump does some work for us.
   1.432 +  BOOL ret = PostQueuedCompletionStatus(port_, 0,
   1.433 +                                        reinterpret_cast<ULONG_PTR>(this),
   1.434 +                                        reinterpret_cast<OVERLAPPED*>(this));
   1.435 +  DCHECK(ret);
   1.436 +}
   1.437 +
   1.438 +void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
   1.439 +  // We know that we can't be blocked right now since this method can only be
   1.440 +  // called on the same thread as Run, so we only need to update our record of
   1.441 +  // how long to sleep when we do sleep.
   1.442 +  delayed_work_time_ = delayed_work_time;
   1.443 +}
   1.444 +
   1.445 +void MessagePumpForIO::RegisterIOHandler(HANDLE file_handle,
   1.446 +                                         IOHandler* handler) {
   1.447 +  ULONG_PTR key = reinterpret_cast<ULONG_PTR>(handler);
   1.448 +  HANDLE port = CreateIoCompletionPort(file_handle, port_, key, 1);
   1.449 +  DCHECK(port == port_.Get());
   1.450 +}
   1.451 +
   1.452 +//-----------------------------------------------------------------------------
   1.453 +// MessagePumpForIO private:
   1.454 +
   1.455 +void MessagePumpForIO::DoRunLoop() {
   1.456 +  for (;;) {
   1.457 +    // If we do any work, we may create more messages etc., and more work may
   1.458 +    // possibly be waiting in another task group.  When we (for example)
   1.459 +    // WaitForIOCompletion(), there is a good chance there are still more
   1.460 +    // messages waiting.  On the other hand, when any of these methods return
   1.461 +    // having done no work, then it is pretty unlikely that calling them
   1.462 +    // again quickly will find any work to do.  Finally, if they all say they
   1.463 +    // had no work, then it is a good time to consider sleeping (waiting) for
   1.464 +    // more work.
   1.465 +
   1.466 +    bool more_work_is_plausible = state_->delegate->DoWork();
   1.467 +    if (state_->should_quit)
   1.468 +      break;
   1.469 +
   1.470 +    more_work_is_plausible |= WaitForIOCompletion(0, NULL);
   1.471 +    if (state_->should_quit)
   1.472 +      break;
   1.473 +
   1.474 +    more_work_is_plausible |=
   1.475 +        state_->delegate->DoDelayedWork(&delayed_work_time_);
   1.476 +    if (state_->should_quit)
   1.477 +      break;
   1.478 +
   1.479 +    if (more_work_is_plausible)
   1.480 +      continue;
   1.481 +
   1.482 +    more_work_is_plausible = state_->delegate->DoIdleWork();
   1.483 +    if (state_->should_quit)
   1.484 +      break;
   1.485 +
   1.486 +    if (more_work_is_plausible)
   1.487 +      continue;
   1.488 +
   1.489 +    WaitForWork();  // Wait (sleep) until we have work to do again.
   1.490 +  }
   1.491 +}
   1.492 +
   1.493 +// Wait until IO completes, up to the time needed by the timer manager to fire
   1.494 +// the next set of timers.
   1.495 +void MessagePumpForIO::WaitForWork() {
   1.496 +  // We do not support nested IO message loops. This is to avoid messy
   1.497 +  // recursion problems.
   1.498 +  DCHECK(state_->run_depth == 1) << "Cannot nest an IO message loop!";
   1.499 +
   1.500 +  int timeout = GetCurrentDelay();
   1.501 +  if (timeout < 0)  // Negative value means no timers waiting.
   1.502 +    timeout = INFINITE;
   1.503 +
   1.504 +  WaitForIOCompletion(timeout, NULL);
   1.505 +}
   1.506 +
   1.507 +bool MessagePumpForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
   1.508 +  IOItem item;
   1.509 +  if (completed_io_.empty() || !MatchCompletedIOItem(filter, &item)) {
   1.510 +    // We have to ask the system for another IO completion.
   1.511 +    if (!GetIOItem(timeout, &item))
   1.512 +      return false;
   1.513 +
   1.514 +    if (ProcessInternalIOItem(item))
   1.515 +      return true;
   1.516 +  }
   1.517 +
   1.518 +  if (item.context->handler) {
   1.519 +    if (filter && item.handler != filter) {
   1.520 +      // Save this item for later
   1.521 +      completed_io_.push_back(item);
   1.522 +    } else {
   1.523 +      DCHECK(item.context->handler == item.handler);
   1.524 +      item.handler->OnIOCompleted(item.context, item.bytes_transfered,
   1.525 +                                  item.error);
   1.526 +    }
   1.527 +  } else {
   1.528 +    // The handler must be gone by now, just cleanup the mess.
   1.529 +    delete item.context;
   1.530 +  }
   1.531 +  return true;
   1.532 +}
   1.533 +
   1.534 +// Asks the OS for another IO completion result.
   1.535 +bool MessagePumpForIO::GetIOItem(DWORD timeout, IOItem* item) {
   1.536 +  memset(item, 0, sizeof(*item));
   1.537 +  ULONG_PTR key = 0;
   1.538 +  OVERLAPPED* overlapped = NULL;
   1.539 +  if (!GetQueuedCompletionStatus(port_.Get(), &item->bytes_transfered, &key,
   1.540 +                                 &overlapped, timeout)) {
   1.541 +    if (!overlapped)
   1.542 +      return false;  // Nothing in the queue.
   1.543 +    item->error = GetLastError();
   1.544 +    item->bytes_transfered = 0;
   1.545 +  }
   1.546 +
   1.547 +  item->handler = reinterpret_cast<IOHandler*>(key);
   1.548 +  item->context = reinterpret_cast<IOContext*>(overlapped);
   1.549 +  return true;
   1.550 +}
   1.551 +
   1.552 +bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) {
   1.553 +  if (this == reinterpret_cast<MessagePumpForIO*>(item.context) &&
   1.554 +      this == reinterpret_cast<MessagePumpForIO*>(item.handler)) {
   1.555 +    // This is our internal completion.
   1.556 +    DCHECK(!item.bytes_transfered);
   1.557 +    InterlockedExchange(&have_work_, 0);
   1.558 +    return true;
   1.559 +  }
   1.560 +  return false;
   1.561 +}
   1.562 +
   1.563 +// Returns a completion item that was previously received.
   1.564 +bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) {
   1.565 +  DCHECK(!completed_io_.empty());
   1.566 +  for (std::list<IOItem>::iterator it = completed_io_.begin();
   1.567 +       it != completed_io_.end(); ++it) {
   1.568 +    if (!filter || it->handler == filter) {
   1.569 +      *item = *it;
   1.570 +      completed_io_.erase(it);
   1.571 +      return true;
   1.572 +    }
   1.573 +  }
   1.574 +  return false;
   1.575 +}
   1.576 +
   1.577 +}  // namespace base

mercurial