message_loop.cc revision 513209b27ff55e2841eac0e4120199c23acce758
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<<<<<<< HEAD
142#elif defined(ANDROID)
143#define MESSAGE_PUMP_UI new base::MessagePumpDefault()
144=======
145#elif defined(TOUCH_UI)
146// TODO(sadrul): enable the new message pump when ready
147#define MESSAGE_PUMP_UI new base::MessagePumpForUI()
148>>>>>>> chromium.org at r65505
149#define MESSAGE_PUMP_IO new base::MessagePumpLibevent()
150#elif defined(OS_POSIX)  // POSIX but not MACOSX.
151#define MESSAGE_PUMP_UI new base::MessagePumpForUI()
152#define MESSAGE_PUMP_IO new base::MessagePumpLibevent()
153#else
154#error Not implemented
155#endif
156
157  if (type_ == TYPE_UI) {
158    pump_ = MESSAGE_PUMP_UI;
159  } else if (type_ == TYPE_IO) {
160    pump_ = MESSAGE_PUMP_IO;
161  } else {
162    DCHECK_EQ(TYPE_DEFAULT, type_);
163    pump_ = new base::MessagePumpDefault();
164  }
165}
166
167MessageLoop::~MessageLoop() {
168  DCHECK(this == current());
169
170  // Let interested parties have one last shot at accessing this.
171  FOR_EACH_OBSERVER(DestructionObserver, destruction_observers_,
172                    WillDestroyCurrentMessageLoop());
173
174  DCHECK(!state_);
175
176  // Clean up any unprocessed tasks, but take care: deleting a task could
177  // result in the addition of more tasks (e.g., via DeleteSoon).  We set a
178  // limit on the number of times we will allow a deleted task to generate more
179  // tasks.  Normally, we should only pass through this loop once or twice.  If
180  // we end up hitting the loop limit, then it is probably due to one task that
181  // is being stubborn.  Inspect the queues to see who is left.
182  bool did_work;
183  for (int i = 0; i < 100; ++i) {
184    DeletePendingTasks();
185    ReloadWorkQueue();
186    // If we end up with empty queues, then break out of the loop.
187    did_work = DeletePendingTasks();
188    if (!did_work)
189      break;
190  }
191  DCHECK(!did_work);
192
193  // OK, now make it so that no one can find us.
194  lazy_tls_ptr.Pointer()->Set(NULL);
195}
196
197void MessageLoop::AddDestructionObserver(
198    DestructionObserver* destruction_observer) {
199  DCHECK(this == current());
200  destruction_observers_.AddObserver(destruction_observer);
201}
202
203void MessageLoop::RemoveDestructionObserver(
204    DestructionObserver* destruction_observer) {
205  DCHECK(this == current());
206  destruction_observers_.RemoveObserver(destruction_observer);
207}
208
209void MessageLoop::AddTaskObserver(TaskObserver* task_observer) {
210  DCHECK_EQ(this, current());
211  task_observers_.AddObserver(task_observer);
212}
213
214void MessageLoop::RemoveTaskObserver(TaskObserver* task_observer) {
215  DCHECK_EQ(this, current());
216  task_observers_.RemoveObserver(task_observer);
217}
218
219void MessageLoop::Run() {
220  AutoRunState save_state(this);
221  RunHandler();
222}
223
224void MessageLoop::RunAllPending() {
225  AutoRunState save_state(this);
226  state_->quit_received = true;  // Means run until we would otherwise block.
227  RunHandler();
228}
229
230// Runs the loop in two different SEH modes:
231// enable_SEH_restoration_ = false : any unhandled exception goes to the last
232// one that calls SetUnhandledExceptionFilter().
233// enable_SEH_restoration_ = true : any unhandled exception goes to the filter
234// that was existed before the loop was run.
235void MessageLoop::RunHandler() {
236#if defined(OS_WIN)
237  if (exception_restoration_) {
238    RunInternalInSEHFrame();
239    return;
240  }
241#endif
242
243  RunInternal();
244}
245//------------------------------------------------------------------------------
246#if defined(OS_WIN)
247__declspec(noinline) void MessageLoop::RunInternalInSEHFrame() {
248  LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter();
249  __try {
250    RunInternal();
251  } __except(SEHFilter(current_filter)) {
252  }
253  return;
254}
255#endif
256//------------------------------------------------------------------------------
257
258void MessageLoop::RunInternal() {
259  DCHECK(this == current());
260
261#ifndef ANDROID
262  StartHistogrammer();
263#endif
264
265#if !defined(OS_MACOSX)
266  if (state_->dispatcher && type() == TYPE_UI) {
267    static_cast<base::MessagePumpForUI*>(pump_.get())->
268        RunWithDispatcher(this, state_->dispatcher);
269    return;
270  }
271#endif
272
273  pump_->Run(this);
274}
275
276//------------------------------------------------------------------------------
277// Wrapper functions for use in above message loop framework.
278
279bool MessageLoop::ProcessNextDelayedNonNestableTask() {
280  if (state_->run_depth != 1)
281    return false;
282
283  if (deferred_non_nestable_work_queue_.empty())
284    return false;
285
286  Task* task = deferred_non_nestable_work_queue_.front().task;
287  deferred_non_nestable_work_queue_.pop();
288
289  RunTask(task);
290  return true;
291}
292
293//------------------------------------------------------------------------------
294
295void MessageLoop::Quit() {
296  DCHECK(current() == this);
297  if (state_) {
298    state_->quit_received = true;
299  } else {
300    NOTREACHED() << "Must be inside Run to call Quit";
301  }
302}
303
304void MessageLoop::QuitNow() {
305  DCHECK(current() == this);
306  if (state_) {
307    pump_->Quit();
308  } else {
309    NOTREACHED() << "Must be inside Run to call Quit";
310  }
311}
312
313void MessageLoop::PostTask(
314    const tracked_objects::Location& from_here, Task* task) {
315  PostTask_Helper(from_here, task, 0, true);
316}
317
318void MessageLoop::PostDelayedTask(
319    const tracked_objects::Location& from_here, Task* task, int64 delay_ms) {
320  PostTask_Helper(from_here, task, delay_ms, true);
321}
322
323void MessageLoop::PostNonNestableTask(
324    const tracked_objects::Location& from_here, Task* task) {
325  PostTask_Helper(from_here, task, 0, false);
326}
327
328void MessageLoop::PostNonNestableDelayedTask(
329    const tracked_objects::Location& from_here, Task* task, int64 delay_ms) {
330  PostTask_Helper(from_here, task, delay_ms, false);
331}
332
333// Possibly called on a background thread!
334void MessageLoop::PostTask_Helper(
335    const tracked_objects::Location& from_here, Task* task, int64 delay_ms,
336    bool nestable) {
337  task->SetBirthPlace(from_here);
338
339  PendingTask pending_task(task, nestable);
340
341  if (delay_ms > 0) {
342    pending_task.delayed_run_time =
343        TimeTicks::Now() + TimeDelta::FromMilliseconds(delay_ms);
344
345#if defined(OS_WIN)
346    if (high_resolution_timer_expiration_.is_null()) {
347      // Windows timers are granular to 15.6ms.  If we only set high-res
348      // timers for those under 15.6ms, then a 18ms timer ticks at ~32ms,
349      // which as a percentage is pretty inaccurate.  So enable high
350      // res timers for any timer which is within 2x of the granularity.
351      // This is a tradeoff between accuracy and power management.
352      bool needs_high_res_timers =
353          delay_ms < (2 * Time::kMinLowResolutionThresholdMs);
354      if (needs_high_res_timers) {
355        Time::ActivateHighResolutionTimer(true);
356        high_resolution_timer_expiration_ = TimeTicks::Now() +
357            TimeDelta::FromMilliseconds(kHighResolutionTimerModeLeaseTimeMs);
358      }
359    }
360#endif
361  } else {
362    DCHECK_EQ(delay_ms, 0) << "delay should not be negative";
363  }
364
365#if defined(OS_WIN)
366  if (!high_resolution_timer_expiration_.is_null()) {
367    if (TimeTicks::Now() > high_resolution_timer_expiration_) {
368      Time::ActivateHighResolutionTimer(false);
369      high_resolution_timer_expiration_ = TimeTicks();
370    }
371  }
372#endif
373
374  // Warning: Don't try to short-circuit, and handle this thread's tasks more
375  // directly, as it could starve handling of foreign threads.  Put every task
376  // into this queue.
377
378  scoped_refptr<base::MessagePump> pump;
379  {
380    AutoLock locked(incoming_queue_lock_);
381
382    bool was_empty = incoming_queue_.empty();
383    incoming_queue_.push(pending_task);
384    if (!was_empty)
385      return;  // Someone else should have started the sub-pump.
386
387    pump = pump_;
388  }
389  // Since the incoming_queue_ may contain a task that destroys this message
390  // loop, we cannot exit incoming_queue_lock_ until we are done with |this|.
391  // We use a stack-based reference to the message pump so that we can call
392  // ScheduleWork outside of incoming_queue_lock_.
393
394  pump->ScheduleWork();
395}
396
397void MessageLoop::SetNestableTasksAllowed(bool allowed) {
398  if (nestable_tasks_allowed_ != allowed) {
399    nestable_tasks_allowed_ = allowed;
400    if (!nestable_tasks_allowed_)
401      return;
402    // Start the native pump if we are not already pumping.
403    pump_->ScheduleWork();
404  }
405}
406
407bool MessageLoop::NestableTasksAllowed() const {
408  return nestable_tasks_allowed_;
409}
410
411bool MessageLoop::IsNested() {
412  return state_->run_depth > 1;
413}
414
415//------------------------------------------------------------------------------
416
417void MessageLoop::RunTask(Task* task) {
418  DCHECK(nestable_tasks_allowed_);
419  // Execute the task and assume the worst: It is probably not reentrant.
420  nestable_tasks_allowed_ = false;
421
422  HistogramEvent(kTaskRunEvent);
423  FOR_EACH_OBSERVER(TaskObserver, task_observers_,
424                    WillProcessTask(task));
425  task->Run();
426  FOR_EACH_OBSERVER(TaskObserver, task_observers_, DidProcessTask(task));
427  delete task;
428
429  nestable_tasks_allowed_ = true;
430}
431
432bool MessageLoop::DeferOrRunPendingTask(const PendingTask& pending_task) {
433  if (pending_task.nestable || state_->run_depth == 1) {
434    RunTask(pending_task.task);
435    // Show that we ran a task (Note: a new one might arrive as a
436    // consequence!).
437    return true;
438  }
439
440  // We couldn't run the task now because we're in a nested message loop
441  // and the task isn't nestable.
442  deferred_non_nestable_work_queue_.push(pending_task);
443  return false;
444}
445
446void MessageLoop::AddToDelayedWorkQueue(const PendingTask& pending_task) {
447  // Move to the delayed work queue.  Initialize the sequence number
448  // before inserting into the delayed_work_queue_.  The sequence number
449  // is used to faciliate FIFO sorting when two tasks have the same
450  // delayed_run_time value.
451  PendingTask new_pending_task(pending_task);
452  new_pending_task.sequence_num = next_sequence_num_++;
453  delayed_work_queue_.push(new_pending_task);
454}
455
456void MessageLoop::ReloadWorkQueue() {
457  // We can improve performance of our loading tasks from incoming_queue_ to
458  // work_queue_ by waiting until the last minute (work_queue_ is empty) to
459  // load.  That reduces the number of locks-per-task significantly when our
460  // queues get large.
461  if (!work_queue_.empty())
462    return;  // Wait till we *really* need to lock and load.
463
464  // Acquire all we can from the inter-thread queue with one lock acquisition.
465  {
466    AutoLock lock(incoming_queue_lock_);
467    if (incoming_queue_.empty())
468      return;
469    incoming_queue_.Swap(&work_queue_);  // Constant time
470    DCHECK(incoming_queue_.empty());
471  }
472}
473
474bool MessageLoop::DeletePendingTasks() {
475  bool did_work = !work_queue_.empty();
476  while (!work_queue_.empty()) {
477    PendingTask pending_task = work_queue_.front();
478    work_queue_.pop();
479    if (!pending_task.delayed_run_time.is_null()) {
480      // We want to delete delayed tasks in the same order in which they would
481      // normally be deleted in case of any funny dependencies between delayed
482      // tasks.
483      AddToDelayedWorkQueue(pending_task);
484    } else {
485      // TODO(darin): Delete all tasks once it is safe to do so.
486      // Until it is totally safe, just do it when running Purify or
487      // Valgrind.
488#if defined(PURIFY) || defined(USE_HEAPCHECKER)
489      delete pending_task.task;
490#elif defined(OS_POSIX)
491      if (RUNNING_ON_VALGRIND)
492        delete pending_task.task;
493#endif  // defined(OS_POSIX)
494    }
495  }
496  did_work |= !deferred_non_nestable_work_queue_.empty();
497  while (!deferred_non_nestable_work_queue_.empty()) {
498    // TODO(darin): Delete all tasks once it is safe to do so.
499    // Until it is totaly safe, only delete them under Purify and Valgrind.
500    Task* task = NULL;
501#if defined(PURIFY) || defined(USE_HEAPCHECKER)
502    task = deferred_non_nestable_work_queue_.front().task;
503#elif defined(OS_POSIX)
504    if (RUNNING_ON_VALGRIND)
505      task = deferred_non_nestable_work_queue_.front().task;
506#endif
507    deferred_non_nestable_work_queue_.pop();
508    if (task)
509      delete task;
510  }
511  did_work |= !delayed_work_queue_.empty();
512  while (!delayed_work_queue_.empty()) {
513    Task* task = delayed_work_queue_.top().task;
514    delayed_work_queue_.pop();
515    delete task;
516  }
517  return did_work;
518}
519
520bool MessageLoop::DoWork() {
521  if (!nestable_tasks_allowed_) {
522    // Task can't be executed right now.
523    return false;
524  }
525
526  for (;;) {
527    ReloadWorkQueue();
528    if (work_queue_.empty())
529      break;
530
531    // Execute oldest task.
532    do {
533      PendingTask pending_task = work_queue_.front();
534      work_queue_.pop();
535      if (!pending_task.delayed_run_time.is_null()) {
536        AddToDelayedWorkQueue(pending_task);
537        // If we changed the topmost task, then it is time to re-schedule.
538        if (delayed_work_queue_.top().task == pending_task.task)
539          pump_->ScheduleDelayedWork(pending_task.delayed_run_time);
540      } else {
541        if (DeferOrRunPendingTask(pending_task))
542          return true;
543      }
544    } while (!work_queue_.empty());
545  }
546
547  // Nothing happened.
548  return false;
549}
550
551bool MessageLoop::DoDelayedWork(base::TimeTicks* next_delayed_work_time) {
552  if (!nestable_tasks_allowed_ || delayed_work_queue_.empty()) {
553    recent_time_ = *next_delayed_work_time = TimeTicks();
554    return false;
555  }
556
557  // When we "fall behind," there will be a lot of tasks in the delayed work
558  // queue that are ready to run.  To increase efficiency when we fall behind,
559  // we will only call Time::Now() intermittently, and then process all tasks
560  // that are ready to run before calling it again.  As a result, the more we
561  // fall behind (and have a lot of ready-to-run delayed tasks), the more
562  // efficient we'll be at handling the tasks.
563
564  TimeTicks next_run_time = delayed_work_queue_.top().delayed_run_time;
565  if (next_run_time > recent_time_) {
566    recent_time_ = TimeTicks::Now();  // Get a better view of Now();
567    if (next_run_time > recent_time_) {
568      *next_delayed_work_time = next_run_time;
569      return false;
570    }
571  }
572
573  PendingTask pending_task = delayed_work_queue_.top();
574  delayed_work_queue_.pop();
575
576  if (!delayed_work_queue_.empty())
577    *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time;
578
579  return DeferOrRunPendingTask(pending_task);
580}
581
582bool MessageLoop::DoIdleWork() {
583  if (ProcessNextDelayedNonNestableTask())
584    return true;
585
586  if (state_->quit_received)
587    pump_->Quit();
588
589  return false;
590}
591
592//------------------------------------------------------------------------------
593// MessageLoop::AutoRunState
594
595MessageLoop::AutoRunState::AutoRunState(MessageLoop* loop) : loop_(loop) {
596  // Make the loop reference us.
597  previous_state_ = loop_->state_;
598  if (previous_state_) {
599    run_depth = previous_state_->run_depth + 1;
600  } else {
601    run_depth = 1;
602  }
603  loop_->state_ = this;
604
605  // Initialize the other fields:
606  quit_received = false;
607#if !defined(OS_MACOSX)
608  dispatcher = NULL;
609#endif
610}
611
612MessageLoop::AutoRunState::~AutoRunState() {
613  loop_->state_ = previous_state_;
614}
615
616//------------------------------------------------------------------------------
617// MessageLoop::PendingTask
618
619bool MessageLoop::PendingTask::operator<(const PendingTask& other) const {
620  // Since the top of a priority queue is defined as the "greatest" element, we
621  // need to invert the comparison here.  We want the smaller time to be at the
622  // top of the heap.
623
624  if (delayed_run_time < other.delayed_run_time)
625    return false;
626
627  if (delayed_run_time > other.delayed_run_time)
628    return true;
629
630  // If the times happen to match, then we use the sequence number to decide.
631  // Compare the difference to support integer roll-over.
632  return (sequence_num - other.sequence_num) > 0;
633}
634
635//------------------------------------------------------------------------------
636// Method and data for histogramming events and actions taken by each instance
637// on each thread.
638
639// static
640void MessageLoop::EnableHistogrammer(bool enable) {
641  enable_histogrammer_ = enable;
642}
643
644void MessageLoop::StartHistogrammer() {
645  if (enable_histogrammer_ && !message_histogram_.get()
646      && base::StatisticsRecorder::WasStarted()) {
647    DCHECK(!thread_name_.empty());
648    message_histogram_ = base::LinearHistogram::FactoryGet(
649        "MsgLoop:" + thread_name_,
650        kLeastNonZeroMessageId, kMaxMessageId,
651        kNumberOfDistinctMessagesDisplayed,
652        message_histogram_->kHexRangePrintingFlag);
653    message_histogram_->SetRangeDescriptions(event_descriptions_);
654  }
655}
656
657void MessageLoop::HistogramEvent(int event) {
658  if (message_histogram_.get())
659    message_histogram_->Add(event);
660}
661
662//------------------------------------------------------------------------------
663// MessageLoopForUI
664
665#if defined(OS_WIN)
666void MessageLoopForUI::DidProcessMessage(const MSG& message) {
667  pump_win()->DidProcessMessage(message);
668}
669#endif  // defined(OS_WIN)
670
671#if !defined(OS_MACOSX) && !defined(ANDROID)
672void MessageLoopForUI::AddObserver(Observer* observer) {
673  pump_ui()->AddObserver(observer);
674}
675
676void MessageLoopForUI::RemoveObserver(Observer* observer) {
677  pump_ui()->RemoveObserver(observer);
678}
679
680void MessageLoopForUI::Run(Dispatcher* dispatcher) {
681  AutoRunState save_state(this);
682  state_->dispatcher = dispatcher;
683  RunHandler();
684}
685#endif  // !defined(OS_MACOSX)
686
687//------------------------------------------------------------------------------
688// MessageLoopForIO
689
690#if defined(OS_WIN)
691
692void MessageLoopForIO::RegisterIOHandler(HANDLE file, IOHandler* handler) {
693  pump_io()->RegisterIOHandler(file, handler);
694}
695
696bool MessageLoopForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
697  return pump_io()->WaitForIOCompletion(timeout, filter);
698}
699
700#elif defined(OS_POSIX)
701
702bool MessageLoopForIO::WatchFileDescriptor(int fd,
703                                           bool persistent,
704                                           Mode mode,
705                                           FileDescriptorWatcher *controller,
706                                           Watcher *delegate) {
707  return pump_libevent()->WatchFileDescriptor(
708      fd,
709      persistent,
710      static_cast<base::MessagePumpLibevent::Mode>(mode),
711      controller,
712      delegate);
713}
714
715#endif
716