1// Copyright 2013 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/message_loop.h"
6
7#include <algorithm>
8
9#include "base/bind.h"
10#include "base/compiler_specific.h"
11#include "base/lazy_instance.h"
12#include "base/logging.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/message_loop/message_pump_default.h"
15#include "base/metrics/histogram.h"
16#include "base/metrics/statistics_recorder.h"
17#include "base/run_loop.h"
18#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
19#include "base/thread_task_runner_handle.h"
20#include "base/threading/thread_local.h"
21#include "base/time/time.h"
22#include "base/tracked_objects.h"
23
24#if defined(OS_MACOSX)
25#include "base/message_loop/message_pump_mac.h"
26#endif
27#if defined(OS_POSIX) && !defined(OS_IOS)
28#include "base/message_loop/message_pump_libevent.h"
29#endif
30#if defined(OS_ANDROID)
31#include "base/message_loop/message_pump_android.h"
32#endif
33#if defined(USE_GLIB)
34#include "base/message_loop/message_pump_glib.h"
35#endif
36
37namespace base {
38
39namespace {
40
41// A lazily created thread local storage for quick access to a thread's message
42// loop, if one exists.  This should be safe and free of static constructors.
43LazyInstance<base::ThreadLocalPointer<MessageLoop> >::Leaky lazy_tls_ptr =
44    LAZY_INSTANCE_INITIALIZER;
45
46// Logical events for Histogram profiling. Run with -message-loop-histogrammer
47// to get an accounting of messages and actions taken on each thread.
48const int kTaskRunEvent = 0x1;
49#if !defined(OS_NACL)
50const int kTimerEvent = 0x2;
51
52// Provide range of message IDs for use in histogramming and debug display.
53const int kLeastNonZeroMessageId = 1;
54const int kMaxMessageId = 1099;
55const int kNumberOfDistinctMessagesDisplayed = 1100;
56
57// Provide a macro that takes an expression (such as a constant, or macro
58// constant) and creates a pair to initalize an array of pairs.  In this case,
59// our pair consists of the expressions value, and the "stringized" version
60// of the expression (i.e., the exrpression put in quotes).  For example, if
61// we have:
62//    #define FOO 2
63//    #define BAR 5
64// then the following:
65//    VALUE_TO_NUMBER_AND_NAME(FOO + BAR)
66// will expand to:
67//   {7, "FOO + BAR"}
68// We use the resulting array as an argument to our histogram, which reads the
69// number as a bucket identifier, and proceeds to use the corresponding name
70// in the pair (i.e., the quoted string) when printing out a histogram.
71#define VALUE_TO_NUMBER_AND_NAME(name) {name, #name},
72
73const LinearHistogram::DescriptionPair event_descriptions_[] = {
74  // Provide some pretty print capability in our histogram for our internal
75  // messages.
76
77  // A few events we handle (kindred to messages), and used to profile actions.
78  VALUE_TO_NUMBER_AND_NAME(kTaskRunEvent)
79  VALUE_TO_NUMBER_AND_NAME(kTimerEvent)
80
81  {-1, NULL}  // The list must be null terminated, per API to histogram.
82};
83#endif  // !defined(OS_NACL)
84
85bool enable_histogrammer_ = false;
86
87MessageLoop::MessagePumpFactory* message_pump_for_ui_factory_ = NULL;
88
89// Returns true if MessagePump::ScheduleWork() must be called one
90// time for every task that is added to the MessageLoop incoming queue.
91bool AlwaysNotifyPump(MessageLoop::Type type) {
92#if defined(OS_ANDROID)
93  // The Android UI message loop needs to get notified each time a task is added
94  // to the incoming queue.
95  return type == MessageLoop::TYPE_UI || type == MessageLoop::TYPE_JAVA;
96#else
97  return false;
98#endif
99}
100
101#if defined(OS_IOS)
102typedef MessagePumpIOSForIO MessagePumpForIO;
103#elif defined(OS_NACL)
104typedef MessagePumpDefault MessagePumpForIO;
105#elif defined(OS_POSIX)
106typedef MessagePumpLibevent MessagePumpForIO;
107#endif
108
109MessagePumpForIO* ToPumpIO(MessagePump* pump) {
110  return static_cast<MessagePumpForIO*>(pump);
111}
112
113}  // namespace
114
115//------------------------------------------------------------------------------
116
117MessageLoop::TaskObserver::TaskObserver() {
118}
119
120MessageLoop::TaskObserver::~TaskObserver() {
121}
122
123MessageLoop::DestructionObserver::~DestructionObserver() {
124}
125
126//------------------------------------------------------------------------------
127
128MessageLoop::MessageLoop(Type type)
129    : type_(type),
130      pending_high_res_tasks_(0),
131      in_high_res_mode_(false),
132      nestable_tasks_allowed_(true),
133#if defined(OS_WIN)
134      os_modal_loop_(false),
135#endif  // OS_WIN
136      message_histogram_(NULL),
137      run_loop_(NULL) {
138  Init();
139
140  pump_ = CreateMessagePumpForType(type).Pass();
141}
142
143MessageLoop::MessageLoop(scoped_ptr<MessagePump> pump)
144    : pump_(pump.Pass()),
145      type_(TYPE_CUSTOM),
146      pending_high_res_tasks_(0),
147      in_high_res_mode_(false),
148      nestable_tasks_allowed_(true),
149#if defined(OS_WIN)
150      os_modal_loop_(false),
151#endif  // OS_WIN
152      message_histogram_(NULL),
153      run_loop_(NULL) {
154  DCHECK(pump_.get());
155  Init();
156}
157
158MessageLoop::~MessageLoop() {
159  DCHECK_EQ(this, current());
160
161  DCHECK(!run_loop_);
162#if defined(OS_WIN)
163  if (in_high_res_mode_)
164    Time::ActivateHighResolutionTimer(false);
165#endif
166  // Clean up any unprocessed tasks, but take care: deleting a task could
167  // result in the addition of more tasks (e.g., via DeleteSoon).  We set a
168  // limit on the number of times we will allow a deleted task to generate more
169  // tasks.  Normally, we should only pass through this loop once or twice.  If
170  // we end up hitting the loop limit, then it is probably due to one task that
171  // is being stubborn.  Inspect the queues to see who is left.
172  bool did_work;
173  for (int i = 0; i < 100; ++i) {
174    DeletePendingTasks();
175    ReloadWorkQueue();
176    // If we end up with empty queues, then break out of the loop.
177    did_work = DeletePendingTasks();
178    if (!did_work)
179      break;
180  }
181  DCHECK(!did_work);
182
183  // Let interested parties have one last shot at accessing this.
184  FOR_EACH_OBSERVER(DestructionObserver, destruction_observers_,
185                    WillDestroyCurrentMessageLoop());
186
187  thread_task_runner_handle_.reset();
188
189  // Tell the incoming queue that we are dying.
190  incoming_task_queue_->WillDestroyCurrentMessageLoop();
191  incoming_task_queue_ = NULL;
192  message_loop_proxy_ = NULL;
193
194  // OK, now make it so that no one can find us.
195  lazy_tls_ptr.Pointer()->Set(NULL);
196}
197
198// static
199MessageLoop* MessageLoop::current() {
200  // TODO(darin): sadly, we cannot enable this yet since people call us even
201  // when they have no intention of using us.
202  // DCHECK(loop) << "Ouch, did you forget to initialize me?";
203  return lazy_tls_ptr.Pointer()->Get();
204}
205
206// static
207void MessageLoop::EnableHistogrammer(bool enable) {
208  enable_histogrammer_ = enable;
209}
210
211// static
212bool MessageLoop::InitMessagePumpForUIFactory(MessagePumpFactory* factory) {
213  if (message_pump_for_ui_factory_)
214    return false;
215
216  message_pump_for_ui_factory_ = factory;
217  return true;
218}
219
220// static
221scoped_ptr<MessagePump> MessageLoop::CreateMessagePumpForType(Type type) {
222// TODO(rvargas): Get rid of the OS guards.
223#if defined(USE_GLIB) && !defined(OS_NACL)
224  typedef MessagePumpGlib MessagePumpForUI;
225#elif defined(OS_LINUX) && !defined(OS_NACL)
226  typedef MessagePumpLibevent MessagePumpForUI;
227#endif
228
229#if defined(OS_IOS) || defined(OS_MACOSX)
230#define MESSAGE_PUMP_UI scoped_ptr<MessagePump>(MessagePumpMac::Create())
231#elif defined(OS_NACL)
232// Currently NaCl doesn't have a UI MessageLoop.
233// TODO(abarth): Figure out if we need this.
234#define MESSAGE_PUMP_UI scoped_ptr<MessagePump>()
235#else
236#define MESSAGE_PUMP_UI scoped_ptr<MessagePump>(new MessagePumpForUI())
237#endif
238
239#if defined(OS_MACOSX)
240  // Use an OS native runloop on Mac to support timer coalescing.
241  #define MESSAGE_PUMP_DEFAULT \
242      scoped_ptr<MessagePump>(new MessagePumpCFRunLoop())
243#else
244  #define MESSAGE_PUMP_DEFAULT scoped_ptr<MessagePump>(new MessagePumpDefault())
245#endif
246
247  if (type == MessageLoop::TYPE_UI) {
248    if (message_pump_for_ui_factory_)
249      return message_pump_for_ui_factory_();
250    return MESSAGE_PUMP_UI;
251  }
252  if (type == MessageLoop::TYPE_IO)
253    return scoped_ptr<MessagePump>(new MessagePumpForIO());
254
255#if defined(OS_ANDROID)
256  if (type == MessageLoop::TYPE_JAVA)
257    return scoped_ptr<MessagePump>(new MessagePumpForUI());
258#endif
259
260  DCHECK_EQ(MessageLoop::TYPE_DEFAULT, type);
261  return MESSAGE_PUMP_DEFAULT;
262}
263
264void MessageLoop::AddDestructionObserver(
265    DestructionObserver* destruction_observer) {
266  DCHECK_EQ(this, current());
267  destruction_observers_.AddObserver(destruction_observer);
268}
269
270void MessageLoop::RemoveDestructionObserver(
271    DestructionObserver* destruction_observer) {
272  DCHECK_EQ(this, current());
273  destruction_observers_.RemoveObserver(destruction_observer);
274}
275
276void MessageLoop::PostTask(
277    const tracked_objects::Location& from_here,
278    const Closure& task) {
279  DCHECK(!task.is_null()) << from_here.ToString();
280  incoming_task_queue_->AddToIncomingQueue(from_here, task, TimeDelta(), true);
281}
282
283void MessageLoop::PostDelayedTask(
284    const tracked_objects::Location& from_here,
285    const Closure& task,
286    TimeDelta delay) {
287  DCHECK(!task.is_null()) << from_here.ToString();
288  incoming_task_queue_->AddToIncomingQueue(from_here, task, delay, true);
289}
290
291void MessageLoop::PostNonNestableTask(
292    const tracked_objects::Location& from_here,
293    const Closure& task) {
294  DCHECK(!task.is_null()) << from_here.ToString();
295  incoming_task_queue_->AddToIncomingQueue(from_here, task, TimeDelta(), false);
296}
297
298void MessageLoop::PostNonNestableDelayedTask(
299    const tracked_objects::Location& from_here,
300    const Closure& task,
301    TimeDelta delay) {
302  DCHECK(!task.is_null()) << from_here.ToString();
303  incoming_task_queue_->AddToIncomingQueue(from_here, task, delay, false);
304}
305
306void MessageLoop::Run() {
307  RunLoop run_loop;
308  run_loop.Run();
309}
310
311void MessageLoop::RunUntilIdle() {
312  RunLoop run_loop;
313  run_loop.RunUntilIdle();
314}
315
316void MessageLoop::QuitWhenIdle() {
317  DCHECK_EQ(this, current());
318  if (run_loop_) {
319    run_loop_->quit_when_idle_received_ = true;
320  } else {
321    NOTREACHED() << "Must be inside Run to call Quit";
322  }
323}
324
325void MessageLoop::QuitNow() {
326  DCHECK_EQ(this, current());
327  if (run_loop_) {
328    pump_->Quit();
329  } else {
330    NOTREACHED() << "Must be inside Run to call Quit";
331  }
332}
333
334bool MessageLoop::IsType(Type type) const {
335  return type_ == type;
336}
337
338static void QuitCurrentWhenIdle() {
339  MessageLoop::current()->QuitWhenIdle();
340}
341
342// static
343Closure MessageLoop::QuitWhenIdleClosure() {
344  return Bind(&QuitCurrentWhenIdle);
345}
346
347void MessageLoop::SetNestableTasksAllowed(bool allowed) {
348  if (allowed) {
349    // Kick the native pump just in case we enter a OS-driven nested message
350    // loop.
351    pump_->ScheduleWork();
352  }
353  nestable_tasks_allowed_ = allowed;
354}
355
356bool MessageLoop::NestableTasksAllowed() const {
357  return nestable_tasks_allowed_;
358}
359
360bool MessageLoop::IsNested() {
361  return run_loop_->run_depth_ > 1;
362}
363
364void MessageLoop::AddTaskObserver(TaskObserver* task_observer) {
365  DCHECK_EQ(this, current());
366  task_observers_.AddObserver(task_observer);
367}
368
369void MessageLoop::RemoveTaskObserver(TaskObserver* task_observer) {
370  DCHECK_EQ(this, current());
371  task_observers_.RemoveObserver(task_observer);
372}
373
374bool MessageLoop::is_running() const {
375  DCHECK_EQ(this, current());
376  return run_loop_ != NULL;
377}
378
379bool MessageLoop::HasHighResolutionTasks() {
380  return incoming_task_queue_->HasHighResolutionTasks();
381}
382
383bool MessageLoop::IsIdleForTesting() {
384  // We only check the imcoming queue|, since we don't want to lock the work
385  // queue.
386  return incoming_task_queue_->IsIdleForTesting();
387}
388
389//------------------------------------------------------------------------------
390
391void MessageLoop::Init() {
392  DCHECK(!current()) << "should only have one message loop per thread";
393  lazy_tls_ptr.Pointer()->Set(this);
394
395  incoming_task_queue_ = new internal::IncomingTaskQueue(this);
396  message_loop_proxy_ =
397      new internal::MessageLoopProxyImpl(incoming_task_queue_);
398  thread_task_runner_handle_.reset(
399      new ThreadTaskRunnerHandle(message_loop_proxy_));
400}
401
402void MessageLoop::RunHandler() {
403  DCHECK_EQ(this, current());
404
405  StartHistogrammer();
406
407#if defined(OS_WIN)
408  if (run_loop_->dispatcher_ && type() == TYPE_UI) {
409    static_cast<MessagePumpForUI*>(pump_.get())->
410        RunWithDispatcher(this, run_loop_->dispatcher_);
411    return;
412  }
413#endif
414
415  pump_->Run(this);
416}
417
418bool MessageLoop::ProcessNextDelayedNonNestableTask() {
419  if (run_loop_->run_depth_ != 1)
420    return false;
421
422  if (deferred_non_nestable_work_queue_.empty())
423    return false;
424
425  PendingTask pending_task = deferred_non_nestable_work_queue_.front();
426  deferred_non_nestable_work_queue_.pop();
427
428  RunTask(pending_task);
429  return true;
430}
431
432void MessageLoop::RunTask(const PendingTask& pending_task) {
433  DCHECK(nestable_tasks_allowed_);
434
435  if (pending_task.is_high_res) {
436    pending_high_res_tasks_--;
437    CHECK(pending_high_res_tasks_ >= 0);
438  }
439  // Execute the task and assume the worst: It is probably not reentrant.
440  nestable_tasks_allowed_ = false;
441
442  HistogramEvent(kTaskRunEvent);
443
444  FOR_EACH_OBSERVER(TaskObserver, task_observers_,
445                    WillProcessTask(pending_task));
446  task_annotator_.RunTask(
447      "MessageLoop::PostTask", "MessageLoop::RunTask", pending_task);
448  FOR_EACH_OBSERVER(TaskObserver, task_observers_,
449                    DidProcessTask(pending_task));
450
451  nestable_tasks_allowed_ = true;
452}
453
454bool MessageLoop::DeferOrRunPendingTask(const PendingTask& pending_task) {
455  if (pending_task.nestable || run_loop_->run_depth_ == 1) {
456    RunTask(pending_task);
457    // Show that we ran a task (Note: a new one might arrive as a
458    // consequence!).
459    return true;
460  }
461
462  // We couldn't run the task now because we're in a nested message loop
463  // and the task isn't nestable.
464  deferred_non_nestable_work_queue_.push(pending_task);
465  return false;
466}
467
468void MessageLoop::AddToDelayedWorkQueue(const PendingTask& pending_task) {
469  // Move to the delayed work queue.
470  delayed_work_queue_.push(pending_task);
471}
472
473bool MessageLoop::DeletePendingTasks() {
474  bool did_work = !work_queue_.empty();
475  while (!work_queue_.empty()) {
476    PendingTask pending_task = work_queue_.front();
477    work_queue_.pop();
478    if (!pending_task.delayed_run_time.is_null()) {
479      // We want to delete delayed tasks in the same order in which they would
480      // normally be deleted in case of any funny dependencies between delayed
481      // tasks.
482      AddToDelayedWorkQueue(pending_task);
483    }
484  }
485  did_work |= !deferred_non_nestable_work_queue_.empty();
486  while (!deferred_non_nestable_work_queue_.empty()) {
487    deferred_non_nestable_work_queue_.pop();
488  }
489  did_work |= !delayed_work_queue_.empty();
490
491  // Historically, we always delete the task regardless of valgrind status. It's
492  // not completely clear why we want to leak them in the loops above.  This
493  // code is replicating legacy behavior, and should not be considered
494  // absolutely "correct" behavior.  See TODO above about deleting all tasks
495  // when it's safe.
496  while (!delayed_work_queue_.empty()) {
497    delayed_work_queue_.pop();
498  }
499  return did_work;
500}
501
502void MessageLoop::ReloadWorkQueue() {
503  // We can improve performance of our loading tasks from the incoming queue to
504  // |*work_queue| by waiting until the last minute (|*work_queue| is empty) to
505  // load. That reduces the number of locks-per-task significantly when our
506  // queues get large.
507  if (work_queue_.empty()) {
508    pending_high_res_tasks_ +=
509        incoming_task_queue_->ReloadWorkQueue(&work_queue_);
510  }
511}
512
513void MessageLoop::ScheduleWork(bool was_empty) {
514  if (was_empty || AlwaysNotifyPump(type_))
515    pump_->ScheduleWork();
516}
517
518//------------------------------------------------------------------------------
519// Method and data for histogramming events and actions taken by each instance
520// on each thread.
521
522void MessageLoop::StartHistogrammer() {
523#if !defined(OS_NACL)  // NaCl build has no metrics code.
524  if (enable_histogrammer_ && !message_histogram_
525      && StatisticsRecorder::IsActive()) {
526    DCHECK(!thread_name_.empty());
527    message_histogram_ = LinearHistogram::FactoryGetWithRangeDescription(
528        "MsgLoop:" + thread_name_,
529        kLeastNonZeroMessageId, kMaxMessageId,
530        kNumberOfDistinctMessagesDisplayed,
531        message_histogram_->kHexRangePrintingFlag,
532        event_descriptions_);
533  }
534#endif
535}
536
537void MessageLoop::HistogramEvent(int event) {
538#if !defined(OS_NACL)
539  if (message_histogram_)
540    message_histogram_->Add(event);
541#endif
542}
543
544bool MessageLoop::DoWork() {
545  if (!nestable_tasks_allowed_) {
546    // Task can't be executed right now.
547    return false;
548  }
549
550  for (;;) {
551    ReloadWorkQueue();
552    if (work_queue_.empty())
553      break;
554
555    // Execute oldest task.
556    do {
557      PendingTask pending_task = work_queue_.front();
558      work_queue_.pop();
559      if (!pending_task.delayed_run_time.is_null()) {
560        AddToDelayedWorkQueue(pending_task);
561        // If we changed the topmost task, then it is time to reschedule.
562        if (delayed_work_queue_.top().task.Equals(pending_task.task))
563          pump_->ScheduleDelayedWork(pending_task.delayed_run_time);
564      } else {
565        if (DeferOrRunPendingTask(pending_task))
566          return true;
567      }
568    } while (!work_queue_.empty());
569  }
570
571  // Nothing happened.
572  return false;
573}
574
575bool MessageLoop::DoDelayedWork(TimeTicks* next_delayed_work_time) {
576  if (!nestable_tasks_allowed_ || delayed_work_queue_.empty()) {
577    recent_time_ = *next_delayed_work_time = TimeTicks();
578    return false;
579  }
580
581  // When we "fall behind," there will be a lot of tasks in the delayed work
582  // queue that are ready to run.  To increase efficiency when we fall behind,
583  // we will only call Time::Now() intermittently, and then process all tasks
584  // that are ready to run before calling it again.  As a result, the more we
585  // fall behind (and have a lot of ready-to-run delayed tasks), the more
586  // efficient we'll be at handling the tasks.
587
588  TimeTicks next_run_time = delayed_work_queue_.top().delayed_run_time;
589  if (next_run_time > recent_time_) {
590    recent_time_ = TimeTicks::Now();  // Get a better view of Now();
591    if (next_run_time > recent_time_) {
592      *next_delayed_work_time = next_run_time;
593      return false;
594    }
595  }
596
597  PendingTask pending_task = delayed_work_queue_.top();
598  delayed_work_queue_.pop();
599
600  if (!delayed_work_queue_.empty())
601    *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time;
602
603  return DeferOrRunPendingTask(pending_task);
604}
605
606bool MessageLoop::DoIdleWork() {
607  if (ProcessNextDelayedNonNestableTask())
608    return true;
609
610  if (run_loop_->quit_when_idle_received_)
611    pump_->Quit();
612
613  // When we return we will do a kernel wait for more tasks.
614#if defined(OS_WIN)
615  // On Windows we activate the high resolution timer so that the wait
616  // _if_ triggered by the timer happens with good resolution. If we don't
617  // do this the default resolution is 15ms which might not be acceptable
618  // for some tasks.
619  bool high_res = pending_high_res_tasks_ > 0;
620  if (high_res != in_high_res_mode_) {
621    in_high_res_mode_ = high_res;
622    Time::ActivateHighResolutionTimer(in_high_res_mode_);
623  }
624#endif
625  return false;
626}
627
628void MessageLoop::DeleteSoonInternal(const tracked_objects::Location& from_here,
629                                     void(*deleter)(const void*),
630                                     const void* object) {
631  PostNonNestableTask(from_here, Bind(deleter, object));
632}
633
634void MessageLoop::ReleaseSoonInternal(
635    const tracked_objects::Location& from_here,
636    void(*releaser)(const void*),
637    const void* object) {
638  PostNonNestableTask(from_here, Bind(releaser, object));
639}
640
641#if !defined(OS_NACL)
642//------------------------------------------------------------------------------
643// MessageLoopForUI
644
645#if defined(OS_ANDROID)
646void MessageLoopForUI::Start() {
647  // No Histogram support for UI message loop as it is managed by Java side
648  static_cast<MessagePumpForUI*>(pump_.get())->Start(this);
649}
650#endif
651
652#if defined(OS_IOS)
653void MessageLoopForUI::Attach() {
654  static_cast<MessagePumpUIApplication*>(pump_.get())->Attach(this);
655}
656#endif
657
658#if defined(USE_OZONE) || (defined(USE_X11) && !defined(USE_GLIB))
659bool MessageLoopForUI::WatchFileDescriptor(
660    int fd,
661    bool persistent,
662    MessagePumpLibevent::Mode mode,
663    MessagePumpLibevent::FileDescriptorWatcher *controller,
664    MessagePumpLibevent::Watcher *delegate) {
665  return static_cast<MessagePumpLibevent*>(pump_.get())->WatchFileDescriptor(
666      fd,
667      persistent,
668      mode,
669      controller,
670      delegate);
671}
672#endif
673
674#endif  // !defined(OS_NACL)
675
676//------------------------------------------------------------------------------
677// MessageLoopForIO
678
679#if !defined(OS_NACL)
680void MessageLoopForIO::AddIOObserver(
681    MessageLoopForIO::IOObserver* io_observer) {
682  ToPumpIO(pump_.get())->AddIOObserver(io_observer);
683}
684
685void MessageLoopForIO::RemoveIOObserver(
686    MessageLoopForIO::IOObserver* io_observer) {
687  ToPumpIO(pump_.get())->RemoveIOObserver(io_observer);
688}
689
690#if defined(OS_WIN)
691void MessageLoopForIO::RegisterIOHandler(HANDLE file, IOHandler* handler) {
692  ToPumpIO(pump_.get())->RegisterIOHandler(file, handler);
693}
694
695bool MessageLoopForIO::RegisterJobObject(HANDLE job, IOHandler* handler) {
696  return ToPumpIO(pump_.get())->RegisterJobObject(job, handler);
697}
698
699bool MessageLoopForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
700  return ToPumpIO(pump_.get())->WaitForIOCompletion(timeout, filter);
701}
702#elif defined(OS_POSIX)
703bool MessageLoopForIO::WatchFileDescriptor(int fd,
704                                           bool persistent,
705                                           Mode mode,
706                                           FileDescriptorWatcher *controller,
707                                           Watcher *delegate) {
708  return ToPumpIO(pump_.get())->WatchFileDescriptor(
709      fd,
710      persistent,
711      mode,
712      controller,
713      delegate);
714}
715#endif
716
717#endif  // !defined(OS_NACL)
718
719}  // namespace base
720