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(CFRunLoopTimerRef timer,
    286                                                    void* info) {
    287   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    288 
    289   // The timer won't fire again until it's reset.
    290   self->delayed_work_fire_time_ = kCFTimeIntervalMax;
    291 
    292   // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources.
    293   // In order to establish the proper priority in which work and delayed work
    294   // are processed one for one, the timer used to schedule delayed work must
    295   // signal a CFRunLoopSource used to dispatch both work and delayed work.
    296   CFRunLoopSourceSignal(self->work_source_);
    297 }
    298 
    299 // Called from the run loop.
    300 // static
    301 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) {
    302   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    303   self->RunWork();
    304 }
    305 
    306 // Called by MessagePumpCFRunLoopBase::RunWorkSource.
    307 bool MessagePumpCFRunLoopBase::RunWork() {
    308   if (!delegate_) {
    309     // This point can be reached with a NULL delegate_ if Run is not on the
    310     // stack but foreign code is spinning the CFRunLoop.  Arrange to come back
    311     // here when a delegate is available.
    312     delegateless_work_ = true;
    313     return false;
    314   }
    315 
    316   // The NSApplication-based run loop only drains the autorelease pool at each
    317   // UI event (NSEvent).  The autorelease pool is not drained for each
    318   // CFRunLoopSource target that's run.  Use a local pool for any autoreleased
    319   // objects if the app is not currently handling a UI event to ensure they're
    320   // released promptly even in the absence of UI events.
    321   MessagePumpScopedAutoreleasePool autorelease_pool(this);
    322 
    323   // Call DoWork and DoDelayedWork once, and if something was done, arrange to
    324   // come back here again as long as the loop is still running.
    325   bool did_work = delegate_->DoWork();
    326   bool resignal_work_source = did_work;
    327 
    328   TimeTicks next_time;
    329   delegate_->DoDelayedWork(&next_time);
    330   if (!did_work) {
    331     // Determine whether there's more delayed work, and if so, if it needs to
    332     // be done at some point in the future or if it's already time to do it.
    333     // Only do these checks if did_work is false. If did_work is true, this
    334     // function, and therefore any additional delayed work, will get another
    335     // chance to run before the loop goes to sleep.
    336     bool more_delayed_work = !next_time.is_null();
    337     if (more_delayed_work) {
    338       TimeDelta delay = next_time - TimeTicks::Now();
    339       if (delay > TimeDelta()) {
    340         // There's more delayed work to be done in the future.
    341         ScheduleDelayedWork(next_time);
    342       } else {
    343         // There's more delayed work to be done, and its time is in the past.
    344         // Arrange to come back here directly as long as the loop is still
    345         // running.
    346         resignal_work_source = true;
    347       }
    348     }
    349   }
    350 
    351   if (resignal_work_source) {
    352     CFRunLoopSourceSignal(work_source_);
    353   }
    354 
    355   return resignal_work_source;
    356 }
    357 
    358 // Called from the run loop.
    359 // static
    360 void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) {
    361   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    362   self->RunIdleWork();
    363 }
    364 
    365 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource.
    366 bool MessagePumpCFRunLoopBase::RunIdleWork() {
    367   if (!delegate_) {
    368     // This point can be reached with a NULL delegate_ if Run is not on the
    369     // stack but foreign code is spinning the CFRunLoop.  Arrange to come back
    370     // here when a delegate is available.
    371     delegateless_idle_work_ = true;
    372     return false;
    373   }
    374 
    375   // The NSApplication-based run loop only drains the autorelease pool at each
    376   // UI event (NSEvent).  The autorelease pool is not drained for each
    377   // CFRunLoopSource target that's run.  Use a local pool for any autoreleased
    378   // objects if the app is not currently handling a UI event to ensure they're
    379   // released promptly even in the absence of UI events.
    380   MessagePumpScopedAutoreleasePool autorelease_pool(this);
    381 
    382   // Call DoIdleWork once, and if something was done, arrange to come back here
    383   // again as long as the loop is still running.
    384   bool did_work = delegate_->DoIdleWork();
    385   if (did_work) {
    386     CFRunLoopSourceSignal(idle_work_source_);
    387   }
    388 
    389   return did_work;
    390 }
    391 
    392 // Called from the run loop.
    393 // static
    394 void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) {
    395   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    396   self->RunNestingDeferredWork();
    397 }
    398 
    399 // Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource.
    400 bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() {
    401   if (!delegate_) {
    402     // This point can be reached with a NULL delegate_ if Run is not on the
    403     // stack but foreign code is spinning the CFRunLoop.  There's no sense in
    404     // attempting to do any work or signalling the work sources because
    405     // without a delegate, work is not possible.
    406     return false;
    407   }
    408 
    409   // Immediately try work in priority order.
    410   if (!RunWork()) {
    411     if (!RunIdleWork()) {
    412       return false;
    413     }
    414   } else {
    415     // Work was done.  Arrange for the loop to try non-nestable idle work on
    416     // a subsequent pass.
    417     CFRunLoopSourceSignal(idle_work_source_);
    418   }
    419 
    420   return true;
    421 }
    422 
    423 // Called before the run loop goes to sleep or exits, or processes sources.
    424 void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() {
    425   // deepest_nesting_level_ is set as run loops are entered.  If the deepest
    426   // level encountered is deeper than the current level, a nested loop
    427   // (relative to the current level) ran since the last time nesting-deferred
    428   // work was scheduled.  When that situation is encountered, schedule
    429   // nesting-deferred work in case any work was deferred because nested work
    430   // was disallowed.
    431   if (deepest_nesting_level_ > nesting_level_) {
    432     deepest_nesting_level_ = nesting_level_;
    433     CFRunLoopSourceSignal(nesting_deferred_work_source_);
    434   }
    435 }
    436 
    437 // Called from the run loop.
    438 // static
    439 void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer,
    440                                                CFRunLoopActivity activity,
    441                                                void* info) {
    442   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    443 
    444   // Attempt to do some idle work before going to sleep.
    445   self->RunIdleWork();
    446 
    447   // The run loop is about to go to sleep.  If any of the work done since it
    448   // started or woke up resulted in a nested run loop running,
    449   // nesting-deferred work may have accumulated.  Schedule it for processing
    450   // if appropriate.
    451   self->MaybeScheduleNestingDeferredWork();
    452 }
    453 
    454 // Called from the run loop.
    455 // static
    456 void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer,
    457                                                  CFRunLoopActivity activity,
    458                                                  void* info) {
    459   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    460 
    461   // The run loop has reached the top of the loop and is about to begin
    462   // processing sources.  If the last iteration of the loop at this nesting
    463   // level did not sleep or exit, nesting-deferred work may have accumulated
    464   // if a nested loop ran.  Schedule nesting-deferred work for processing if
    465   // appropriate.
    466   self->MaybeScheduleNestingDeferredWork();
    467 }
    468 
    469 // Called from the run loop.
    470 // static
    471 void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer,
    472                                                  CFRunLoopActivity activity,
    473                                                  void* info) {
    474   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    475 
    476   switch (activity) {
    477     case kCFRunLoopEntry:
    478       ++self->nesting_level_;
    479       if (self->nesting_level_ > self->deepest_nesting_level_) {
    480         self->deepest_nesting_level_ = self->nesting_level_;
    481       }
    482       break;
    483 
    484     case kCFRunLoopExit:
    485       // Not all run loops go to sleep.  If a run loop is stopped before it
    486       // goes to sleep due to a CFRunLoopStop call, or if the timeout passed
    487       // to CFRunLoopRunInMode expires, the run loop may proceed directly from
    488       // handling sources to exiting without any sleep.  This most commonly
    489       // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it
    490       // to make a single pass through the loop and exit without sleep.  Some
    491       // native loops use CFRunLoop in this way.  Because PreWaitObserver will
    492       // not be called in these case, MaybeScheduleNestingDeferredWork needs
    493       // to be called here, as the run loop exits.
    494       //
    495       // MaybeScheduleNestingDeferredWork consults self->nesting_level_
    496       // to determine whether to schedule nesting-deferred work.  It expects
    497       // the nesting level to be set to the depth of the loop that is going
    498       // to sleep or exiting.  It must be called before decrementing the
    499       // value so that the value still corresponds to the level of the exiting
    500       // loop.
    501       self->MaybeScheduleNestingDeferredWork();
    502       --self->nesting_level_;
    503       break;
    504 
    505     default:
    506       break;
    507   }
    508 
    509   self->EnterExitRunLoop(activity);
    510 }
    511 
    512 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop.  The default
    513 // implementation is a no-op.
    514 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
    515 }
    516 
    517 // Base version returns a standard NSAutoreleasePool.
    518 AutoreleasePoolType* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
    519   return [[NSAutoreleasePool alloc] init];
    520 }
    521 
    522 MessagePumpCFRunLoop::MessagePumpCFRunLoop()
    523     : quit_pending_(false) {
    524 }
    525 
    526 MessagePumpCFRunLoop::~MessagePumpCFRunLoop() {}
    527 
    528 // Called by MessagePumpCFRunLoopBase::DoRun.  If other CFRunLoopRun loops were
    529 // running lower on the run loop thread's stack when this object was created,
    530 // the same number of CFRunLoopRun loops must be running for the outermost call
    531 // to Run.  Run/DoRun are reentrant after that point.
    532 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) {
    533   // This is completely identical to calling CFRunLoopRun(), except autorelease
    534   // pool management is introduced.
    535   int result;
    536   do {
    537     MessagePumpScopedAutoreleasePool autorelease_pool(this);
    538     result = CFRunLoopRunInMode(kCFRunLoopDefaultMode,
    539                                 kCFTimeIntervalMax,
    540                                 false);
    541   } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished);
    542 }
    543 
    544 // Must be called on the run loop thread.
    545 void MessagePumpCFRunLoop::Quit() {
    546   // Stop the innermost run loop managed by this MessagePumpCFRunLoop object.
    547   if (nesting_level() == run_nesting_level()) {
    548     // This object is running the innermost loop, just stop it.
    549     CFRunLoopStop(run_loop());
    550   } else {
    551     // There's another loop running inside the loop managed by this object.
    552     // In other words, someone else called CFRunLoopRunInMode on the same
    553     // thread, deeper on the stack than the deepest Run call.  Don't preempt
    554     // other run loops, just mark this object to quit the innermost Run as
    555     // soon as the other inner loops not managed by Run are done.
    556     quit_pending_ = true;
    557   }
    558 }
    559 
    560 // Called by MessagePumpCFRunLoopBase::EnterExitObserver.
    561 void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) {
    562   if (activity == kCFRunLoopExit &&
    563       nesting_level() == run_nesting_level() &&
    564       quit_pending_) {
    565     // Quit was called while loops other than those managed by this object
    566     // were running further inside a run loop managed by this object.  Now
    567     // that all unmanaged inner run loops are gone, stop the loop running
    568     // just inside Run.
    569     CFRunLoopStop(run_loop());
    570     quit_pending_ = false;
    571   }
    572 }
    573 
    574 MessagePumpNSRunLoop::MessagePumpNSRunLoop()
    575     : keep_running_(true) {
    576   CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
    577   source_context.perform = NoOp;
    578   quit_source_ = CFRunLoopSourceCreate(NULL,  // allocator
    579                                        0,     // priority
    580                                        &source_context);
    581   CFRunLoopAddSourceToAllModes(run_loop(), quit_source_);
    582 }
    583 
    584 MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
    585   CFRunLoopRemoveSourceFromAllModes(run_loop(), quit_source_);
    586   CFRelease(quit_source_);
    587 }
    588 
    589 void MessagePumpNSRunLoop::DoRun(Delegate* delegate) {
    590   while (keep_running_) {
    591     // NSRunLoop manages autorelease pools itself.
    592     [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
    593                              beforeDate:[NSDate distantFuture]];
    594   }
    595 
    596   keep_running_ = true;
    597 }
    598 
    599 void MessagePumpNSRunLoop::Quit() {
    600   keep_running_ = false;
    601   CFRunLoopSourceSignal(quit_source_);
    602   CFRunLoopWakeUp(run_loop());
    603 }
    604 
    605 #if defined(OS_IOS)
    606 MessagePumpUIApplication::MessagePumpUIApplication()
    607     : run_loop_(NULL) {
    608 }
    609 
    610 MessagePumpUIApplication::~MessagePumpUIApplication() {}
    611 
    612 void MessagePumpUIApplication::DoRun(Delegate* delegate) {
    613   NOTREACHED();
    614 }
    615 
    616 void MessagePumpUIApplication::Quit() {
    617   NOTREACHED();
    618 }
    619 
    620 void MessagePumpUIApplication::Attach(Delegate* delegate) {
    621   DCHECK(!run_loop_);
    622   run_loop_ = new RunLoop();
    623   CHECK(run_loop_->BeforeRun());
    624   SetDelegate(delegate);
    625 }
    626 
    627 #else
    628 
    629 MessagePumpNSApplication::MessagePumpNSApplication()
    630     : keep_running_(true),
    631       running_own_loop_(false) {
    632 }
    633 
    634 MessagePumpNSApplication::~MessagePumpNSApplication() {}
    635 
    636 void MessagePumpNSApplication::DoRun(Delegate* delegate) {
    637   bool last_running_own_loop_ = running_own_loop_;
    638 
    639   // NSApp must be initialized by calling:
    640   // [{some class which implements CrAppProtocol} sharedApplication]
    641   // Most likely candidates are CrApplication or BrowserCrApplication.
    642   // These can be initialized from C++ code by calling
    643   // RegisterCrApp() or RegisterBrowserCrApp().
    644   CHECK(NSApp);
    645 
    646   if (![NSApp isRunning]) {
    647     running_own_loop_ = false;
    648     // NSApplication manages autorelease pools itself when run this way.
    649     [NSApp run];
    650   } else {
    651     running_own_loop_ = true;
    652     NSDate* distant_future = [NSDate distantFuture];
    653     while (keep_running_) {
    654       MessagePumpScopedAutoreleasePool autorelease_pool(this);
    655       NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
    656                                           untilDate:distant_future
    657                                              inMode:NSDefaultRunLoopMode
    658                                             dequeue:YES];
    659       if (event) {
    660         [NSApp sendEvent:event];
    661       }
    662     }
    663     keep_running_ = true;
    664   }
    665 
    666   running_own_loop_ = last_running_own_loop_;
    667 }
    668 
    669 void MessagePumpNSApplication::Quit() {
    670   if (!running_own_loop_) {
    671     [[NSApplication sharedApplication] stop:nil];
    672   } else {
    673     keep_running_ = false;
    674   }
    675 
    676   // Send a fake event to wake the loop up.
    677   [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
    678                                       location:NSZeroPoint
    679                                  modifierFlags:0
    680                                      timestamp:0
    681                                   windowNumber:0
    682                                        context:NULL
    683                                        subtype:0
    684                                          data1:0
    685                                          data2:0]
    686            atStart:NO];
    687 }
    688 
    689 MessagePumpCrApplication::MessagePumpCrApplication() {
    690 }
    691 
    692 MessagePumpCrApplication::~MessagePumpCrApplication() {
    693 }
    694 
    695 // Prevents an autorelease pool from being created if the app is in the midst of
    696 // handling a UI event because various parts of AppKit depend on objects that
    697 // are created while handling a UI event to be autoreleased in the event loop.
    698 // An example of this is NSWindowController. When a window with a window
    699 // controller is closed it goes through a stack like this:
    700 // (Several stack frames elided for clarity)
    701 //
    702 // #0 [NSWindowController autorelease]
    703 // #1 DoAClose
    704 // #2 MessagePumpCFRunLoopBase::DoWork()
    705 // #3 [NSRunLoop run]
    706 // #4 [NSButton performClick:]
    707 // #5 [NSWindow sendEvent:]
    708 // #6 [NSApp sendEvent:]
    709 // #7 [NSApp run]
    710 //
    711 // -performClick: spins a nested run loop. If the pool created in DoWork was a
    712 // standard NSAutoreleasePool, it would release the objects that were
    713 // autoreleased into it once DoWork released it. This would cause the window
    714 // controller, which autoreleased itself in frame #0, to release itself, and
    715 // possibly free itself. Unfortunately this window controller controls the
    716 // window in frame #5. When the stack is unwound to frame #5, the window would
    717 // no longer exists and crashes may occur. Apple gets around this by never
    718 // releasing the pool it creates in frame #4, and letting frame #7 clean it up
    719 // when it cleans up the pool that wraps frame #7. When an autorelease pool is
    720 // released it releases all other pools that were created after it on the
    721 // autorelease pool stack.
    722 //
    723 // CrApplication is responsible for setting handlingSendEvent to true just
    724 // before it sends the event through the event handling mechanism, and
    725 // returning it to its previous value once the event has been sent.
    726 AutoreleasePoolType* MessagePumpCrApplication::CreateAutoreleasePool() {
    727   if (MessagePumpMac::IsHandlingSendEvent())
    728     return nil;
    729   return MessagePumpNSApplication::CreateAutoreleasePool();
    730 }
    731 
    732 // static
    733 bool MessagePumpMac::UsingCrApp() {
    734   DCHECK([NSThread isMainThread]);
    735 
    736   // If NSApp is still not initialized, then the subclass used cannot
    737   // be determined.
    738   DCHECK(NSApp);
    739 
    740   // The pump was created using MessagePumpNSApplication.
    741   if (g_not_using_cr_app)
    742     return false;
    743 
    744   return [NSApp conformsToProtocol:@protocol(CrAppProtocol)];
    745 }
    746 
    747 // static
    748 bool MessagePumpMac::IsHandlingSendEvent() {
    749   DCHECK([NSApp conformsToProtocol:@protocol(CrAppProtocol)]);
    750   NSObject<CrAppProtocol>* app = static_cast<NSObject<CrAppProtocol>*>(NSApp);
    751   return [app isHandlingSendEvent];
    752 }
    753 #endif  // !defined(OS_IOS)
    754 
    755 // static
    756 MessagePump* MessagePumpMac::Create() {
    757   if ([NSThread isMainThread]) {
    758 #if defined(OS_IOS)
    759     return new MessagePumpUIApplication;
    760 #else
    761     if ([NSApp conformsToProtocol:@protocol(CrAppProtocol)])
    762       return new MessagePumpCrApplication;
    763 
    764     // The main-thread MessagePump implementations REQUIRE an NSApp.
    765     // Executables which have specific requirements for their
    766     // NSApplication subclass should initialize appropriately before
    767     // creating an event loop.
    768     [NSApplication sharedApplication];
    769     g_not_using_cr_app = true;
    770     return new MessagePumpNSApplication;
    771 #endif
    772   }
    773 
    774   return new MessagePumpNSRunLoop;
    775 }
    776 
    777 }  // namespace base
    778