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