tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

message_pump_mac.mm (29792B)


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