tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

message_pump_win.cc (20344B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
      4 // Use of this source code is governed by a BSD-style license that can be
      5 // found in the LICENSE file.
      6 
      7 #include "base/message_pump_win.h"
      8 
      9 #include <math.h>
     10 
     11 #include "base/message_loop.h"
     12 #include "base/histogram.h"
     13 #include "base/win_util.h"
     14 #include "mozilla/Maybe.h"
     15 #include "mozilla/ProfilerLabels.h"
     16 #include "mozilla/ProfilerThreadSleep.h"
     17 #include "WinUtils.h"
     18 
     19 using base::Time;
     20 
     21 namespace base {
     22 
     23 static const wchar_t kWndClass[] = L"Chrome_MessagePumpWindow";
     24 
     25 // Message sent to get an additional time slice for pumping (processing) another
     26 // task (a series of such messages creates a continuous task pump).
     27 static const int kMsgHaveWork = WM_USER + 1;
     28 
     29 //-----------------------------------------------------------------------------
     30 // MessagePumpWin public:
     31 
     32 void MessagePumpWin::AddObserver(Observer* observer) {
     33  observers_.AddObserver(observer);
     34 }
     35 
     36 void MessagePumpWin::RemoveObserver(Observer* observer) {
     37  observers_.RemoveObserver(observer);
     38 }
     39 
     40 void MessagePumpWin::WillProcessMessage(const MSG& msg) {
     41  FOR_EACH_OBSERVER(Observer, observers_, WillProcessMessage(msg));
     42 }
     43 
     44 void MessagePumpWin::DidProcessMessage(const MSG& msg) {
     45  FOR_EACH_OBSERVER(Observer, observers_, DidProcessMessage(msg));
     46 }
     47 
     48 void MessagePumpWin::RunWithDispatcher(Delegate* delegate,
     49                                       Dispatcher* dispatcher) {
     50  RunState s;
     51  s.delegate = delegate;
     52  s.dispatcher = dispatcher;
     53  s.should_quit = false;
     54  s.run_depth = state_ ? state_->run_depth + 1 : 1;
     55 
     56  RunState* previous_state = state_;
     57  state_ = &s;
     58 
     59  DoRunLoop();
     60 
     61  state_ = previous_state;
     62 }
     63 
     64 void MessagePumpWin::Quit() {
     65  DCHECK(state_);
     66  state_->should_quit = true;
     67 }
     68 
     69 //-----------------------------------------------------------------------------
     70 // MessagePumpWin protected:
     71 
     72 int MessagePumpWin::GetCurrentDelay() const {
     73  if (delayed_work_time_.is_null()) return -1;
     74 
     75  // Be careful here.  TimeDelta has a precision of microseconds, but we want a
     76  // value in milliseconds.  If there are 5.5ms left, should the delay be 5 or
     77  // 6?  It should be 6 to avoid executing delayed work too early.
     78  double timeout =
     79      ceil((delayed_work_time_ - TimeTicks::Now()).InMillisecondsF());
     80 
     81  // If this value is negative, then we need to run delayed work soon.
     82  int delay = static_cast<int>(timeout);
     83  if (delay < 0) delay = 0;
     84 
     85  return delay;
     86 }
     87 
     88 //-----------------------------------------------------------------------------
     89 // MessagePumpForUI public:
     90 
     91 MessagePumpForUI::MessagePumpForUI() { InitMessageWnd(); }
     92 
     93 MessagePumpForUI::~MessagePumpForUI() {
     94  DestroyWindow(message_hwnd_);
     95  UnregisterClass(kWndClass, GetModuleHandle(NULL));
     96 }
     97 
     98 void MessagePumpForUI::ScheduleWork() {
     99  if (InterlockedExchange(&have_work_, 1))
    100    return;  // Someone else continued the pumping.
    101 
    102  // Make sure the MessagePump does some work for us.
    103  PostMessage(message_hwnd_, kMsgHaveWork, reinterpret_cast<WPARAM>(this), 0);
    104 
    105  // In order to wake up any cross-process COM calls which may currently be
    106  // pending on the main thread, we also have to post a UI message.
    107  PostMessage(message_hwnd_, WM_NULL, 0, 0);
    108 }
    109 
    110 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
    111  //
    112  // We would *like* to provide high resolution timers.  Windows timers using
    113  // SetTimer() have a 10ms granularity.  We have to use WM_TIMER as a wakeup
    114  // mechanism because the application can enter modal windows loops where it
    115  // is not running our MessageLoop; the only way to have our timers fire in
    116  // these cases is to post messages there.
    117  //
    118  // To provide sub-10ms timers, we process timers directly from our run loop.
    119  // For the common case, timers will be processed there as the run loop does
    120  // its normal work.  However, we *also* set the system timer so that WM_TIMER
    121  // events fire.  This mops up the case of timers not being able to work in
    122  // modal message loops.  It is possible for the SetTimer to pop and have no
    123  // pending timers, because they could have already been processed by the
    124  // run loop itself.
    125  //
    126  // We use a single SetTimer corresponding to the timer that will expire
    127  // soonest.  As new timers are created and destroyed, we update SetTimer.
    128  // Getting a spurrious SetTimer event firing is benign, as we'll just be
    129  // processing an empty timer queue.
    130  //
    131  delayed_work_time_ = delayed_work_time;
    132 
    133  int delay_msec = GetCurrentDelay();
    134  DCHECK(delay_msec >= 0);
    135  if (delay_msec < USER_TIMER_MINIMUM) delay_msec = USER_TIMER_MINIMUM;
    136 
    137  // Create a WM_TIMER event that will wake us up to check for any pending
    138  // timers (in case we are running within a nested, external sub-pump).
    139  SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), delay_msec, NULL);
    140 }
    141 
    142 void MessagePumpForUI::PumpOutPendingPaintMessages() {
    143  // If we are being called outside of the context of Run, then don't try to do
    144  // any work.
    145  if (!state_) return;
    146 
    147  // Create a mini-message-pump to force immediate processing of only Windows
    148  // WM_PAINT messages.  Don't provide an infinite loop, but do enough peeking
    149  // to get the job done.  Actual common max is 4 peeks, but we'll be a little
    150  // safe here.
    151  const int kMaxPeekCount = 20;
    152  int peek_count;
    153  for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) {
    154    MSG msg;
    155    if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT)) break;
    156    ProcessMessageHelper(msg);
    157    if (state_->should_quit) break;
    158  }
    159 }
    160 
    161 //-----------------------------------------------------------------------------
    162 // MessagePumpForUI private:
    163 
    164 // static
    165 LRESULT CALLBACK MessagePumpForUI::WndProcThunk(HWND hwnd, UINT message,
    166                                                WPARAM wparam, LPARAM lparam) {
    167  switch (message) {
    168    case kMsgHaveWork:
    169      reinterpret_cast<MessagePumpForUI*>(wparam)->HandleWorkMessage();
    170      break;
    171    case WM_TIMER:
    172      reinterpret_cast<MessagePumpForUI*>(wparam)->HandleTimerMessage();
    173      break;
    174  }
    175  return DefWindowProc(hwnd, message, wparam, lparam);
    176 }
    177 
    178 void MessagePumpForUI::DoRunLoop() {
    179  // IF this was just a simple PeekMessage() loop (servicing all possible work
    180  // queues), then Windows would try to achieve the following order according
    181  // to MSDN documentation about PeekMessage with no filter):
    182  //    * Sent messages
    183  //    * Posted messages
    184  //    * Sent messages (again)
    185  //    * WM_PAINT messages
    186  //    * WM_TIMER messages
    187  //
    188  // Summary: none of the above classes is starved, and sent messages has twice
    189  // the chance of being processed (i.e., reduced service time).
    190 
    191  for (;;) {
    192    // If we do any work, we may create more messages etc., and more work may
    193    // possibly be waiting in another task group.  When we (for example)
    194    // ProcessNextWindowsMessage(), there is a good chance there are still more
    195    // messages waiting.  On the other hand, when any of these methods return
    196    // having done no work, then it is pretty unlikely that calling them again
    197    // quickly will find any work to do.  Finally, if they all say they had no
    198    // work, then it is a good time to consider sleeping (waiting) for more
    199    // work.
    200 
    201    bool more_work_is_plausible = ProcessNextWindowsMessage();
    202    if (state_->should_quit) break;
    203 
    204    more_work_is_plausible |= state_->delegate->DoWork();
    205    if (state_->should_quit) break;
    206 
    207    more_work_is_plausible |=
    208        state_->delegate->DoDelayedWork(&delayed_work_time_);
    209    // If we did not process any delayed work, then we can assume that our
    210    // existing WM_TIMER if any will fire when delayed work should run.  We
    211    // don't want to disturb that timer if it is already in flight.  However,
    212    // if we did do all remaining delayed work, then lets kill the WM_TIMER.
    213    if (more_work_is_plausible && delayed_work_time_.is_null())
    214      KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
    215    if (state_->should_quit) break;
    216 
    217    if (more_work_is_plausible) continue;
    218 
    219    more_work_is_plausible = state_->delegate->DoIdleWork();
    220    if (state_->should_quit) break;
    221 
    222    if (more_work_is_plausible) continue;
    223 
    224    WaitForWork();  // Wait (sleep) until we have work to do again.
    225  }
    226 }
    227 
    228 void MessagePumpForUI::InitMessageWnd() {
    229  HINSTANCE hinst = GetModuleHandle(NULL);
    230 
    231  WNDCLASSEX wc = {0};
    232  wc.cbSize = sizeof(wc);
    233  wc.lpfnWndProc = WndProcThunk;
    234  wc.hInstance = hinst;
    235  wc.lpszClassName = kWndClass;
    236  RegisterClassEx(&wc);
    237 
    238  message_hwnd_ =
    239      CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, 0);
    240  DCHECK(message_hwnd_);
    241 }
    242 
    243 void MessagePumpForUI::WaitForWork() {
    244  AUTO_PROFILER_LABEL("MessagePumpForUI::WaitForWork", IDLE);
    245 
    246  // Wait until a message is available, up to the time needed by the timer
    247  // manager to fire the next set of timers.
    248  int delay = GetCurrentDelay();
    249  if (delay < 0)  // Negative value means no timers waiting.
    250    delay = INFINITE;
    251 
    252  mozilla::widget::WinUtils::WaitForMessage(delay);
    253 }
    254 
    255 void MessagePumpForUI::HandleWorkMessage() {
    256  // If we are being called outside of the context of Run, then don't try to do
    257  // any work.  This could correspond to a MessageBox call or something of that
    258  // sort.
    259  if (!state_) {
    260    // Since we handled a kMsgHaveWork message, we must still update this flag.
    261    InterlockedExchange(&have_work_, 0);
    262    return;
    263  }
    264 
    265  // Let whatever would have run had we not been putting messages in the queue
    266  // run now.  This is an attempt to make our dummy message not starve other
    267  // messages that may be in the Windows message queue.
    268  ProcessPumpReplacementMessage();
    269 
    270  // Now give the delegate a chance to do some work.  He'll let us know if he
    271  // needs to do more work.
    272  if (state_->delegate->DoWork()) ScheduleWork();
    273 }
    274 
    275 void MessagePumpForUI::HandleTimerMessage() {
    276  KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
    277 
    278  // If we are being called outside of the context of Run, then don't do
    279  // anything.  This could correspond to a MessageBox call or something of
    280  // that sort.
    281  if (!state_) return;
    282 
    283  state_->delegate->DoDelayedWork(&delayed_work_time_);
    284  if (!delayed_work_time_.is_null()) {
    285    // A bit gratuitous to set delayed_work_time_ again, but oh well.
    286    ScheduleDelayedWork(delayed_work_time_);
    287  }
    288 }
    289 
    290 bool MessagePumpForUI::ProcessNextWindowsMessage() {
    291  // If there are sent messages in the queue then PeekMessage internally
    292  // dispatches the message and returns false. We return true in this
    293  // case to ensure that the message loop peeks again instead of calling
    294  // MsgWaitForMultipleObjectsEx again.
    295  bool sent_messages_in_queue = false;
    296  DWORD queue_status = ::GetQueueStatus(QS_SENDMESSAGE);
    297  if (HIWORD(queue_status) & QS_SENDMESSAGE) sent_messages_in_queue = true;
    298 
    299  MSG msg;
    300  if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    301    return ProcessMessageHelper(msg);
    302 
    303  return sent_messages_in_queue;
    304 }
    305 
    306 bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) {
    307  if (WM_QUIT == msg.message) {
    308    // WM_QUIT is the standard way to exit a ::GetMessage() loop. Our
    309    // MessageLoop has its own quit mechanism, so WM_QUIT is unexpected and
    310    // should be ignored.
    311    return true;
    312  }
    313 
    314  // While running our main message pump, we discard kMsgHaveWork messages.
    315  if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_)
    316    return ProcessPumpReplacementMessage();
    317 
    318  WillProcessMessage(msg);
    319 
    320  if (state_->dispatcher) {
    321    if (!state_->dispatcher->Dispatch(msg)) state_->should_quit = true;
    322  } else {
    323    ::TranslateMessage(&msg);
    324    ::DispatchMessage(&msg);
    325  }
    326 
    327  DidProcessMessage(msg);
    328  return true;
    329 }
    330 
    331 bool MessagePumpForUI::ProcessPumpReplacementMessage() {
    332  // When we encounter a kMsgHaveWork message, this method is called to peek
    333  // and process a replacement message, such as a WM_PAINT or WM_TIMER.  The
    334  // goal is to make the kMsgHaveWork as non-intrusive as possible, even though
    335  // a continuous stream of such messages are posted.  This method carefully
    336  // peeks a message while there is no chance for a kMsgHaveWork to be pending,
    337  // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to
    338  // possibly be posted), and finally dispatches that peeked replacement.  Note
    339  // that the re-post of kMsgHaveWork may be asynchronous to this thread!!
    340 
    341  MSG msg;
    342  bool have_message = false;
    343  if (MessageLoop::current()->os_modal_loop()) {
    344    // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above.
    345    have_message = ::PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) ||
    346                   ::PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE);
    347  } else {
    348    have_message = (0 != ::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
    349 
    350    if (have_message && msg.message == WM_NULL)
    351      have_message = (0 != ::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
    352  }
    353 
    354  DCHECK(!have_message || kMsgHaveWork != msg.message ||
    355         msg.hwnd != message_hwnd_);
    356 
    357  // Since we discarded a kMsgHaveWork message, we must update the flag.
    358  int old_have_work = InterlockedExchange(&have_work_, 0);
    359  DCHECK(old_have_work);
    360 
    361  // We don't need a special time slice if we didn't have_message to process.
    362  if (!have_message) return false;
    363 
    364  if (WM_QUIT == msg.message) {
    365    // If we're in a nested ::GetMessage() loop then we must let that loop see
    366    // the WM_QUIT in order for it to exit. If we're in DoRunLoop then the re-
    367    // posted WM_QUIT will be either ignored, or handled, by
    368    // ProcessMessageHelper() called directly from ProcessNextWindowsMessage().
    369    ::PostQuitMessage(static_cast<int>(msg.wParam));
    370    // Note: we *must not* ScheduleWork() here as WM_QUIT is a low-priority
    371    // message on Windows (it is only returned by ::PeekMessage() when idle) :
    372    // https://blogs.msdn.microsoft.com/oldnewthing/20051104-33/?p=33453. As
    373    // such posting a kMsgHaveWork message via ScheduleWork() would cause an
    374    // infinite loop (kMsgHaveWork message handled first means we end up here
    375    // again and repost WM_QUIT+ScheduleWork() again, etc.). Not leaving a
    376    // kMsgHaveWork message behind however is also problematic as unwinding
    377    // multiple layers of nested ::GetMessage() loops can result in starving
    378    // application tasks. TODO(https://crbug.com/890016) : Fix this.
    379 
    380    // The return value is mostly irrelevant but return true like we would after
    381    // processing a QuitClosure() task.
    382    return true;
    383  }
    384 
    385  // Guarantee we'll get another time slice in the case where we go into native
    386  // windows code. This ScheduleWork() may hurt performance a tiny bit when
    387  // tasks appear very infrequently, but when the event queue is busy, the
    388  // kMsgHaveWork events get (percentage wise) rarer and rarer.
    389  ScheduleWork();
    390  return ProcessMessageHelper(msg);
    391 }
    392 
    393 //-----------------------------------------------------------------------------
    394 // MessagePumpForIO public:
    395 
    396 MessagePumpForIO::MessagePumpForIO() {
    397  port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1));
    398  DCHECK(port_.IsValid());
    399 }
    400 
    401 void MessagePumpForIO::ScheduleWork() {
    402  if (InterlockedExchange(&have_work_, 1))
    403    return;  // Someone else continued the pumping.
    404 
    405  // Make sure the MessagePump does some work for us.
    406  BOOL ret =
    407      PostQueuedCompletionStatus(port_, 0, reinterpret_cast<ULONG_PTR>(this),
    408                                 reinterpret_cast<OVERLAPPED*>(this));
    409  DCHECK(ret);
    410 }
    411 
    412 void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
    413  // We know that we can't be blocked right now since this method can only be
    414  // called on the same thread as Run, so we only need to update our record of
    415  // how long to sleep when we do sleep.
    416  delayed_work_time_ = delayed_work_time;
    417 }
    418 
    419 void MessagePumpForIO::RegisterIOHandler(HANDLE file_handle,
    420                                         IOHandler* handler) {
    421  ULONG_PTR key = reinterpret_cast<ULONG_PTR>(handler);
    422  HANDLE port = CreateIoCompletionPort(file_handle, port_, key, 1);
    423  DCHECK(port == port_.Get());
    424 }
    425 
    426 //-----------------------------------------------------------------------------
    427 // MessagePumpForIO private:
    428 
    429 void MessagePumpForIO::DoRunLoop() {
    430  for (;;) {
    431    // If we do any work, we may create more messages etc., and more work may
    432    // possibly be waiting in another task group.  When we (for example)
    433    // WaitForIOCompletion(), there is a good chance there are still more
    434    // messages waiting.  On the other hand, when any of these methods return
    435    // having done no work, then it is pretty unlikely that calling them
    436    // again quickly will find any work to do.  Finally, if they all say they
    437    // had no work, then it is a good time to consider sleeping (waiting) for
    438    // more work.
    439 
    440    bool more_work_is_plausible = state_->delegate->DoWork();
    441    if (state_->should_quit) break;
    442 
    443    more_work_is_plausible |= WaitForIOCompletion(0, NULL);
    444    if (state_->should_quit) break;
    445 
    446    more_work_is_plausible |=
    447        state_->delegate->DoDelayedWork(&delayed_work_time_);
    448    if (state_->should_quit) break;
    449 
    450    if (more_work_is_plausible) continue;
    451 
    452    more_work_is_plausible = state_->delegate->DoIdleWork();
    453    if (state_->should_quit) break;
    454 
    455    if (more_work_is_plausible) continue;
    456 
    457    WaitForWork();  // Wait (sleep) until we have work to do again.
    458  }
    459 }
    460 
    461 // Wait until IO completes, up to the time needed by the timer manager to fire
    462 // the next set of timers.
    463 void MessagePumpForIO::WaitForWork() {
    464  // We do not support nested IO message loops. This is to avoid messy
    465  // recursion problems.
    466  DCHECK(state_->run_depth == 1) << "Cannot nest an IO message loop!";
    467 
    468  int timeout = GetCurrentDelay();
    469  if (timeout < 0)  // Negative value means no timers waiting.
    470    timeout = INFINITE;
    471 
    472  WaitForIOCompletion(timeout, NULL);
    473 }
    474 
    475 bool MessagePumpForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
    476  IOItem item;
    477  if (completed_io_.empty() || !MatchCompletedIOItem(filter, &item)) {
    478    // We have to ask the system for another IO completion.
    479    if (!GetIOItem(timeout, &item)) return false;
    480 
    481    if (ProcessInternalIOItem(item)) return true;
    482  }
    483 
    484  if (item.context->handler) {
    485    if (filter && item.handler != filter) {
    486      // Save this item for later
    487      completed_io_.push_back(item);
    488    } else {
    489      DCHECK(item.context->handler == item.handler);
    490      item.handler->OnIOCompleted(item.context, item.bytes_transfered,
    491                                  item.error);
    492    }
    493  } else {
    494    // The handler must be gone by now, just cleanup the mess.
    495    delete item.context;
    496  }
    497  return true;
    498 }
    499 
    500 // Asks the OS for another IO completion result.
    501 bool MessagePumpForIO::GetIOItem(DWORD timeout, IOItem* item) {
    502  memset(item, 0, sizeof(*item));
    503  ULONG_PTR key = 0;
    504  OVERLAPPED* overlapped = NULL;
    505  BOOL success;
    506  {
    507    AUTO_PROFILER_LABEL("MessagePumpForIO::GetIOItem::Wait", IDLE);
    508 #ifdef MOZ_GECKO_PROFILER
    509    mozilla::Maybe<mozilla::AutoProfilerThreadSleep> profilerThreadSleep;
    510    if (timeout != 0) {
    511      profilerThreadSleep.emplace();
    512    }
    513 #endif
    514    success = GetQueuedCompletionStatus(port_.Get(), &item->bytes_transfered,
    515                                        &key, &overlapped, timeout);
    516  }
    517  if (!success) {
    518    if (!overlapped) return false;  // Nothing in the queue.
    519    item->error = GetLastError();
    520    item->bytes_transfered = 0;
    521  }
    522 
    523  item->handler = reinterpret_cast<IOHandler*>(key);
    524  item->context = reinterpret_cast<IOContext*>(overlapped);
    525  return true;
    526 }
    527 
    528 bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) {
    529  if (this == reinterpret_cast<MessagePumpForIO*>(item.context) &&
    530      this == reinterpret_cast<MessagePumpForIO*>(item.handler)) {
    531    // This is our internal completion.
    532    DCHECK(!item.bytes_transfered);
    533    InterlockedExchange(&have_work_, 0);
    534    return true;
    535  }
    536  return false;
    537 }
    538 
    539 // Returns a completion item that was previously received.
    540 bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) {
    541  DCHECK(!completed_io_.empty());
    542  for (std::list<IOItem>::iterator it = completed_io_.begin();
    543       it != completed_io_.end(); ++it) {
    544    if (!filter || it->handler == filter) {
    545      *item = *it;
    546      completed_io_.erase(it);
    547      return true;
    548    }
    549  }
    550  return false;
    551 }
    552 
    553 }  // namespace base