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