ipc/chromium/src/base/message_pump_mac.mm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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.
     5 #include "base/message_pump_mac.h"
     7 #import <AppKit/AppKit.h>
     8 #import <Foundation/Foundation.h>
     9 #include <IOKit/IOMessage.h>
    10 #include <IOKit/pwr_mgt/IOPMLib.h>
    12 #include <limits>
    14 #import "base/chrome_application_mac.h"
    15 #include "base/logging.h"
    16 #include "base/time.h"
    18 namespace {
    20 void NoOp(void* info) {
    21 }
    23 const CFTimeInterval kCFTimeIntervalMax =
    24     std::numeric_limits<CFTimeInterval>::max();
    26 }  // namespace
    28 namespace base {
    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   }
    42  private:
    43   NSAutoreleasePool* pool_;
    44   DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool);
    45 };
    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_);
    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);
    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);
    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);
    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);
    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);
   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);
   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);
   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);
   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 }
   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   }
   154   CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_,
   155                           kCFRunLoopCommonModes);
   156   CFRelease(enter_exit_observer_);
   158   CFRunLoopRemoveObserver(run_loop_, pre_source_observer_,
   159                           kCFRunLoopCommonModes);
   160   CFRelease(pre_source_observer_);
   162   CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_,
   163                           kCFRunLoopCommonModes);
   164   CFRelease(pre_wait_observer_);
   166   CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_,
   167                         kCFRunLoopCommonModes);
   168   CFRelease(nesting_deferred_work_source_);
   170   CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes);
   171   CFRelease(idle_work_source_);
   173   CFRunLoopRemoveSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes);
   174   CFRelease(delayed_work_source_);
   176   CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes);
   177   CFRelease(work_source_);
   179   CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes);
   180   CFRelease(delayed_work_timer_);
   182   CFRelease(run_loop_);
   183 }
   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;
   192   Delegate* last_delegate = delegate_;
   193   delegate_ = delegate;
   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   }
   213   DoRun(delegate);
   215   // Restore the previous state of the object.
   216   delegate_ = last_delegate;
   217   run_nesting_level_ = last_run_nesting_level;
   218 }
   220 // May be called on any thread.
   221 void MessagePumpCFRunLoopBase::ScheduleWork() {
   222   CFRunLoopSourceSignal(work_source_);
   223   CFRunLoopWakeUp(run_loop_);
   224 }
   226 // Must be called on the run loop thread.
   227 void MessagePumpCFRunLoopBase::ScheduleDelayedWork(
   228     const TimeTicks& delayed_work_time) {
   229   TimeDelta delta = delayed_work_time - TimeTicks::Now();
   230   delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF();
   231   CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_);
   232 }
   234 // Called from the run loop.
   235 // static
   236 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer,
   237                                                    void* info) {
   238   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   240   // The timer won't fire again until it's reset.
   241   self->delayed_work_fire_time_ = kCFTimeIntervalMax;
   243   // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources.
   244   // In order to establish the proper priority where delegate_->DoDelayedWork
   245   // can only be called if delegate_->DoWork returns false, the timer used
   246   // to schedule delayed work must signal a CFRunLoopSource set at a lower
   247   // priority than the one used for delegate_->DoWork.
   248   CFRunLoopSourceSignal(self->delayed_work_source_);
   249 }
   251 // Called from the run loop.
   252 // static
   253 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) {
   254   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   255   self->RunWork();
   256 }
   258 // Called by MessagePumpCFRunLoopBase::RunWorkSource.
   259 bool MessagePumpCFRunLoopBase::RunWork() {
   260   if (!delegate_) {
   261     // This point can be reached with a NULL delegate_ if Run is not on the
   262     // stack but foreign code is spinning the CFRunLoop.  Arrange to come back
   263     // here when a delegate is available.
   264     delegateless_work_ = true;
   265     return false;
   266   }
   268   // The NSApplication-based run loop only drains the autorelease pool at each
   269   // UI event (NSEvent).  The autorelease pool is not drained for each
   270   // CFRunLoopSource target that's run.  Use a local pool for any autoreleased
   271   // objects if the app is not currently handling a UI event to ensure they're
   272   // released promptly even in the absence of UI events.
   273   MessagePumpScopedAutoreleasePool autorelease_pool(this);
   275   // Call DoWork once, and if something was done, arrange to come back here
   276   // again as long as the loop is still running.
   277   bool did_work = delegate_->DoWork();
   278   if (did_work) {
   279     CFRunLoopSourceSignal(work_source_);
   280   }
   282   return did_work;
   283 }
   285 // Called from the run loop.
   286 // static
   287 void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) {
   288   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   289   self->RunDelayedWork();
   290 }
   292 // Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource.
   293 bool MessagePumpCFRunLoopBase::RunDelayedWork() {
   294   if (!delegate_) {
   295     // This point can be reached with a NULL delegate_ if Run is not on the
   296     // stack but foreign code is spinning the CFRunLoop.  Arrange to come back
   297     // here when a delegate is available.
   298     delegateless_delayed_work_ = true;
   299     return false;
   300   }
   302   // The NSApplication-based run loop only drains the autorelease pool at each
   303   // UI event (NSEvent).  The autorelease pool is not drained for each
   304   // CFRunLoopSource target that's run.  Use a local pool for any autoreleased
   305   // objects if the app is not currently handling a UI event to ensure they're
   306   // released promptly even in the absence of UI events.
   307   MessagePumpScopedAutoreleasePool autorelease_pool(this);
   309   TimeTicks next_time;
   310   delegate_->DoDelayedWork(&next_time);
   312   bool more_work = !next_time.is_null();
   313   if (more_work) {
   314     TimeDelta delay = next_time - TimeTicks::Now();
   315     if (delay > TimeDelta()) {
   316       // There's more delayed work to be done in the future.
   317       ScheduleDelayedWork(next_time);
   318     } else {
   319       // There's more delayed work to be done, and its time is in the past.
   320       // Arrange to come back here directly as long as the loop is still
   321       // running.
   322       CFRunLoopSourceSignal(delayed_work_source_);
   323     }
   324   }
   326   return more_work;
   327 }
   329 // Called from the run loop.
   330 // static
   331 void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) {
   332   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   333   self->RunIdleWork();
   334 }
   336 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource.
   337 bool MessagePumpCFRunLoopBase::RunIdleWork() {
   338   if (!delegate_) {
   339     // This point can be reached with a NULL delegate_ if Run is not on the
   340     // stack but foreign code is spinning the CFRunLoop.  Arrange to come back
   341     // here when a delegate is available.
   342     delegateless_idle_work_ = true;
   343     return false;
   344   }
   346   // The NSApplication-based run loop only drains the autorelease pool at each
   347   // UI event (NSEvent).  The autorelease pool is not drained for each
   348   // CFRunLoopSource target that's run.  Use a local pool for any autoreleased
   349   // objects if the app is not currently handling a UI event to ensure they're
   350   // released promptly even in the absence of UI events.
   351   MessagePumpScopedAutoreleasePool autorelease_pool(this);
   353   // Call DoIdleWork once, and if something was done, arrange to come back here
   354   // again as long as the loop is still running.
   355   bool did_work = delegate_->DoIdleWork();
   356   if (did_work) {
   357     CFRunLoopSourceSignal(idle_work_source_);
   358   }
   360   return did_work;
   361 }
   363 // Called from the run loop.
   364 // static
   365 void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) {
   366   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   367   self->RunNestingDeferredWork();
   368 }
   370 // Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource.
   371 bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() {
   372   if (!delegate_) {
   373     // This point can be reached with a NULL delegate_ if Run is not on the
   374     // stack but foreign code is spinning the CFRunLoop.  There's no sense in
   375     // attempting to do any work or signalling the work sources because
   376     // without a delegate, work is not possible.
   377     return false;
   378   }
   380   // Immediately try work in priority order.
   381   if (!RunWork()) {
   382     if (!RunDelayedWork()) {
   383       if (!RunIdleWork()) {
   384         return false;
   385       }
   386     } else {
   387       // There was no work, and delayed work was done.  Arrange for the loop
   388       // to try non-nestable idle work on a subsequent pass.
   389       CFRunLoopSourceSignal(idle_work_source_);
   390     }
   391   } else {
   392     // Work was done.  Arrange for the loop to try non-nestable delayed and
   393     // idle work on a subsequent pass.
   394     CFRunLoopSourceSignal(delayed_work_source_);
   395     CFRunLoopSourceSignal(idle_work_source_);
   396   }
   398   return true;
   399 }
   401 // Called before the run loop goes to sleep or exits, or processes sources.
   402 void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() {
   403   // deepest_nesting_level_ is set as run loops are entered.  If the deepest
   404   // level encountered is deeper than the current level, a nested loop
   405   // (relative to the current level) ran since the last time nesting-deferred
   406   // work was scheduled.  When that situation is encountered, schedule
   407   // nesting-deferred work in case any work was deferred because nested work
   408   // was disallowed.
   409   if (deepest_nesting_level_ > nesting_level_) {
   410     deepest_nesting_level_ = nesting_level_;
   411     CFRunLoopSourceSignal(nesting_deferred_work_source_);
   412   }
   413 }
   415 // Called from the run loop.
   416 // static
   417 void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer,
   418                                                CFRunLoopActivity activity,
   419                                                void* info) {
   420   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   422   // Attempt to do some idle work before going to sleep.
   423   self->RunIdleWork();
   425   // The run loop is about to go to sleep.  If any of the work done since it
   426   // started or woke up resulted in a nested run loop running,
   427   // nesting-deferred work may have accumulated.  Schedule it for processing
   428   // if appropriate.
   429   self->MaybeScheduleNestingDeferredWork();
   430 }
   432 // Called from the run loop.
   433 // static
   434 void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer,
   435                                                  CFRunLoopActivity activity,
   436                                                  void* info) {
   437   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   439   // The run loop has reached the top of the loop and is about to begin
   440   // processing sources.  If the last iteration of the loop at this nesting
   441   // level did not sleep or exit, nesting-deferred work may have accumulated
   442   // if a nested loop ran.  Schedule nesting-deferred work for processing if
   443   // appropriate.
   444   self->MaybeScheduleNestingDeferredWork();
   445 }
   447 // Called from the run loop.
   448 // static
   449 void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer,
   450                                                  CFRunLoopActivity activity,
   451                                                  void* info) {
   452   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   454   switch (activity) {
   455     case kCFRunLoopEntry:
   456       ++self->nesting_level_;
   457       if (self->nesting_level_ > self->deepest_nesting_level_) {
   458         self->deepest_nesting_level_ = self->nesting_level_;
   459       }
   460       break;
   462     case kCFRunLoopExit:
   463       // Not all run loops go to sleep.  If a run loop is stopped before it
   464       // goes to sleep due to a CFRunLoopStop call, or if the timeout passed
   465       // to CFRunLoopRunInMode expires, the run loop may proceed directly from
   466       // handling sources to exiting without any sleep.  This most commonly
   467       // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it
   468       // to make a single pass through the loop and exit without sleep.  Some
   469       // native loops use CFRunLoop in this way.  Because PreWaitObserver will
   470       // not be called in these case, MaybeScheduleNestingDeferredWork needs
   471       // to be called here, as the run loop exits.
   472       //
   473       // MaybeScheduleNestingDeferredWork consults self->nesting_level_
   474       // to determine whether to schedule nesting-deferred work.  It expects
   475       // the nesting level to be set to the depth of the loop that is going
   476       // to sleep or exiting.  It must be called before decrementing the
   477       // value so that the value still corresponds to the level of the exiting
   478       // loop.
   479       self->MaybeScheduleNestingDeferredWork();
   480       --self->nesting_level_;
   481       break;
   483     default:
   484       break;
   485   }
   487   self->EnterExitRunLoop(activity);
   488 }
   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/
   533   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   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;
   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;
   552     default:
   553       break;
   554   }
   555 }
   557 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop.  The default
   558 // implementation is a no-op.
   559 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
   560 }
   562 // Base version returns a standard NSAutoreleasePool.
   563 NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
   564   return [[NSAutoreleasePool alloc] init];
   565 }
   567 MessagePumpCFRunLoop::MessagePumpCFRunLoop()
   568     : quit_pending_(false) {
   569 }
   571 // Called by MessagePumpCFRunLoopBase::DoRun.  If other CFRunLoopRun loops were
   572 // running lower on the run loop thread's stack when this object was created,
   573 // the same number of CFRunLoopRun loops must be running for the outermost call
   574 // to Run.  Run/DoRun are reentrant after that point.
   575 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) {
   576   // This is completely identical to calling CFRunLoopRun(), except autorelease
   577   // pool management is introduced.
   578   int result;
   579   do {
   580     MessagePumpScopedAutoreleasePool autorelease_pool(this);
   581     result = CFRunLoopRunInMode(kCFRunLoopDefaultMode,
   582                                 kCFTimeIntervalMax,
   583                                 false);
   584   } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished);
   585 }
   587 // Must be called on the run loop thread.
   588 void MessagePumpCFRunLoop::Quit() {
   589   // Stop the innermost run loop managed by this MessagePumpCFRunLoop object.
   590   if (nesting_level() == run_nesting_level()) {
   591     // This object is running the innermost loop, just stop it.
   592     CFRunLoopStop(run_loop());
   593   } else {
   594     // There's another loop running inside the loop managed by this object.
   595     // In other words, someone else called CFRunLoopRunInMode on the same
   596     // thread, deeper on the stack than the deepest Run call.  Don't preempt
   597     // other run loops, just mark this object to quit the innermost Run as
   598     // soon as the other inner loops not managed by Run are done.
   599     quit_pending_ = true;
   600   }
   601 }
   603 // Called by MessagePumpCFRunLoopBase::EnterExitObserver.
   604 void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) {
   605   if (activity == kCFRunLoopExit &&
   606       nesting_level() == run_nesting_level() &&
   607       quit_pending_) {
   608     // Quit was called while loops other than those managed by this object
   609     // were running further inside a run loop managed by this object.  Now
   610     // that all unmanaged inner run loops are gone, stop the loop running
   611     // just inside Run.
   612     CFRunLoopStop(run_loop());
   613     quit_pending_ = false;
   614   }
   615 }
   617 MessagePumpNSRunLoop::MessagePumpNSRunLoop()
   618     : keep_running_(true) {
   619   CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
   620   source_context.perform = NoOp;
   621   quit_source_ = CFRunLoopSourceCreate(NULL,  // allocator
   622                                        0,     // priority
   623                                        &source_context);
   624   CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
   625 }
   627 MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
   628   CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
   629   CFRelease(quit_source_);
   630 }
   632 void MessagePumpNSRunLoop::DoRun(Delegate* delegate) {
   633   while (keep_running_) {
   634     // NSRunLoop manages autorelease pools itself.
   635     [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
   636                              beforeDate:[NSDate distantFuture]];
   637   }
   639   keep_running_ = true;
   640 }
   642 void MessagePumpNSRunLoop::Quit() {
   643   keep_running_ = false;
   644   CFRunLoopSourceSignal(quit_source_);
   645   CFRunLoopWakeUp(run_loop());
   646 }
   648 MessagePumpNSApplication::MessagePumpNSApplication()
   649     : keep_running_(true),
   650       running_own_loop_(false) {
   651 }
   653 void MessagePumpNSApplication::DoRun(Delegate* delegate) {
   654   bool last_running_own_loop_ = running_own_loop_;
   656   // TODO(dmaclach): Get rid of this gratuitous sharedApplication.
   657   // Tests should be setting up their applications on their own.
   658   [CrApplication sharedApplication];
   660   if (![NSApp isRunning]) {
   661     running_own_loop_ = false;
   662     // NSApplication manages autorelease pools itself when run this way.
   663     [NSApp run];
   664   } else {
   665     running_own_loop_ = true;
   666     NSDate* distant_future = [NSDate distantFuture];
   667     while (keep_running_) {
   668       MessagePumpScopedAutoreleasePool autorelease_pool(this);
   669       NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
   670                                           untilDate:distant_future
   671                                              inMode:NSDefaultRunLoopMode
   672                                             dequeue:YES];
   673       if (event) {
   674         [NSApp sendEvent:event];
   675       }
   676     }
   677     keep_running_ = true;
   678   }
   680   running_own_loop_ = last_running_own_loop_;
   681 }
   683 void MessagePumpNSApplication::Quit() {
   684   if (!running_own_loop_) {
   685     [NSApp stop:nil];
   686   } else {
   687     keep_running_ = false;
   688   }
   690   // Send a fake event to wake the loop up.
   691   [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
   692                                       location:NSMakePoint(0, 0)
   693                                  modifierFlags:0
   694                                      timestamp:0
   695                                   windowNumber:0
   696                                        context:NULL
   697                                        subtype:0
   698                                          data1:0
   699                                          data2:0]
   700            atStart:NO];
   701 }
   703 // Prevents an autorelease pool from being created if the app is in the midst of
   704 // handling a UI event because various parts of AppKit depend on objects that
   705 // are created while handling a UI event to be autoreleased in the event loop.
   706 // An example of this is NSWindowController. When a window with a window
   707 // controller is closed it goes through a stack like this:
   708 // (Several stack frames elided for clarity)
   709 //
   710 // #0 [NSWindowController autorelease]
   711 // #1 DoAClose
   712 // #2 MessagePumpCFRunLoopBase::DoWork()
   713 // #3 [NSRunLoop run]
   714 // #4 [NSButton performClick:]
   715 // #5 [NSWindow sendEvent:]
   716 // #6 [NSApp sendEvent:]
   717 // #7 [NSApp run]
   718 //
   719 // -performClick: spins a nested run loop. If the pool created in DoWork was a
   720 // standard NSAutoreleasePool, it would release the objects that were
   721 // autoreleased into it once DoWork released it. This would cause the window
   722 // controller, which autoreleased itself in frame #0, to release itself, and
   723 // possibly free itself. Unfortunately this window controller controls the
   724 // window in frame #5. When the stack is unwound to frame #5, the window would
   725 // no longer exists and crashes may occur. Apple gets around this by never
   726 // releasing the pool it creates in frame #4, and letting frame #7 clean it up
   727 // when it cleans up the pool that wraps frame #7. When an autorelease pool is
   728 // released it releases all other pools that were created after it on the
   729 // autorelease pool stack.
   730 //
   731 // CrApplication is responsible for setting handlingSendEvent to true just
   732 // before it sends the event throught the event handling mechanism, and
   733 // returning it to its previous value once the event has been sent.
   734 NSAutoreleasePool* MessagePumpNSApplication::CreateAutoreleasePool() {
   735   NSAutoreleasePool* pool = nil;
   736   DCHECK([NSApp isKindOfClass:[CrApplication class]]);
   737   if (![static_cast<CrApplication*>(NSApp) isHandlingSendEvent]) {
   738     pool = MessagePumpCFRunLoopBase::CreateAutoreleasePool();
   739   }
   740   return pool;
   741 }
   743 // static
   744 MessagePump* MessagePumpMac::Create() {
   745   if ([NSThread isMainThread]) {
   746     return new MessagePumpNSApplication;
   747   }
   749   return new MessagePumpNSRunLoop;
   750 }
   752 }  // namespace base

mercurial