message_loop.cc revision e4feccc6428acb735e68c15bcf972504a705c547
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/message_loop.h"
6
7#if defined(OS_POSIX) && !defined(OS_MACOSX)
8#include <gdk/gdk.h>
9#include <gdk/gdkx.h>
10#endif
11
12#include <algorithm>
13
14#include "base/compiler_specific.h"
15#include "base/lazy_instance.h"
16#include "base/logging.h"
17#include "base/message_pump_default.h"
18#include "base/metrics/histogram.h"
19#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
20#include "base/threading/thread_local.h"
21
22#if defined(OS_MACOSX)
23#include "base/message_pump_mac.h"
24#endif
25#if defined(OS_POSIX)
26#include "base/message_pump_libevent.h"
27#endif
28#if defined(OS_POSIX) && !defined(OS_MACOSX)
29#include "base/message_pump_glib.h"
30#endif
31#if defined(TOUCH_UI)
32#include "base/message_pump_glib_x.h"
33#endif
34
35using base::TimeDelta;
36using base::TimeTicks;
37
38namespace {
39
40// A lazily created thread local storage for quick access to a thread's message
41// loop, if one exists.  This should be safe and free of static constructors.
42base::LazyInstance<base::ThreadLocalPointer<MessageLoop> > lazy_tls_ptr(
43    base::LINKER_INITIALIZED);
44
45// Logical events for Histogram profiling. Run with -message-loop-histogrammer
46// to get an accounting of messages and actions taken on each thread.
47const int kTaskRunEvent = 0x1;
48const int kTimerEvent = 0x2;
49
50// Provide range of message IDs for use in histogramming and debug display.
51const int kLeastNonZeroMessageId = 1;
52const int kMaxMessageId = 1099;
53const int kNumberOfDistinctMessagesDisplayed = 1100;
54
55// Provide a macro that takes an expression (such as a constant, or macro
56// constant) and creates a pair to initalize an array of pairs.  In this case,
57// our pair consists of the expressions value, and the "stringized" version
58// of the expression (i.e., the exrpression put in quotes).  For example, if
59// we have:
60//    #define FOO 2
61//    #define BAR 5
62// then the following:
63//    VALUE_TO_NUMBER_AND_NAME(FOO + BAR)
64// will expand to:
65//   {7, "FOO + BAR"}
66// We use the resulting array as an argument to our histogram, which reads the
67// number as a bucket identifier, and proceeds to use the corresponding name
68// in the pair (i.e., the quoted string) when printing out a histogram.
69#define VALUE_TO_NUMBER_AND_NAME(name) {name, #name},
70
71const base::LinearHistogram::DescriptionPair event_descriptions_[] = {
72  // Provide some pretty print capability in our histogram for our internal
73  // messages.
74
75  // A few events we handle (kindred to messages), and used to profile actions.
76  VALUE_TO_NUMBER_AND_NAME(kTaskRunEvent)
77  VALUE_TO_NUMBER_AND_NAME(kTimerEvent)
78
79  {-1, NULL}  // The list must be null terminated, per API to histogram.
80};
81
82bool enable_histogrammer_ = false;
83
84}  // namespace
85
86//------------------------------------------------------------------------------
87
88#if defined(OS_WIN)
89
90// Upon a SEH exception in this thread, it restores the original unhandled
91// exception filter.
92static int SEHFilter(LPTOP_LEVEL_EXCEPTION_FILTER old_filter) {
93  ::SetUnhandledExceptionFilter(old_filter);
94  return EXCEPTION_CONTINUE_SEARCH;
95}
96
97// Retrieves a pointer to the current unhandled exception filter. There
98// is no standalone getter method.
99static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() {
100  LPTOP_LEVEL_EXCEPTION_FILTER top_filter = NULL;
101  top_filter = ::SetUnhandledExceptionFilter(0);
102  ::SetUnhandledExceptionFilter(top_filter);
103  return top_filter;
104}
105
106#endif  // defined(OS_WIN)
107
108//------------------------------------------------------------------------------
109
110MessageLoop::TaskObserver::TaskObserver() {
111}
112
113MessageLoop::TaskObserver::~TaskObserver() {
114}
115
116MessageLoop::DestructionObserver::~DestructionObserver() {
117}
118
119//------------------------------------------------------------------------------
120
121MessageLoop::MessageLoop(Type type)
122    : type_(type),
123      nestable_tasks_allowed_(true),
124      exception_restoration_(false),
125      state_(NULL),
126      next_sequence_num_(0) {
127  DCHECK(!current()) << "should only have one message loop per thread";
128  lazy_tls_ptr.Pointer()->Set(this);
129
130// TODO(rvargas): Get rid of the OS guards.
131#if defined(OS_WIN)
132#define MESSAGE_PUMP_UI new base::MessagePumpForUI()
133#define MESSAGE_PUMP_IO new base::MessagePumpForIO()
134#elif defined(OS_MACOSX)
135#define MESSAGE_PUMP_UI base::MessagePumpMac::Create()
136#define MESSAGE_PUMP_IO new base::MessagePumpLibevent()
137#elif defined(ANDROID)
138#define MESSAGE_PUMP_UI new base::MessagePumpDefault()
139#define MESSAGE_PUMP_IO new base::MessagePumpLibevent()
140#elif defined(TOUCH_UI)
141#define MESSAGE_PUMP_UI new base::MessagePumpGlibX()
142#define MESSAGE_PUMP_IO new base::MessagePumpLibevent()
143#elif defined(OS_NACL)
144// Currently NaCl doesn't have a UI or an IO MessageLoop.
145// TODO(abarth): Figure out if we need these.
146#define MESSAGE_PUMP_UI NULL
147#define MESSAGE_PUMP_IO NULL
148#elif defined(OS_POSIX)  // POSIX but not MACOSX.
149#define MESSAGE_PUMP_UI new base::MessagePumpForUI()
150#define MESSAGE_PUMP_IO new base::MessagePumpLibevent()
151#else
152#error Not implemented
153#endif
154
155  if (type_ == TYPE_UI) {
156    pump_ = MESSAGE_PUMP_UI;
157  } else if (type_ == TYPE_IO) {
158    pump_ = MESSAGE_PUMP_IO;
159  } else {
160    DCHECK_EQ(TYPE_DEFAULT, type_);
161    pump_ = new base::MessagePumpDefault();
162  }
163}
164
165MessageLoop::~MessageLoop() {
166  DCHECK_EQ(this, current());
167
168  DCHECK(!state_);
169
170  // Clean up any unprocessed tasks, but take care: deleting a task could
171  // result in the addition of more tasks (e.g., via DeleteSoon).  We set a
172  // limit on the number of times we will allow a deleted task to generate more
173  // tasks.  Normally, we should only pass through this loop once or twice.  If
174  // we end up hitting the loop limit, then it is probably due to one task that
175  // is being stubborn.  Inspect the queues to see who is left.
176  bool did_work;
177  for (int i = 0; i < 100; ++i) {
178    DeletePendingTasks();
179    ReloadWorkQueue();
180    // If we end up with empty queues, then break out of the loop.
181    did_work = DeletePendingTasks();
182    if (!did_work)
183      break;
184  }
185  DCHECK(!did_work);
186
187  // Let interested parties have one last shot at accessing this.
188  FOR_EACH_OBSERVER(DestructionObserver, destruction_observers_,
189                    WillDestroyCurrentMessageLoop());
190
191  // OK, now make it so that no one can find us.
192  lazy_tls_ptr.Pointer()->Set(NULL);
193}
194
195// static
196MessageLoop* MessageLoop::current() {
197  // TODO(darin): sadly, we cannot enable this yet since people call us even
198  // when they have no intention of using us.
199  // DCHECK(loop) << "Ouch, did you forget to initialize me?";
200  return lazy_tls_ptr.Pointer()->Get();
201}
202
203// static
204void MessageLoop::EnableHistogrammer(bool enable) {
205  enable_histogrammer_ = enable;
206}
207
208void MessageLoop::AddDestructionObserver(
209    DestructionObserver* destruction_observer) {
210  DCHECK_EQ(this, current());
211  destruction_observers_.AddObserver(destruction_observer);
212}
213
214void MessageLoop::RemoveDestructionObserver(
215    DestructionObserver* destruction_observer) {
216  DCHECK_EQ(this, current());
217  destruction_observers_.RemoveObserver(destruction_observer);
218}
219
220void MessageLoop::PostTask(
221    const tracked_objects::Location& from_here, Task* task) {
222  PostTask_Helper(from_here, task, 0, true);
223}
224
225void MessageLoop::PostDelayedTask(
226    const tracked_objects::Location& from_here, Task* task, int64 delay_ms) {
227  PostTask_Helper(from_here, task, delay_ms, true);
228}
229
230void MessageLoop::PostNonNestableTask(
231    const tracked_objects::Location& from_here, Task* task) {
232  PostTask_Helper(from_here, task, 0, false);
233}
234
235void MessageLoop::PostNonNestableDelayedTask(
236    const tracked_objects::Location& from_here, Task* task, int64 delay_ms) {
237  PostTask_Helper(from_here, task, delay_ms, false);
238}
239
240void MessageLoop::Run() {
241  AutoRunState save_state(this);
242  RunHandler();
243}
244
245void MessageLoop::RunAllPending() {
246  AutoRunState save_state(this);
247  state_->quit_received = true;  // Means run until we would otherwise block.
248  RunHandler();
249}
250
251void MessageLoop::Quit() {
252  DCHECK_EQ(this, current());
253  if (state_) {
254    state_->quit_received = true;
255  } else {
256    NOTREACHED() << "Must be inside Run to call Quit";
257  }
258}
259
260void MessageLoop::QuitNow() {
261  DCHECK_EQ(this, current());
262  if (state_) {
263    pump_->Quit();
264  } else {
265    NOTREACHED() << "Must be inside Run to call Quit";
266  }
267}
268
269void MessageLoop::SetNestableTasksAllowed(bool allowed) {
270  if (nestable_tasks_allowed_ != allowed) {
271    nestable_tasks_allowed_ = allowed;
272    if (!nestable_tasks_allowed_)
273      return;
274    // Start the native pump if we are not already pumping.
275    pump_->ScheduleWork();
276  }
277}
278
279bool MessageLoop::NestableTasksAllowed() const {
280  return nestable_tasks_allowed_;
281}
282
283bool MessageLoop::IsNested() {
284  return state_->run_depth > 1;
285}
286
287void MessageLoop::AddTaskObserver(TaskObserver* task_observer) {
288  DCHECK_EQ(this, current());
289  task_observers_.AddObserver(task_observer);
290}
291
292void MessageLoop::RemoveTaskObserver(TaskObserver* task_observer) {
293  DCHECK_EQ(this, current());
294  task_observers_.RemoveObserver(task_observer);
295}
296
297void MessageLoop::AssertIdle() const {
298  // We only check |incoming_queue_|, since we don't want to lock |work_queue_|.
299  base::AutoLock lock(incoming_queue_lock_);
300  DCHECK(incoming_queue_.empty());
301}
302
303//------------------------------------------------------------------------------
304
305// Runs the loop in two different SEH modes:
306// enable_SEH_restoration_ = false : any unhandled exception goes to the last
307// one that calls SetUnhandledExceptionFilter().
308// enable_SEH_restoration_ = true : any unhandled exception goes to the filter
309// that was existed before the loop was run.
310void MessageLoop::RunHandler() {
311#if defined(OS_WIN)
312  if (exception_restoration_) {
313    RunInternalInSEHFrame();
314    return;
315  }
316#endif
317
318  RunInternal();
319}
320
321#if defined(OS_WIN)
322__declspec(noinline) void MessageLoop::RunInternalInSEHFrame() {
323  LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter();
324  __try {
325    RunInternal();
326  } __except(SEHFilter(current_filter)) {
327  }
328  return;
329}
330#endif
331
332void MessageLoop::RunInternal() {
333  DCHECK_EQ(this, current());
334
335#ifndef ANDROID
336  StartHistogrammer();
337#endif
338
339#if !defined(OS_MACOSX)
340  if (state_->dispatcher && type() == TYPE_UI) {
341    static_cast<base::MessagePumpForUI*>(pump_.get())->
342        RunWithDispatcher(this, state_->dispatcher);
343    return;
344  }
345#endif
346
347  pump_->Run(this);
348}
349
350bool MessageLoop::ProcessNextDelayedNonNestableTask() {
351  if (state_->run_depth != 1)
352    return false;
353
354  if (deferred_non_nestable_work_queue_.empty())
355    return false;
356
357  Task* task = deferred_non_nestable_work_queue_.front().task;
358  deferred_non_nestable_work_queue_.pop();
359
360  RunTask(task);
361  return true;
362}
363
364void MessageLoop::RunTask(Task* task) {
365  DCHECK(nestable_tasks_allowed_);
366  // Execute the task and assume the worst: It is probably not reentrant.
367  nestable_tasks_allowed_ = false;
368
369  HistogramEvent(kTaskRunEvent);
370  FOR_EACH_OBSERVER(TaskObserver, task_observers_,
371                    WillProcessTask(task));
372  task->Run();
373  FOR_EACH_OBSERVER(TaskObserver, task_observers_, DidProcessTask(task));
374  delete task;
375
376  nestable_tasks_allowed_ = true;
377}
378
379bool MessageLoop::DeferOrRunPendingTask(const PendingTask& pending_task) {
380  if (pending_task.nestable || state_->run_depth == 1) {
381    RunTask(pending_task.task);
382    // Show that we ran a task (Note: a new one might arrive as a
383    // consequence!).
384    return true;
385  }
386
387  // We couldn't run the task now because we're in a nested message loop
388  // and the task isn't nestable.
389  deferred_non_nestable_work_queue_.push(pending_task);
390  return false;
391}
392
393void MessageLoop::AddToDelayedWorkQueue(const PendingTask& pending_task) {
394  // Move to the delayed work queue.  Initialize the sequence number
395  // before inserting into the delayed_work_queue_.  The sequence number
396  // is used to faciliate FIFO sorting when two tasks have the same
397  // delayed_run_time value.
398  PendingTask new_pending_task(pending_task);
399  new_pending_task.sequence_num = next_sequence_num_++;
400  delayed_work_queue_.push(new_pending_task);
401}
402
403void MessageLoop::ReloadWorkQueue() {
404  // We can improve performance of our loading tasks from incoming_queue_ to
405  // work_queue_ by waiting until the last minute (work_queue_ is empty) to
406  // load.  That reduces the number of locks-per-task significantly when our
407  // queues get large.
408  if (!work_queue_.empty())
409    return;  // Wait till we *really* need to lock and load.
410
411  // Acquire all we can from the inter-thread queue with one lock acquisition.
412  {
413    base::AutoLock lock(incoming_queue_lock_);
414    if (incoming_queue_.empty())
415      return;
416    incoming_queue_.Swap(&work_queue_);  // Constant time
417    DCHECK(incoming_queue_.empty());
418  }
419}
420
421bool MessageLoop::DeletePendingTasks() {
422  bool did_work = !work_queue_.empty();
423  while (!work_queue_.empty()) {
424    PendingTask pending_task = work_queue_.front();
425    work_queue_.pop();
426    if (!pending_task.delayed_run_time.is_null()) {
427      // We want to delete delayed tasks in the same order in which they would
428      // normally be deleted in case of any funny dependencies between delayed
429      // tasks.
430      AddToDelayedWorkQueue(pending_task);
431    } else {
432      // TODO(darin): Delete all tasks once it is safe to do so.
433      // Until it is totally safe, just do it when running Purify or
434      // Valgrind.
435#if defined(PURIFY) || defined(USE_HEAPCHECKER)
436      delete pending_task.task;
437#else
438      if (RunningOnValgrind())
439        delete pending_task.task;
440#endif  // defined(OS_POSIX)
441    }
442  }
443  did_work |= !deferred_non_nestable_work_queue_.empty();
444  while (!deferred_non_nestable_work_queue_.empty()) {
445    // TODO(darin): Delete all tasks once it is safe to do so.
446    // Until it is totaly safe, only delete them under Purify and Valgrind.
447    Task* task = NULL;
448#if defined(PURIFY) || defined(USE_HEAPCHECKER)
449    task = deferred_non_nestable_work_queue_.front().task;
450#else
451    if (RunningOnValgrind())
452      task = deferred_non_nestable_work_queue_.front().task;
453#endif
454    deferred_non_nestable_work_queue_.pop();
455    if (task)
456      delete task;
457  }
458  did_work |= !delayed_work_queue_.empty();
459  while (!delayed_work_queue_.empty()) {
460    Task* task = delayed_work_queue_.top().task;
461    delayed_work_queue_.pop();
462    delete task;
463  }
464  return did_work;
465}
466
467// Possibly called on a background thread!
468void MessageLoop::PostTask_Helper(
469    const tracked_objects::Location& from_here, Task* task, int64 delay_ms,
470    bool nestable) {
471  task->SetBirthPlace(from_here);
472
473  PendingTask pending_task(task, nestable);
474
475  if (delay_ms > 0) {
476    pending_task.delayed_run_time =
477        TimeTicks::Now() + TimeDelta::FromMilliseconds(delay_ms);
478
479#if defined(OS_WIN)
480    if (high_resolution_timer_expiration_.is_null()) {
481      // Windows timers are granular to 15.6ms.  If we only set high-res
482      // timers for those under 15.6ms, then a 18ms timer ticks at ~32ms,
483      // which as a percentage is pretty inaccurate.  So enable high
484      // res timers for any timer which is within 2x of the granularity.
485      // This is a tradeoff between accuracy and power management.
486      bool needs_high_res_timers =
487          delay_ms < (2 * base::Time::kMinLowResolutionThresholdMs);
488      if (needs_high_res_timers) {
489        base::Time::ActivateHighResolutionTimer(true);
490        high_resolution_timer_expiration_ = TimeTicks::Now() +
491            TimeDelta::FromMilliseconds(kHighResolutionTimerModeLeaseTimeMs);
492      }
493    }
494#endif
495  } else {
496    DCHECK_EQ(delay_ms, 0) << "delay should not be negative";
497  }
498
499#if defined(OS_WIN)
500  if (!high_resolution_timer_expiration_.is_null()) {
501    if (TimeTicks::Now() > high_resolution_timer_expiration_) {
502      base::Time::ActivateHighResolutionTimer(false);
503      high_resolution_timer_expiration_ = TimeTicks();
504    }
505  }
506#endif
507
508  // Warning: Don't try to short-circuit, and handle this thread's tasks more
509  // directly, as it could starve handling of foreign threads.  Put every task
510  // into this queue.
511
512  scoped_refptr<base::MessagePump> pump;
513  {
514    base::AutoLock locked(incoming_queue_lock_);
515
516    bool was_empty = incoming_queue_.empty();
517    incoming_queue_.push(pending_task);
518    if (!was_empty)
519      return;  // Someone else should have started the sub-pump.
520
521    pump = pump_;
522  }
523  // Since the incoming_queue_ may contain a task that destroys this message
524  // loop, we cannot exit incoming_queue_lock_ until we are done with |this|.
525  // We use a stack-based reference to the message pump so that we can call
526  // ScheduleWork outside of incoming_queue_lock_.
527
528  pump->ScheduleWork();
529}
530
531//------------------------------------------------------------------------------
532// Method and data for histogramming events and actions taken by each instance
533// on each thread.
534
535void MessageLoop::StartHistogrammer() {
536  if (enable_histogrammer_ && !message_histogram_.get()
537      && base::StatisticsRecorder::IsActive()) {
538    DCHECK(!thread_name_.empty());
539    message_histogram_ = base::LinearHistogram::FactoryGet(
540        "MsgLoop:" + thread_name_,
541        kLeastNonZeroMessageId, kMaxMessageId,
542        kNumberOfDistinctMessagesDisplayed,
543        message_histogram_->kHexRangePrintingFlag);
544    message_histogram_->SetRangeDescriptions(event_descriptions_);
545  }
546}
547
548void MessageLoop::HistogramEvent(int event) {
549  if (message_histogram_.get())
550    message_histogram_->Add(event);
551}
552
553bool MessageLoop::DoWork() {
554  if (!nestable_tasks_allowed_) {
555    // Task can't be executed right now.
556    return false;
557  }
558
559  for (;;) {
560    ReloadWorkQueue();
561    if (work_queue_.empty())
562      break;
563
564    // Execute oldest task.
565    do {
566      PendingTask pending_task = work_queue_.front();
567      work_queue_.pop();
568      if (!pending_task.delayed_run_time.is_null()) {
569        AddToDelayedWorkQueue(pending_task);
570        // If we changed the topmost task, then it is time to re-schedule.
571        if (delayed_work_queue_.top().task == pending_task.task)
572          pump_->ScheduleDelayedWork(pending_task.delayed_run_time);
573      } else {
574        if (DeferOrRunPendingTask(pending_task))
575          return true;
576      }
577    } while (!work_queue_.empty());
578  }
579
580  // Nothing happened.
581  return false;
582}
583
584bool MessageLoop::DoDelayedWork(base::TimeTicks* next_delayed_work_time) {
585  if (!nestable_tasks_allowed_ || delayed_work_queue_.empty()) {
586    recent_time_ = *next_delayed_work_time = TimeTicks();
587    return false;
588  }
589
590  // When we "fall behind," there will be a lot of tasks in the delayed work
591  // queue that are ready to run.  To increase efficiency when we fall behind,
592  // we will only call Time::Now() intermittently, and then process all tasks
593  // that are ready to run before calling it again.  As a result, the more we
594  // fall behind (and have a lot of ready-to-run delayed tasks), the more
595  // efficient we'll be at handling the tasks.
596
597  TimeTicks next_run_time = delayed_work_queue_.top().delayed_run_time;
598  if (next_run_time > recent_time_) {
599    recent_time_ = TimeTicks::Now();  // Get a better view of Now();
600    if (next_run_time > recent_time_) {
601      *next_delayed_work_time = next_run_time;
602      return false;
603    }
604  }
605
606  PendingTask pending_task = delayed_work_queue_.top();
607  delayed_work_queue_.pop();
608
609  if (!delayed_work_queue_.empty())
610    *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time;
611
612  return DeferOrRunPendingTask(pending_task);
613}
614
615bool MessageLoop::DoIdleWork() {
616  if (ProcessNextDelayedNonNestableTask())
617    return true;
618
619  if (state_->quit_received)
620    pump_->Quit();
621
622  return false;
623}
624
625//------------------------------------------------------------------------------
626// MessageLoop::AutoRunState
627
628MessageLoop::AutoRunState::AutoRunState(MessageLoop* loop) : loop_(loop) {
629  // Make the loop reference us.
630  previous_state_ = loop_->state_;
631  if (previous_state_) {
632    run_depth = previous_state_->run_depth + 1;
633  } else {
634    run_depth = 1;
635  }
636  loop_->state_ = this;
637
638  // Initialize the other fields:
639  quit_received = false;
640#if !defined(OS_MACOSX)
641  dispatcher = NULL;
642#endif
643}
644
645MessageLoop::AutoRunState::~AutoRunState() {
646  loop_->state_ = previous_state_;
647}
648
649//------------------------------------------------------------------------------
650// MessageLoop::PendingTask
651
652bool MessageLoop::PendingTask::operator<(const PendingTask& other) const {
653  // Since the top of a priority queue is defined as the "greatest" element, we
654  // need to invert the comparison here.  We want the smaller time to be at the
655  // top of the heap.
656
657  if (delayed_run_time < other.delayed_run_time)
658    return false;
659
660  if (delayed_run_time > other.delayed_run_time)
661    return true;
662
663  // If the times happen to match, then we use the sequence number to decide.
664  // Compare the difference to support integer roll-over.
665  return (sequence_num - other.sequence_num) > 0;
666}
667
668//------------------------------------------------------------------------------
669// MessageLoopForUI
670
671#if defined(OS_WIN)
672void MessageLoopForUI::DidProcessMessage(const MSG& message) {
673  pump_win()->DidProcessMessage(message);
674}
675#endif  // defined(OS_WIN)
676
677#if defined(USE_X11)
678Display* MessageLoopForUI::GetDisplay() {
679  return gdk_x11_get_default_xdisplay();
680}
681#endif  // defined(USE_X11)
682
683#if !defined(OS_MACOSX) && !defined(OS_NACL) && !defined(ANDROID)
684void MessageLoopForUI::AddObserver(Observer* observer) {
685  pump_ui()->AddObserver(observer);
686}
687
688void MessageLoopForUI::RemoveObserver(Observer* observer) {
689  pump_ui()->RemoveObserver(observer);
690}
691
692void MessageLoopForUI::Run(Dispatcher* dispatcher) {
693  AutoRunState save_state(this);
694  state_->dispatcher = dispatcher;
695  RunHandler();
696}
697#endif  // !defined(OS_MACOSX) && !defined(OS_NACL)
698
699//------------------------------------------------------------------------------
700// MessageLoopForIO
701
702#if defined(OS_WIN)
703
704void MessageLoopForIO::RegisterIOHandler(HANDLE file, IOHandler* handler) {
705  pump_io()->RegisterIOHandler(file, handler);
706}
707
708bool MessageLoopForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
709  return pump_io()->WaitForIOCompletion(timeout, filter);
710}
711
712#elif defined(OS_POSIX) && !defined(OS_NACL)
713
714bool MessageLoopForIO::WatchFileDescriptor(int fd,
715                                           bool persistent,
716                                           Mode mode,
717                                           FileDescriptorWatcher *controller,
718                                           Watcher *delegate) {
719  return pump_libevent()->WatchFileDescriptor(
720      fd,
721      persistent,
722      static_cast<base::MessagePumpLibevent::Mode>(mode),
723      controller,
724      delegate);
725}
726
727#endif
728