Home | History | Annotate | Download | only in message_loop
      1 // Copyright (c) 2012 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 #import "base/message_loop/message_pump_mac.h"
      6 
      7 #include <dlfcn.h>
      8 #import <Foundation/Foundation.h>
      9 
     10 #include <limits>
     11 
     12 #include "base/logging.h"
     13 #include "base/mac/scoped_cftyperef.h"
     14 #include "base/message_loop/timer_slack.h"
     15 #include "base/run_loop.h"
     16 #include "base/time/time.h"
     17 
     18 #if !defined(OS_IOS)
     19 #import <AppKit/AppKit.h>
     20 #endif  // !defined(OS_IOS)
     21 
     22 namespace base {
     23 
     24 namespace {
     25 
     26 void CFRunLoopAddSourceToAllModes(CFRunLoopRef rl, CFRunLoopSourceRef source) {
     27   CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
     28   CFRunLoopAddSource(rl, source, kMessageLoopExclusiveRunLoopMode);
     29 }
     30 
     31 void CFRunLoopRemoveSourceFromAllModes(CFRunLoopRef rl,
     32                                        CFRunLoopSourceRef source) {
     33   CFRunLoopRemoveSource(rl, source, kCFRunLoopCommonModes);
     34   CFRunLoopRemoveSource(rl, source, kMessageLoopExclusiveRunLoopMode);
     35 }
     36 
     37 void CFRunLoopAddTimerToAllModes(CFRunLoopRef rl, CFRunLoopTimerRef timer) {
     38   CFRunLoopAddTimer(rl, timer, kCFRunLoopCommonModes);
     39   CFRunLoopAddTimer(rl, timer, kMessageLoopExclusiveRunLoopMode);
     40 }
     41 
     42 void CFRunLoopRemoveTimerFromAllModes(CFRunLoopRef rl,
     43                                       CFRunLoopTimerRef timer) {
     44   CFRunLoopRemoveTimer(rl, timer, kCFRunLoopCommonModes);
     45   CFRunLoopRemoveTimer(rl, timer, kMessageLoopExclusiveRunLoopMode);
     46 }
     47 
     48 void CFRunLoopAddObserverToAllModes(CFRunLoopRef rl,
     49                                     CFRunLoopObserverRef observer) {
     50   CFRunLoopAddObserver(rl, observer, kCFRunLoopCommonModes);
     51   CFRunLoopAddObserver(rl, observer, kMessageLoopExclusiveRunLoopMode);
     52 }
     53 
     54 void CFRunLoopRemoveObserverFromAllModes(CFRunLoopRef rl,
     55                                          CFRunLoopObserverRef observer) {
     56   CFRunLoopRemoveObserver(rl, observer, kCFRunLoopCommonModes);
     57   CFRunLoopRemoveObserver(rl, observer, kMessageLoopExclusiveRunLoopMode);
     58 }
     59 
     60 void NoOp(void* /* info */) {
     61 }
     62 
     63 const CFTimeInterval kCFTimeIntervalMax =
     64     std::numeric_limits<CFTimeInterval>::max();
     65 
     66 #if !defined(OS_IOS)
     67 // Set to true if MessagePumpMac::Create() is called before NSApp is
     68 // initialized.  Only accessed from the main thread.
     69 bool g_not_using_cr_app = false;
     70 #endif
     71 
     72 // Call through to CFRunLoopTimerSetTolerance(), which is only available on
     73 // OS X 10.9.
     74 void SetTimerTolerance(CFRunLoopTimerRef timer, CFTimeInterval tolerance) {
     75   typedef void (*CFRunLoopTimerSetTolerancePtr)(CFRunLoopTimerRef timer,
     76       CFTimeInterval tolerance);
     77 
     78   static CFRunLoopTimerSetTolerancePtr settimertolerance_function_ptr;
     79 
     80   static dispatch_once_t get_timer_tolerance_function_ptr_once;
     81   dispatch_once(&get_timer_tolerance_function_ptr_once, ^{
     82       NSBundle* bundle =[NSBundle
     83         bundleWithPath:@"/System/Library/Frameworks/CoreFoundation.framework"];
     84       const char* path = [[bundle executablePath] fileSystemRepresentation];
     85       CHECK(path);
     86       void* library_handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
     87       CHECK(library_handle) << dlerror();
     88       settimertolerance_function_ptr =
     89           reinterpret_cast<CFRunLoopTimerSetTolerancePtr>(
     90               dlsym(library_handle, "CFRunLoopTimerSetTolerance"));
     91 
     92       dlclose(library_handle);
     93   });
     94 
     95   if (settimertolerance_function_ptr)
     96     settimertolerance_function_ptr(timer, tolerance);
     97 }
     98 
     99 }  // namespace
    100 
    101 // static
    102 const CFStringRef kMessageLoopExclusiveRunLoopMode =
    103     CFSTR("kMessageLoopExclusiveRunLoopMode");
    104 
    105 // A scoper for autorelease pools created from message pump run loops.
    106 // Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare
    107 // case where an autorelease pool needs to be passed in.
    108 class MessagePumpScopedAutoreleasePool {
    109  public:
    110   explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) :
    111       pool_(pump->CreateAutoreleasePool()) {
    112   }
    113    ~MessagePumpScopedAutoreleasePool() {
    114     [pool_ drain];
    115   }
    116 
    117  private:
    118   NSAutoreleasePool* pool_;
    119   DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool);
    120 };
    121 
    122 // Must be called on the run loop thread.
    123 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
    124     : delegate_(NULL),
    125       delayed_work_fire_time_(kCFTimeIntervalMax),
    126       timer_slack_(base::TIMER_SLACK_NONE),
    127       nesting_level_(0),
    128       run_nesting_level_(0),
    129       deepest_nesting_level_(0),
    130       delegateless_work_(false),
    131       delegateless_idle_work_(false) {
    132   run_loop_ = CFRunLoopGetCurrent();
    133   CFRetain(run_loop_);
    134 
    135   // Set a repeating timer with a preposterous firing time and interval.  The
    136   // timer will effectively never fire as-is.  The firing time will be adjusted
    137   // as needed when ScheduleDelayedWork is called.
    138   CFRunLoopTimerContext timer_context = CFRunLoopTimerContext();
    139   timer_context.info = this;
    140   delayed_work_timer_ = CFRunLoopTimerCreate(NULL,                // allocator
    141                                              kCFTimeIntervalMax,  // fire time
    142                                              kCFTimeIntervalMax,  // interval
    143                                              0,                   // flags
    144                                              0,                   // priority
    145                                              RunDelayedWorkTimer,
    146                                              &timer_context);
    147   CFRunLoopAddTimerToAllModes(run_loop_, delayed_work_timer_);
    148 
    149   CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
    150   source_context.info = this;
    151   source_context.perform = RunWorkSource;
    152   work_source_ = CFRunLoopSourceCreate(NULL,  // allocator
    153                                        1,     // priority
    154                                        &source_context);
    155   CFRunLoopAddSourceToAllModes(run_loop_, work_source_);
    156 
    157   source_context.perform = RunIdleWorkSource;
    158   idle_work_source_ = CFRunLoopSourceCreate(NULL,  // allocator
    159                                             2,     // priority
    160                                             &source_context);
    161   CFRunLoopAddSourceToAllModes(run_loop_, idle_work_source_);
    162 
    163   source_context.perform = RunNestingDeferredWorkSource;
    164   nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL,  // allocator
    165                                                         0,     // priority
    166                                                         &source_context);
    167   CFRunLoopAddSourceToAllModes(run_loop_, nesting_deferred_work_source_);
    168 
    169   CFRunLoopObserverContext observer_context = CFRunLoopObserverContext();
    170   observer_context.info = this;
    171   pre_wait_observer_ = CFRunLoopObserverCreate(NULL,  // allocator
    172                                                kCFRunLoopBeforeWaiting,
    173                                                true,  // repeat
    174                                                0,     // priority
    175                                                PreWaitObserver,
    176                                                &observer_context);
    177   CFRunLoopAddObserverToAllModes(run_loop_, pre_wait_observer_);
    178 
    179   pre_source_observer_ = CFRunLoopObserverCreate(NULL,  // allocator
    180                                                  kCFRunLoopBeforeSources,
    181                                                  true,  // repeat
    182                                                  0,     // priority
    183                                                  PreSourceObserver,
    184                                                  &observer_context);
    185   CFRunLoopAddObserverToAllModes(run_loop_, pre_source_observer_);
    186 
    187   enter_exit_observer_ = CFRunLoopObserverCreate(NULL,  // allocator
    188                                                  kCFRunLoopEntry |
    189                                                      kCFRunLoopExit,
    190                                                  true,  // repeat
    191                                                  0,     // priority
    192                                                  EnterExitObserver,
    193                                                  &observer_context);
    194   CFRunLoopAddObserverToAllModes(run_loop_, enter_exit_observer_);
    195 }
    196 
    197 // Ideally called on the run loop thread.  If other run loops were running
    198 // lower on the run loop thread's stack when this object was created, the
    199 // same number of run loops must be running when this object is destroyed.
    200 MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() {
    201   CFRunLoopRemoveObserverFromAllModes(run_loop_, enter_exit_observer_);
    202   CFRelease(enter_exit_observer_);
    203 
    204   CFRunLoopRemoveObserverFromAllModes(run_loop_, pre_source_observer_);
    205   CFRelease(pre_source_observer_);
    206 
    207   CFRunLoopRemoveObserverFromAllModes(run_loop_, pre_wait_observer_);
    208   CFRelease(pre_wait_observer_);
    209 
    210   CFRunLoopRemoveSourceFromAllModes(run_loop_, nesting_deferred_work_source_);
    211   CFRelease(nesting_deferred_work_source_);
    212 
    213   CFRunLoopRemoveSourceFromAllModes(run_loop_, idle_work_source_);
    214   CFRelease(idle_work_source_);
    215 
    216   CFRunLoopRemoveSourceFromAllModes(run_loop_, work_source_);
    217   CFRelease(work_source_);
    218 
    219   CFRunLoopRemoveTimerFromAllModes(run_loop_, delayed_work_timer_);
    220   CFRelease(delayed_work_timer_);
    221 
    222   CFRelease(run_loop_);
    223 }
    224 
    225 // Must be called on the run loop thread.
    226 void MessagePumpCFRunLoopBase::Run(Delegate* delegate) {
    227   // nesting_level_ will be incremented in EnterExitRunLoop, so set
    228   // run_nesting_level_ accordingly.
    229   int last_run_nesting_level = run_nesting_level_;
    230   run_nesting_level_ = nesting_level_ + 1;
    231 
    232   Delegate* last_delegate = delegate_;
    233   SetDelegate(delegate);
    234 
    235   DoRun(delegate);
    236 
    237   // Restore the previous state of the object.
    238   SetDelegate(last_delegate);
    239   run_nesting_level_ = last_run_nesting_level;
    240 }
    241 
    242 void MessagePumpCFRunLoopBase::SetDelegate(Delegate* delegate) {
    243   delegate_ = delegate;
    244 
    245   if (delegate) {
    246     // If any work showed up but could not be dispatched for want of a
    247     // delegate, set it up for dispatch again now that a delegate is
    248     // available.
    249     if (delegateless_work_) {
    250       CFRunLoopSourceSignal(work_source_);
    251       delegateless_work_ = false;
    252     }
    253     if (delegateless_idle_work_) {
    254       CFRunLoopSourceSignal(idle_work_source_);
    255       delegateless_idle_work_ = false;
    256     }
    257   }
    258 }
    259 
    260 // May be called on any thread.
    261 void MessagePumpCFRunLoopBase::ScheduleWork() {
    262   CFRunLoopSourceSignal(work_source_);
    263   CFRunLoopWakeUp(run_loop_);
    264 }
    265 
    266 // Must be called on the run loop thread.
    267 void MessagePumpCFRunLoopBase::ScheduleDelayedWork(
    268     const TimeTicks& delayed_work_time) {
    269   TimeDelta delta = delayed_work_time - TimeTicks::Now();
    270   delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF();
    271   CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_);
    272   if (timer_slack_ == TIMER_SLACK_MAXIMUM) {
    273     SetTimerTolerance(delayed_work_timer_, delta.InSecondsF() * 0.5);
    274   } else {
    275     SetTimerTolerance(delayed_work_timer_, 0);
    276   }
    277 }
    278 
    279 void MessagePumpCFRunLoopBase::SetTimerSlack(TimerSlack timer_slack) {
    280   timer_slack_ = timer_slack;
    281 }
    282 
    283 // Called from the run loop.
    284 // static
    285 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(
    286     CFRunLoopTimerRef /* timer */,
    287     void* info) {
    288   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    289 
    290   // The timer won't fire again until it's reset.
    291   self->delayed_work_fire_time_ = kCFTimeIntervalMax;
    292 
    293   // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources.
    294   // In order to establish the proper priority in which work and delayed work
    295   // are processed one for one, the timer used to schedule delayed work must
    296   // signal a CFRunLoopSource used to dispatch both work and delayed work.
    297   CFRunLoopSourceSignal(self->work_source_);
    298 }
    299 
    300 // Called from the run loop.
    301 // static
    302 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) {
    303   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    304   self->RunWork();
    305 }
    306 
    307 // Called by MessagePumpCFRunLoopBase::RunWorkSource.
    308 bool MessagePumpCFRunLoopBase::RunWork() {
    309   if (!delegate_) {
    310     // This point can be reached with a NULL delegate_ if Run is not on the
    311     // stack but foreign code is spinning the CFRunLoop.  Arrange to come back
    312     // here when a delegate is available.
    313     delegateless_work_ = true;
    314     return false;
    315   }
    316 
    317   // The NSApplication-based run loop only drains the autorelease pool at each
    318   // UI event (NSEvent).  The autorelease pool is not drained for each
    319   // CFRunLoopSource target that's run.  Use a local pool for any autoreleased
    320   // objects if the app is not currently handling a UI event to ensure they're
    321   // released promptly even in the absence of UI events.
    322   MessagePumpScopedAutoreleasePool autorelease_pool(this);
    323 
    324   // Call DoWork and DoDelayedWork once, and if something was done, arrange to
    325   // come back here again as long as the loop is still running.
    326   bool did_work = delegate_->DoWork();
    327   bool resignal_work_source = did_work;
    328 
    329   TimeTicks next_time;
    330   delegate_->DoDelayedWork(&next_time);
    331   if (!did_work) {
    332     // Determine whether there's more delayed work, and if so, if it needs to
    333     // be done at some point in the future or if it's already time to do it.
    334     // Only do these checks if did_work is false. If did_work is true, this
    335     // function, and therefore any additional delayed work, will get another
    336     // chance to run before the loop goes to sleep.
    337     bool more_delayed_work = !next_time.is_null();
    338     if (more_delayed_work) {
    339       TimeDelta delay = next_time - TimeTicks::Now();
    340       if (delay > TimeDelta()) {
    341         // There's more delayed work to be done in the future.
    342         ScheduleDelayedWork(next_time);
    343       } else {
    344         // There's more delayed work to be done, and its time is in the past.
    345         // Arrange to come back here directly as long as the loop is still
    346         // running.
    347         resignal_work_source = true;
    348       }
    349     }
    350   }
    351 
    352   if (resignal_work_source) {
    353     CFRunLoopSourceSignal(work_source_);
    354   }
    355 
    356   return resignal_work_source;
    357 }
    358 
    359 // Called from the run loop.
    360 // static
    361 void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) {
    362   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    363   self->RunIdleWork();
    364 }
    365 
    366 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource.
    367 bool MessagePumpCFRunLoopBase::RunIdleWork() {
    368   if (!delegate_) {
    369     // This point can be reached with a NULL delegate_ if Run is not on the
    370     // stack but foreign code is spinning the CFRunLoop.  Arrange to come back
    371     // here when a delegate is available.
    372     delegateless_idle_work_ = true;
    373     return false;
    374   }
    375 
    376   // The NSApplication-based run loop only drains the autorelease pool at each
    377   // UI event (NSEvent).  The autorelease pool is not drained for each
    378   // CFRunLoopSource target that's run.  Use a local pool for any autoreleased
    379   // objects if the app is not currently handling a UI event to ensure they're
    380   // released promptly even in the absence of UI events.
    381   MessagePumpScopedAutoreleasePool autorelease_pool(this);
    382 
    383   // Call DoIdleWork once, and if something was done, arrange to come back here
    384   // again as long as the loop is still running.
    385   bool did_work = delegate_->DoIdleWork();
    386   if (did_work) {
    387     CFRunLoopSourceSignal(idle_work_source_);
    388   }
    389 
    390   return did_work;
    391 }
    392 
    393 // Called from the run loop.
    394 // static
    395 void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) {
    396   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    397   self->RunNestingDeferredWork();
    398 }
    399 
    400 // Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource.
    401 bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() {
    402   if (!delegate_) {
    403     // This point can be reached with a NULL delegate_ if Run is not on the
    404     // stack but foreign code is spinning the CFRunLoop.  There's no sense in
    405     // attempting to do any work or signalling the work sources because
    406     // without a delegate, work is not possible.
    407     return false;
    408   }
    409 
    410   // Immediately try work in priority order.
    411   if (!RunWork()) {
    412     if (!RunIdleWork()) {
    413       return false;
    414     }
    415   } else {
    416     // Work was done.  Arrange for the loop to try non-nestable idle work on
    417     // a subsequent pass.
    418     CFRunLoopSourceSignal(idle_work_source_);
    419   }
    420 
    421   return true;
    422 }
    423 
    424 // Called before the run loop goes to sleep or exits, or processes sources.
    425 void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() {
    426   // deepest_nesting_level_ is set as run loops are entered.  If the deepest
    427   // level encountered is deeper than the current level, a nested loop
    428   // (relative to the current level) ran since the last time nesting-deferred
    429   // work was scheduled.  When that situation is encountered, schedule
    430   // nesting-deferred work in case any work was deferred because nested work
    431   // was disallowed.
    432   if (deepest_nesting_level_ > nesting_level_) {
    433     deepest_nesting_level_ = nesting_level_;
    434     CFRunLoopSourceSignal(nesting_deferred_work_source_);
    435   }
    436 }
    437 
    438 // Called from the run loop.
    439 // static
    440 void MessagePumpCFRunLoopBase::PreWaitObserver(
    441     CFRunLoopObserverRef /* observer */,
    442     CFRunLoopActivity /* activity */,
    443     void* info) {
    444   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    445 
    446   // Attempt to do some idle work before going to sleep.
    447   self->RunIdleWork();
    448 
    449   // The run loop is about to go to sleep.  If any of the work done since it
    450   // started or woke up resulted in a nested run loop running,
    451   // nesting-deferred work may have accumulated.  Schedule it for processing
    452   // if appropriate.
    453   self->MaybeScheduleNestingDeferredWork();
    454 }
    455 
    456 // Called from the run loop.
    457 // static
    458 void MessagePumpCFRunLoopBase::PreSourceObserver(
    459     CFRunLoopObserverRef /* observer */,
    460     CFRunLoopActivity /* activity */,
    461     void* info) {
    462   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    463 
    464   // The run loop has reached the top of the loop and is about to begin
    465   // processing sources.  If the last iteration of the loop at this nesting
    466   // level did not sleep or exit, nesting-deferred work may have accumulated
    467   // if a nested loop ran.  Schedule nesting-deferred work for processing if
    468   // appropriate.
    469   self->MaybeScheduleNestingDeferredWork();
    470 }
    471 
    472 // Called from the run loop.
    473 // static
    474 void MessagePumpCFRunLoopBase::EnterExitObserver(
    475     CFRunLoopObserverRef /* observer */,
    476     CFRunLoopActivity activity,
    477     void* info) {
    478   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    479 
    480   switch (activity) {
    481     case kCFRunLoopEntry:
    482       ++self->nesting_level_;
    483       if (self->nesting_level_ > self->deepest_nesting_level_) {
    484         self->deepest_nesting_level_ = self->nesting_level_;
    485       }
    486       break;
    487 
    488     case kCFRunLoopExit:
    489       // Not all run loops go to sleep.  If a run loop is stopped before it
    490       // goes to sleep due to a CFRunLoopStop call, or if the timeout passed
    491       // to CFRunLoopRunInMode expires, the run loop may proceed directly from
    492       // handling sources to exiting without any sleep.  This most commonly
    493       // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it
    494       // to make a single pass through the loop and exit without sleep.  Some
    495       // native loops use CFRunLoop in this way.  Because PreWaitObserver will
    496       // not be called in these case, MaybeScheduleNestingDeferredWork needs
    497       // to be called here, as the run loop exits.
    498       //
    499       // MaybeScheduleNestingDeferredWork consults self->nesting_level_
    500       // to determine whether to schedule nesting-deferred work.  It expects
    501       // the nesting level to be set to the depth of the loop that is going
    502       // to sleep or exiting.  It must be called before decrementing the
    503       // value so that the value still corresponds to the level of the exiting
    504       // loop.
    505       self->MaybeScheduleNestingDeferredWork();
    506       --self->nesting_level_;
    507       break;
    508 
    509     default:
    510       break;
    511   }
    512 
    513   self->EnterExitRunLoop(activity);
    514 }
    515 
    516 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop.  The default
    517 // implementation is a no-op.
    518 void MessagePumpCFRunLoopBase::EnterExitRunLoop(
    519     CFRunLoopActivity /* activity */) {
    520 }
    521 
    522 // Base version returns a standard NSAutoreleasePool.
    523 AutoreleasePoolType* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
    524   return [[NSAutoreleasePool alloc] init];
    525 }
    526 
    527 MessagePumpCFRunLoop::MessagePumpCFRunLoop()
    528     : quit_pending_(false) {
    529 }
    530 
    531 MessagePumpCFRunLoop::~MessagePumpCFRunLoop() {}
    532 
    533 // Called by MessagePumpCFRunLoopBase::DoRun.  If other CFRunLoopRun loops were
    534 // running lower on the run loop thread's stack when this object was created,
    535 // the same number of CFRunLoopRun loops must be running for the outermost call
    536 // to Run.  Run/DoRun are reentrant after that point.
    537 void MessagePumpCFRunLoop::DoRun(Delegate* /* delegate */) {
    538   // This is completely identical to calling CFRunLoopRun(), except autorelease
    539   // pool management is introduced.
    540   int result;
    541   do {
    542     MessagePumpScopedAutoreleasePool autorelease_pool(this);
    543     result = CFRunLoopRunInMode(kCFRunLoopDefaultMode,
    544                                 kCFTimeIntervalMax,
    545                                 false);
    546   } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished);
    547 }
    548 
    549 // Must be called on the run loop thread.
    550 void MessagePumpCFRunLoop::Quit() {
    551   // Stop the innermost run loop managed by this MessagePumpCFRunLoop object.
    552   if (nesting_level() == run_nesting_level()) {
    553     // This object is running the innermost loop, just stop it.
    554     CFRunLoopStop(run_loop());
    555   } else {
    556     // There's another loop running inside the loop managed by this object.
    557     // In other words, someone else called CFRunLoopRunInMode on the same
    558     // thread, deeper on the stack than the deepest Run call.  Don't preempt
    559     // other run loops, just mark this object to quit the innermost Run as
    560     // soon as the other inner loops not managed by Run are done.
    561     quit_pending_ = true;
    562   }
    563 }
    564 
    565 // Called by MessagePumpCFRunLoopBase::EnterExitObserver.
    566 void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) {
    567   if (activity == kCFRunLoopExit &&
    568       nesting_level() == run_nesting_level() &&
    569       quit_pending_) {
    570     // Quit was called while loops other than those managed by this object
    571     // were running further inside a run loop managed by this object.  Now
    572     // that all unmanaged inner run loops are gone, stop the loop running
    573     // just inside Run.
    574     CFRunLoopStop(run_loop());
    575     quit_pending_ = false;
    576   }
    577 }
    578 
    579 MessagePumpNSRunLoop::MessagePumpNSRunLoop()
    580     : keep_running_(true) {
    581   CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
    582   source_context.perform = NoOp;
    583   quit_source_ = CFRunLoopSourceCreate(NULL,  // allocator
    584                                        0,     // priority
    585                                        &source_context);
    586   CFRunLoopAddSourceToAllModes(run_loop(), quit_source_);
    587 }
    588 
    589 MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
    590   CFRunLoopRemoveSourceFromAllModes(run_loop(), quit_source_);
    591   CFRelease(quit_source_);
    592 }
    593 
    594 void MessagePumpNSRunLoop::DoRun(Delegate* /* delegate */) {
    595   while (keep_running_) {
    596     // NSRunLoop manages autorelease pools itself.
    597     [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
    598                              beforeDate:[NSDate distantFuture]];
    599   }
    600 
    601   keep_running_ = true;
    602 }
    603 
    604 void MessagePumpNSRunLoop::Quit() {
    605   keep_running_ = false;
    606   CFRunLoopSourceSignal(quit_source_);
    607   CFRunLoopWakeUp(run_loop());
    608 }
    609 
    610 #if defined(OS_IOS)
    611 MessagePumpUIApplication::MessagePumpUIApplication()
    612     : run_loop_(NULL) {
    613 }
    614 
    615 MessagePumpUIApplication::~MessagePumpUIApplication() {}
    616 
    617 void MessagePumpUIApplication::DoRun(Delegate* delegate) {
    618   NOTREACHED();
    619 }
    620 
    621 void MessagePumpUIApplication::Quit() {
    622   NOTREACHED();
    623 }
    624 
    625 void MessagePumpUIApplication::Attach(Delegate* delegate) {
    626   DCHECK(!run_loop_);
    627   run_loop_ = new RunLoop();
    628   CHECK(run_loop_->BeforeRun());
    629   SetDelegate(delegate);
    630 }
    631 
    632 #else
    633 
    634 MessagePumpNSApplication::MessagePumpNSApplication()
    635     : keep_running_(true),
    636       running_own_loop_(false) {
    637 }
    638 
    639 MessagePumpNSApplication::~MessagePumpNSApplication() {}
    640 
    641 void MessagePumpNSApplication::DoRun(Delegate* /* delegate */) {
    642   bool last_running_own_loop_ = running_own_loop_;
    643 
    644   // NSApp must be initialized by calling:
    645   // [{some class which implements CrAppProtocol} sharedApplication]
    646   // Most likely candidates are CrApplication or BrowserCrApplication.
    647   // These can be initialized from C++ code by calling
    648   // RegisterCrApp() or RegisterBrowserCrApp().
    649   CHECK(NSApp);
    650 
    651   if (![NSApp isRunning]) {
    652     running_own_loop_ = false;
    653     // NSApplication manages autorelease pools itself when run this way.
    654     [NSApp run];
    655   } else {
    656     running_own_loop_ = true;
    657     NSDate* distant_future = [NSDate distantFuture];
    658     while (keep_running_) {
    659       MessagePumpScopedAutoreleasePool autorelease_pool(this);
    660       NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
    661                                           untilDate:distant_future
    662                                              inMode:NSDefaultRunLoopMode
    663                                             dequeue:YES];
    664       if (event) {
    665         [NSApp sendEvent:event];
    666       }
    667     }
    668     keep_running_ = true;
    669   }
    670 
    671   running_own_loop_ = last_running_own_loop_;
    672 }
    673 
    674 void MessagePumpNSApplication::Quit() {
    675   if (!running_own_loop_) {
    676     [[NSApplication sharedApplication] stop:nil];
    677   } else {
    678     keep_running_ = false;
    679   }
    680 
    681   // Send a fake event to wake the loop up.
    682   [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
    683                                       location:NSZeroPoint
    684                                  modifierFlags:0
    685                                      timestamp:0
    686                                   windowNumber:0
    687                                        context:NULL
    688                                        subtype:0
    689                                          data1:0
    690                                          data2:0]
    691            atStart:NO];
    692 }
    693 
    694 MessagePumpCrApplication::MessagePumpCrApplication() {
    695 }
    696 
    697 MessagePumpCrApplication::~MessagePumpCrApplication() {
    698 }
    699 
    700 // Prevents an autorelease pool from being created if the app is in the midst of
    701 // handling a UI event because various parts of AppKit depend on objects that
    702 // are created while handling a UI event to be autoreleased in the event loop.
    703 // An example of this is NSWindowController. When a window with a window
    704 // controller is closed it goes through a stack like this:
    705 // (Several stack frames elided for clarity)
    706 //
    707 // #0 [NSWindowController autorelease]
    708 // #1 DoAClose
    709 // #2 MessagePumpCFRunLoopBase::DoWork()
    710 // #3 [NSRunLoop run]
    711 // #4 [NSButton performClick:]
    712 // #5 [NSWindow sendEvent:]
    713 // #6 [NSApp sendEvent:]
    714 // #7 [NSApp run]
    715 //
    716 // -performClick: spins a nested run loop. If the pool created in DoWork was a
    717 // standard NSAutoreleasePool, it would release the objects that were
    718 // autoreleased into it once DoWork released it. This would cause the window
    719 // controller, which autoreleased itself in frame #0, to release itself, and
    720 // possibly free itself. Unfortunately this window controller controls the
    721 // window in frame #5. When the stack is unwound to frame #5, the window would
    722 // no longer exists and crashes may occur. Apple gets around this by never
    723 // releasing the pool it creates in frame #4, and letting frame #7 clean it up
    724 // when it cleans up the pool that wraps frame #7. When an autorelease pool is
    725 // released it releases all other pools that were created after it on the
    726 // autorelease pool stack.
    727 //
    728 // CrApplication is responsible for setting handlingSendEvent to true just
    729 // before it sends the event through the event handling mechanism, and
    730 // returning it to its previous value once the event has been sent.
    731 AutoreleasePoolType* MessagePumpCrApplication::CreateAutoreleasePool() {
    732   if (MessagePumpMac::IsHandlingSendEvent())
    733     return nil;
    734   return MessagePumpNSApplication::CreateAutoreleasePool();
    735 }
    736 
    737 // static
    738 bool MessagePumpMac::UsingCrApp() {
    739   DCHECK([NSThread isMainThread]);
    740 
    741   // If NSApp is still not initialized, then the subclass used cannot
    742   // be determined.
    743   DCHECK(NSApp);
    744 
    745   // The pump was created using MessagePumpNSApplication.
    746   if (g_not_using_cr_app)
    747     return false;
    748 
    749   return [NSApp conformsToProtocol:@protocol(CrAppProtocol)];
    750 }
    751 
    752 // static
    753 bool MessagePumpMac::IsHandlingSendEvent() {
    754   DCHECK([NSApp conformsToProtocol:@protocol(CrAppProtocol)]);
    755   NSObject<CrAppProtocol>* app = static_cast<NSObject<CrAppProtocol>*>(NSApp);
    756   return [app isHandlingSendEvent];
    757 }
    758 #endif  // !defined(OS_IOS)
    759 
    760 // static
    761 MessagePump* MessagePumpMac::Create() {
    762   if ([NSThread isMainThread]) {
    763 #if defined(OS_IOS)
    764     return new MessagePumpUIApplication;
    765 #else
    766     if ([NSApp conformsToProtocol:@protocol(CrAppProtocol)])
    767       return new MessagePumpCrApplication;
    768 
    769     // The main-thread MessagePump implementations REQUIRE an NSApp.
    770     // Executables which have specific requirements for their
    771     // NSApplication subclass should initialize appropriately before
    772     // creating an event loop.
    773     [NSApplication sharedApplication];
    774     g_not_using_cr_app = true;
    775     return new MessagePumpNSApplication;
    776 #endif
    777   }
    778 
    779   return new MessagePumpNSRunLoop;
    780 }
    781 
    782 }  // namespace base
    783