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