tor-browser

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

message_loop.cc (22939B)


      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_loop.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "base/logging.h"
     12 #include "base/message_pump_default.h"
     13 #include "base/string_util.h"
     14 #include "base/thread_local.h"
     15 #include "mozilla/Atomics.h"
     16 #include "mozilla/MaybeLeakRefPtr.h"
     17 #include "mozilla/Mutex.h"
     18 #include "mozilla/ProfilerRunnable.h"
     19 #include "mozilla/TargetShutdownTaskSet.h"
     20 #include "nsIEventTarget.h"
     21 #include "nsITargetShutdownTask.h"
     22 #include "nsThreadUtils.h"
     23 
     24 #if defined(XP_DARWIN)
     25 #  include "base/message_pump_mac.h"
     26 #  include "base/message_pump_kqueue.h"
     27 #endif
     28 #if defined(XP_UNIX) && !defined(XP_DARWIN)
     29 #  include "base/message_pump_libevent.h"
     30 #endif
     31 #if defined(XP_LINUX) || defined(__DragonFly__) || defined(XP_FREEBSD) || \
     32    defined(XP_NETBSD) || defined(XP_OPENBSD)
     33 #  if defined(MOZ_WIDGET_GTK)
     34 #    include "base/message_pump_glib.h"
     35 #  endif
     36 #endif
     37 #ifdef ANDROID
     38 #  include "base/message_pump_android.h"
     39 #endif
     40 #include "nsISerialEventTarget.h"
     41 
     42 #include "mozilla/ipc/MessagePump.h"
     43 #include "nsThreadUtils.h"
     44 
     45 using base::Time;
     46 using base::TimeDelta;
     47 using base::TimeTicks;
     48 
     49 using mozilla::Runnable;
     50 
     51 static base::ThreadLocalPointer<MessageLoop>& get_tls_ptr() {
     52  static base::ThreadLocalPointer<MessageLoop> tls_ptr;
     53  return tls_ptr;
     54 }
     55 
     56 //------------------------------------------------------------------------------
     57 
     58 #if defined(XP_WIN)
     59 
     60 // Upon a SEH exception in this thread, it restores the original unhandled
     61 // exception filter.
     62 static int SEHFilter(LPTOP_LEVEL_EXCEPTION_FILTER old_filter) {
     63  ::SetUnhandledExceptionFilter(old_filter);
     64  return EXCEPTION_CONTINUE_SEARCH;
     65 }
     66 
     67 // Retrieves a pointer to the current unhandled exception filter. There
     68 // is no standalone getter method.
     69 static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() {
     70  LPTOP_LEVEL_EXCEPTION_FILTER top_filter = NULL;
     71  top_filter = ::SetUnhandledExceptionFilter(0);
     72  ::SetUnhandledExceptionFilter(top_filter);
     73  return top_filter;
     74 }
     75 
     76 #endif  // defined(XP_WIN)
     77 
     78 //------------------------------------------------------------------------------
     79 
     80 class MessageLoop::EventTarget : public nsISerialEventTarget,
     81                                 public nsITargetShutdownTask,
     82                                 public MessageLoop::DestructionObserver {
     83 public:
     84  NS_DECL_THREADSAFE_ISUPPORTS
     85  NS_DECL_NSIEVENTTARGET_FULL
     86 
     87  void TargetShutdown() override {
     88    TargetShutdownTaskSet::TasksArray shutdownTasks;
     89    {
     90      mozilla::MutexAutoLock lock(mMutex);
     91      if (mShutdownTasksRun) {
     92        return;
     93      }
     94      shutdownTasks = mShutdownTasks.Extract();
     95    }
     96    for (const auto& task : shutdownTasks) {
     97      task->TargetShutdown();
     98    }
     99  }
    100 
    101  explicit EventTarget(MessageLoop* aLoop)
    102      : mMutex("MessageLoop::EventTarget"), mLoop(aLoop) {
    103    aLoop->AddDestructionObserver(this);
    104  }
    105 
    106 private:
    107  virtual ~EventTarget() {
    108    if (mLoop) {
    109      mLoop->RemoveDestructionObserver(this);
    110    }
    111  }
    112 
    113  void WillDestroyCurrentMessageLoop() override {
    114    {
    115      mozilla::MutexAutoLock lock(mMutex);
    116      // The MessageLoop is being destroyed and we are called from its
    117      // destructor There's no real need to remove ourselves from the
    118      // destruction observer list. But it makes things look tidier.
    119      mLoop->RemoveDestructionObserver(this);
    120      mLoop = nullptr;
    121    }
    122 
    123    TargetShutdown();
    124  }
    125 
    126  mozilla::Mutex mMutex;
    127  bool mShutdownTasksRun MOZ_GUARDED_BY(mMutex) = false;
    128  TargetShutdownTaskSet mShutdownTasks MOZ_GUARDED_BY(mMutex);
    129  MessageLoop* mLoop MOZ_GUARDED_BY(mMutex);
    130 };
    131 
    132 NS_IMPL_ISUPPORTS(MessageLoop::EventTarget, nsIEventTarget,
    133                  nsISerialEventTarget)
    134 
    135 NS_IMETHODIMP_(bool)
    136 MessageLoop::EventTarget::IsOnCurrentThreadInfallible() {
    137  mozilla::MutexAutoLock lock(mMutex);
    138  return mLoop == MessageLoop::current();
    139 }
    140 
    141 NS_IMETHODIMP
    142 MessageLoop::EventTarget::IsOnCurrentThread(bool* aResult) {
    143  *aResult = IsOnCurrentThreadInfallible();
    144  return NS_OK;
    145 }
    146 
    147 NS_IMETHODIMP
    148 MessageLoop::EventTarget::DispatchFromScript(nsIRunnable* aEvent,
    149                                             DispatchFlags aFlags) {
    150  return Dispatch(do_AddRef(aEvent), aFlags);
    151 }
    152 
    153 NS_IMETHODIMP
    154 MessageLoop::EventTarget::Dispatch(already_AddRefed<nsIRunnable> aEvent,
    155                                   DispatchFlags aFlags) {
    156  mozilla::MaybeLeakRefPtr<nsIRunnable> event(std::move(aEvent),
    157                                              aFlags & NS_DISPATCH_FALLIBLE);
    158 
    159  mozilla::MutexAutoLock lock(mMutex);
    160  if (!mLoop) {
    161    return NS_ERROR_NOT_INITIALIZED;
    162  }
    163 
    164  mLoop->PostTask(event.forget());
    165  return NS_OK;
    166 }
    167 
    168 NS_IMETHODIMP
    169 MessageLoop::EventTarget::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent,
    170                                          uint32_t aDelayMs) {
    171  mozilla::MutexAutoLock lock(mMutex);
    172  if (!mLoop || mShutdownTasksRun) {
    173    return NS_ERROR_NOT_INITIALIZED;
    174  }
    175 
    176  mLoop->PostDelayedTask(std::move(aEvent), aDelayMs);
    177  return NS_OK;
    178 }
    179 
    180 NS_IMETHODIMP
    181 MessageLoop::EventTarget::RegisterShutdownTask(nsITargetShutdownTask* aTask) {
    182  mozilla::MutexAutoLock lock(mMutex);
    183  if (!mLoop || mShutdownTasksRun) {
    184    return NS_ERROR_UNEXPECTED;
    185  }
    186  mShutdownTasks.AddTask(aTask);
    187  return NS_OK;
    188 }
    189 
    190 NS_IMETHODIMP
    191 MessageLoop::EventTarget::UnregisterShutdownTask(nsITargetShutdownTask* aTask) {
    192  mozilla::MutexAutoLock lock(mMutex);
    193  if (!mLoop) {
    194    return NS_ERROR_UNEXPECTED;
    195  }
    196  return mShutdownTasks.RemoveTask(aTask);
    197 }
    198 
    199 //------------------------------------------------------------------------------
    200 
    201 // static
    202 MessageLoop* MessageLoop::current() { return get_tls_ptr().Get(); }
    203 
    204 // static
    205 void MessageLoop::set_current(MessageLoop* loop) { get_tls_ptr().Set(loop); }
    206 
    207 static mozilla::Atomic<int32_t> message_loop_id_seq(0);
    208 
    209 MessageLoop::MessageLoop(Type type, nsISerialEventTarget* aEventTarget)
    210    : type_(type),
    211      id_(++message_loop_id_seq),
    212      nestable_tasks_allowed_(true),
    213      exception_restoration_(false),
    214      incoming_queue_lock_("MessageLoop Incoming Queue Lock"),
    215      state_(NULL),
    216      run_depth_base_(1),
    217      shutting_down_(false),
    218 #ifdef XP_WIN
    219      os_modal_loop_(false),
    220 #endif  // XP_WIN
    221      transient_hang_timeout_(0),
    222      permanent_hang_timeout_(0),
    223      next_sequence_num_(0) {
    224  DCHECK(!current()) << "should only have one message loop per thread";
    225  get_tls_ptr().Set(this);
    226 
    227  // Must initialize after current() is initialized.
    228  mEventTarget = new EventTarget(this);
    229 
    230  switch (type_) {
    231    case TYPE_MOZILLA_PARENT:
    232      MOZ_RELEASE_ASSERT(!aEventTarget);
    233      pump_ = new mozilla::ipc::MessagePump(aEventTarget);
    234      return;
    235    case TYPE_MOZILLA_CHILD:
    236      MOZ_RELEASE_ASSERT(!aEventTarget);
    237      pump_ = new mozilla::ipc::MessagePumpForChildProcess();
    238      // There is a MessageLoop Run call from XRE_InitChildProcess
    239      // and another one from MessagePumpForChildProcess. The one
    240      // from MessagePumpForChildProcess becomes the base, so we need
    241      // to set run_depth_base_ to 2 or we'll never be able to process
    242      // Idle tasks.
    243      run_depth_base_ = 2;
    244      return;
    245    case TYPE_MOZILLA_NONMAINTHREAD:
    246      pump_ = new mozilla::ipc::MessagePumpForNonMainThreads(aEventTarget);
    247      return;
    248 #if defined(XP_WIN) || defined(XP_DARWIN)
    249    case TYPE_MOZILLA_NONMAINUITHREAD:
    250      pump_ = new mozilla::ipc::MessagePumpForNonMainUIThreads(aEventTarget);
    251      return;
    252 #elif defined(MOZ_WIDGET_ANDROID)
    253    case TYPE_MOZILLA_ANDROID_UI:
    254      MOZ_RELEASE_ASSERT(aEventTarget);
    255      pump_ = new mozilla::ipc::MessagePumpForAndroidUI(aEventTarget);
    256      return;
    257 #endif  // defined(MOZ_WIDGET_ANDROID)
    258    default:
    259      // Create one of Chromium's standard MessageLoop types below.
    260      break;
    261  }
    262 
    263 #if defined(XP_WIN)
    264  // TODO(rvargas): Get rid of the OS guards.
    265  if (type_ == TYPE_DEFAULT) {
    266    pump_ = new base::MessagePumpDefault();
    267  } else if (type_ == TYPE_IO) {
    268    pump_ = new base::MessagePumpForIO();
    269  } else {
    270    DCHECK(type_ == TYPE_UI);
    271    pump_ = new base::MessagePumpForUI();
    272  }
    273 #else
    274  if (type_ == TYPE_UI) {
    275 #  if defined(XP_DARWIN)
    276    pump_ = base::MessagePumpMac::Create();
    277 #  elif defined(XP_LINUX) || defined(__DragonFly__) || defined(XP_FREEBSD) || \
    278      defined(XP_NETBSD) || defined(XP_OPENBSD)
    279    pump_ = new base::MessagePumpForUI();
    280 #  endif  // XP_LINUX
    281  } else if (type_ == TYPE_IO) {
    282 #  if defined(XP_DARWIN)
    283    pump_ = new base::MessagePumpKqueue();
    284 #  else
    285    pump_ = new base::MessagePumpLibevent();
    286 #  endif
    287  } else {
    288    pump_ = new base::MessagePumpDefault();
    289  }
    290 #endif
    291 
    292  // We want GetCurrentSerialEventTarget() to return the real nsThread if it
    293  // will be used to dispatch tasks. However, under all other cases; we'll want
    294  // it to return this MessageLoop's EventTarget.
    295  if (nsISerialEventTarget* thread = pump_->GetXPCOMThread()) {
    296    MOZ_ALWAYS_SUCCEEDS(thread->RegisterShutdownTask(mEventTarget));
    297  } else {
    298    mozilla::SerialEventTargetGuard::Set(mEventTarget);
    299  }
    300 }
    301 
    302 MessageLoop::~MessageLoop() {
    303  DCHECK(this == current());
    304 
    305  // Let interested parties have one last shot at accessing this.
    306  FOR_EACH_OBSERVER(DestructionObserver, destruction_observers_,
    307                    WillDestroyCurrentMessageLoop());
    308 
    309  DCHECK(!state_);
    310 
    311  // Clean up any unprocessed tasks, but take care: deleting a task could
    312  // result in the addition of more tasks (e.g., via DeleteSoon).  We set a
    313  // limit on the number of times we will allow a deleted task to generate more
    314  // tasks.  Normally, we should only pass through this loop once or twice.  If
    315  // we end up hitting the loop limit, then it is probably due to one task that
    316  // is being stubborn.  Inspect the queues to see who is left.
    317  bool did_work;
    318  for (int i = 0; i < 100; ++i) {
    319    DeletePendingTasks();
    320    ReloadWorkQueue();
    321    // If we end up with empty queues, then break out of the loop.
    322    did_work = DeletePendingTasks();
    323    if (!did_work) break;
    324  }
    325  DCHECK(!did_work);
    326 
    327  // OK, now make it so that no one can find us.
    328  get_tls_ptr().Set(NULL);
    329 }
    330 
    331 void MessageLoop::AddDestructionObserver(DestructionObserver* obs) {
    332  DCHECK(this == current());
    333  destruction_observers_.AddObserver(obs);
    334 }
    335 
    336 void MessageLoop::RemoveDestructionObserver(DestructionObserver* obs) {
    337  DCHECK(this == current());
    338  destruction_observers_.RemoveObserver(obs);
    339 }
    340 
    341 void MessageLoop::Run() {
    342  AutoRunState save_state(this);
    343  RunHandler();
    344 }
    345 
    346 // Runs the loop in two different SEH modes:
    347 // enable_SEH_restoration_ = false : any unhandled exception goes to the last
    348 // one that calls SetUnhandledExceptionFilter().
    349 // enable_SEH_restoration_ = true : any unhandled exception goes to the filter
    350 // that was existed before the loop was run.
    351 void MessageLoop::RunHandler() {
    352 #if defined(XP_WIN)
    353  if (exception_restoration_) {
    354    LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter();
    355    MOZ_SEH_TRY { RunInternal(); }
    356    MOZ_SEH_EXCEPT(SEHFilter(current_filter)) {}
    357    return;
    358  }
    359 #endif
    360 
    361  RunInternal();
    362 }
    363 
    364 //------------------------------------------------------------------------------
    365 
    366 void MessageLoop::RunInternal() {
    367  DCHECK(this == current());
    368  pump_->Run(this);
    369 }
    370 
    371 //------------------------------------------------------------------------------
    372 // Wrapper functions for use in above message loop framework.
    373 
    374 bool MessageLoop::ProcessNextDelayedNonNestableTask() {
    375  if (state_->run_depth > run_depth_base_) return false;
    376 
    377  if (deferred_non_nestable_work_queue_.empty()) return false;
    378 
    379  nsCOMPtr<nsIRunnable> task =
    380      std::move(deferred_non_nestable_work_queue_.front().task);
    381  deferred_non_nestable_work_queue_.pop();
    382 
    383  RunTask(task.forget());
    384  return true;
    385 }
    386 
    387 //------------------------------------------------------------------------------
    388 
    389 void MessageLoop::Quit() {
    390  DCHECK(current() == this);
    391  if (state_) {
    392    state_->quit_received = true;
    393  } else {
    394    NOTREACHED() << "Must be inside Run to call Quit";
    395  }
    396 }
    397 
    398 void MessageLoop::PostTask(already_AddRefed<nsIRunnable> task) {
    399  PostTask_Helper(std::move(task), 0);
    400 }
    401 
    402 void MessageLoop::PostDelayedTask(already_AddRefed<nsIRunnable> task,
    403                                  int delay_ms) {
    404  PostTask_Helper(std::move(task), delay_ms);
    405 }
    406 
    407 void MessageLoop::PostIdleTask(already_AddRefed<nsIRunnable> task) {
    408  DCHECK(current() == this);
    409  MOZ_ASSERT(NS_IsMainThread());
    410 
    411  PendingTask pending_task(std::move(task), false);
    412  mozilla::LogRunnable::LogDispatch(pending_task.task.get());
    413  deferred_non_nestable_work_queue_.push(std::move(pending_task));
    414 }
    415 
    416 // Possibly called on a background thread!
    417 void MessageLoop::PostTask_Helper(already_AddRefed<nsIRunnable> task,
    418                                  int delay_ms) {
    419  if (nsISerialEventTarget* target = pump_->GetXPCOMThread()) {
    420    nsresult rv;
    421    if (delay_ms) {
    422      rv = target->DelayedDispatch(std::move(task), delay_ms);
    423    } else {
    424      rv = target->Dispatch(std::move(task), NS_DISPATCH_NORMAL);
    425    }
    426    MOZ_ALWAYS_SUCCEEDS(rv);
    427    return;
    428  }
    429 
    430  // Tasks should only be queued before or during the Run loop, not after.
    431  MOZ_ASSERT(!shutting_down_);
    432 
    433  PendingTask pending_task(std::move(task), true);
    434 
    435  if (delay_ms > 0) {
    436    pending_task.delayed_run_time =
    437        TimeTicks::Now() + TimeDelta::FromMilliseconds(delay_ms);
    438  } else {
    439    DCHECK(delay_ms == 0) << "delay should not be negative";
    440  }
    441 
    442  // Warning: Don't try to short-circuit, and handle this thread's tasks more
    443  // directly, as it could starve handling of foreign threads.  Put every task
    444  // into this queue.
    445 
    446  RefPtr<base::MessagePump> pump;
    447  {
    448    mozilla::MutexAutoLock locked(incoming_queue_lock_);
    449    mozilla::LogRunnable::LogDispatch(pending_task.task.get());
    450    incoming_queue_.push(std::move(pending_task));
    451    pump = pump_;
    452  }
    453  // Since the incoming_queue_ may contain a task that destroys this message
    454  // loop, we cannot exit incoming_queue_lock_ until we are done with |this|.
    455  // We use a stack-based reference to the message pump so that we can call
    456  // ScheduleWork outside of incoming_queue_lock_.
    457 
    458  pump->ScheduleWork();
    459 }
    460 
    461 void MessageLoop::SetNestableTasksAllowed(bool allowed) {
    462  if (nestable_tasks_allowed_ != allowed) {
    463    nestable_tasks_allowed_ = allowed;
    464    if (!nestable_tasks_allowed_) return;
    465    // Start the native pump if we are not already pumping.
    466    pump_->ScheduleWorkForNestedLoop();
    467  }
    468 }
    469 
    470 void MessageLoop::ScheduleWork() {
    471  // Start the native pump if we are not already pumping.
    472  pump_->ScheduleWork();
    473 }
    474 
    475 bool MessageLoop::NestableTasksAllowed() const {
    476  return nestable_tasks_allowed_;
    477 }
    478 
    479 //------------------------------------------------------------------------------
    480 
    481 void MessageLoop::RunTask(already_AddRefed<nsIRunnable> aTask) {
    482  DCHECK(nestable_tasks_allowed_);
    483  // Execute the task and assume the worst: It is probably not reentrant.
    484  nestable_tasks_allowed_ = false;
    485 
    486  nsCOMPtr<nsIRunnable> task = aTask;
    487 
    488  {
    489    mozilla::LogRunnable::Run log(task.get());
    490    AUTO_PROFILE_FOLLOWING_RUNNABLE(task);
    491    task->Run();
    492    task = nullptr;
    493  }
    494 
    495  nestable_tasks_allowed_ = true;
    496 }
    497 
    498 bool MessageLoop::DeferOrRunPendingTask(PendingTask&& pending_task) {
    499  if (pending_task.nestable || state_->run_depth <= run_depth_base_) {
    500    RunTask(pending_task.task.forget());
    501    // Show that we ran a task (Note: a new one might arrive as a
    502    // consequence!).
    503    return true;
    504  }
    505 
    506  // We couldn't run the task now because we're in a nested message loop
    507  // and the task isn't nestable.
    508  mozilla::LogRunnable::LogDispatch(pending_task.task.get());
    509  deferred_non_nestable_work_queue_.push(std::move(pending_task));
    510  return false;
    511 }
    512 
    513 void MessageLoop::AddToDelayedWorkQueue(const PendingTask& pending_task) {
    514  // Move to the delayed work queue.  Initialize the sequence number
    515  // before inserting into the delayed_work_queue_.  The sequence number
    516  // is used to faciliate FIFO sorting when two tasks have the same
    517  // delayed_run_time value.
    518  PendingTask new_pending_task(pending_task);
    519  new_pending_task.sequence_num = next_sequence_num_++;
    520  mozilla::LogRunnable::LogDispatch(new_pending_task.task.get());
    521  delayed_work_queue_.push(std::move(new_pending_task));
    522 }
    523 
    524 void MessageLoop::ReloadWorkQueue() {
    525  // We can improve performance of our loading tasks from incoming_queue_ to
    526  // work_queue_ by waiting until the last minute (work_queue_ is empty) to
    527  // load.  That reduces the number of locks-per-task significantly when our
    528  // queues get large.
    529  if (!work_queue_.empty())
    530    return;  // Wait till we *really* need to lock and load.
    531 
    532  // Acquire all we can from the inter-thread queue with one lock acquisition.
    533  {
    534    mozilla::MutexAutoLock lock(incoming_queue_lock_);
    535    if (incoming_queue_.empty()) return;
    536    std::swap(incoming_queue_, work_queue_);
    537    DCHECK(incoming_queue_.empty());
    538  }
    539 }
    540 
    541 bool MessageLoop::DeletePendingTasks() {
    542  MOZ_ASSERT(work_queue_.empty());
    543  bool did_work = !deferred_non_nestable_work_queue_.empty();
    544  while (!deferred_non_nestable_work_queue_.empty()) {
    545    deferred_non_nestable_work_queue_.pop();
    546  }
    547  did_work |= !delayed_work_queue_.empty();
    548  while (!delayed_work_queue_.empty()) {
    549    delayed_work_queue_.pop();
    550  }
    551  return did_work;
    552 }
    553 
    554 bool MessageLoop::DoWork() {
    555  if (!nestable_tasks_allowed_) {
    556    // Task can't be executed right now.
    557    return false;
    558  }
    559 
    560  for (;;) {
    561    ReloadWorkQueue();
    562    if (work_queue_.empty()) break;
    563 
    564    // Execute oldest task.
    565    do {
    566      PendingTask pending_task = std::move(work_queue_.front());
    567      work_queue_.pop();
    568      if (!pending_task.delayed_run_time.is_null()) {
    569        // NB: Don't move, because we use this later!
    570        AddToDelayedWorkQueue(pending_task);
    571        // If we changed the topmost task, then it is time to re-schedule.
    572        if (delayed_work_queue_.top().task == pending_task.task)
    573          pump_->ScheduleDelayedWork(pending_task.delayed_run_time);
    574      } else {
    575        if (DeferOrRunPendingTask(std::move(pending_task))) return true;
    576      }
    577    } while (!work_queue_.empty());
    578  }
    579 
    580  // Nothing happened.
    581  return false;
    582 }
    583 
    584 bool MessageLoop::DoDelayedWork(TimeTicks* next_delayed_work_time) {
    585  if (!nestable_tasks_allowed_ || delayed_work_queue_.empty()) {
    586    *next_delayed_work_time = TimeTicks();
    587    return false;
    588  }
    589 
    590  if (delayed_work_queue_.top().delayed_run_time > TimeTicks::Now()) {
    591    *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time;
    592    return false;
    593  }
    594 
    595  PendingTask pending_task = delayed_work_queue_.top();
    596  delayed_work_queue_.pop();
    597 
    598  if (!delayed_work_queue_.empty())
    599    *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time;
    600 
    601  return DeferOrRunPendingTask(std::move(pending_task));
    602 }
    603 
    604 bool MessageLoop::DoIdleWork() {
    605  if (ProcessNextDelayedNonNestableTask()) return true;
    606 
    607  if (state_->quit_received) pump_->Quit();
    608 
    609  return false;
    610 }
    611 
    612 //------------------------------------------------------------------------------
    613 // MessageLoop::AutoRunState
    614 
    615 MessageLoop::AutoRunState::AutoRunState(MessageLoop* loop) : loop_(loop) {
    616  // Top-level Run should only get called once.
    617  MOZ_ASSERT(!loop_->shutting_down_);
    618 
    619  // Make the loop reference us.
    620  previous_state_ = loop_->state_;
    621  if (previous_state_) {
    622    run_depth = previous_state_->run_depth + 1;
    623  } else {
    624    run_depth = 1;
    625  }
    626  loop_->state_ = this;
    627 
    628  // Initialize the other fields:
    629  quit_received = false;
    630 #if defined(XP_WIN)
    631  dispatcher = NULL;
    632 #endif
    633 }
    634 
    635 MessageLoop::AutoRunState::~AutoRunState() {
    636  loop_->state_ = previous_state_;
    637 
    638  // If exiting a top-level Run, then we're shutting down.
    639  loop_->shutting_down_ = !previous_state_;
    640 }
    641 
    642 //------------------------------------------------------------------------------
    643 // MessageLoop::PendingTask
    644 
    645 bool MessageLoop::PendingTask::operator<(const PendingTask& other) const {
    646  // Since the top of a priority queue is defined as the "greatest" element, we
    647  // need to invert the comparison here.  We want the smaller time to be at the
    648  // top of the heap.
    649 
    650  if (delayed_run_time < other.delayed_run_time) return false;
    651 
    652  if (delayed_run_time > other.delayed_run_time) return true;
    653 
    654  // If the times happen to match, then we use the sequence number to decide.
    655  // Compare the difference to support integer roll-over.
    656  return (sequence_num - other.sequence_num) > 0;
    657 }
    658 
    659 //------------------------------------------------------------------------------
    660 // MessageLoop::SerialEventTarget
    661 
    662 nsISerialEventTarget* MessageLoop::SerialEventTarget() { return mEventTarget; }
    663 
    664 //------------------------------------------------------------------------------
    665 // MessageLoopForUI
    666 
    667 #if defined(XP_WIN)
    668 
    669 void MessageLoopForUI::Run(Dispatcher* dispatcher) {
    670  AutoRunState save_state(this);
    671  state_->dispatcher = dispatcher;
    672  RunHandler();
    673 }
    674 
    675 void MessageLoopForUI::AddObserver(Observer* observer) {
    676  pump_win()->AddObserver(observer);
    677 }
    678 
    679 void MessageLoopForUI::RemoveObserver(Observer* observer) {
    680  pump_win()->RemoveObserver(observer);
    681 }
    682 
    683 void MessageLoopForUI::WillProcessMessage(const MSG& message) {
    684  pump_win()->WillProcessMessage(message);
    685 }
    686 void MessageLoopForUI::DidProcessMessage(const MSG& message) {
    687  pump_win()->DidProcessMessage(message);
    688 }
    689 void MessageLoopForUI::PumpOutPendingPaintMessages() {
    690  pump_ui()->PumpOutPendingPaintMessages();
    691 }
    692 
    693 #endif  // defined(XP_WIN)
    694 
    695 //------------------------------------------------------------------------------
    696 // MessageLoopForIO
    697 
    698 #if defined(XP_WIN)
    699 
    700 void MessageLoopForIO::RegisterIOHandler(HANDLE file, IOHandler* handler) {
    701  pump_io()->RegisterIOHandler(file, handler);
    702 }
    703 
    704 bool MessageLoopForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
    705  return pump_io()->WaitForIOCompletion(timeout, filter);
    706 }
    707 
    708 #elif defined(XP_DARWIN)
    709 
    710 bool MessageLoopForIO::WatchFileDescriptor(int fd, bool persistent, Mode mode,
    711                                           FileDescriptorWatcher* controller,
    712                                           Watcher* delegate) {
    713  return pump_kqueue()->WatchFileDescriptor(
    714      fd, persistent, static_cast<base::MessagePumpKqueue::Mode>(mode),
    715      controller, delegate);
    716 }
    717 
    718 bool MessageLoopForIO::WatchMachReceivePort(mach_port_t port,
    719                                            MachPortWatchController* controller,
    720                                            MachPortWatcher* delegate) {
    721  return pump_kqueue()->WatchMachReceivePort(port, controller, delegate);
    722 }
    723 
    724 #else
    725 
    726 bool MessageLoopForIO::WatchFileDescriptor(int fd, bool persistent, Mode mode,
    727                                           FileDescriptorWatcher* controller,
    728                                           Watcher* delegate) {
    729  return pump_libevent()->WatchFileDescriptor(
    730      fd, persistent, static_cast<base::MessagePumpLibevent::Mode>(mode),
    731      controller, delegate);
    732 }
    733 
    734 #endif