Home | History | Annotate | Download | only in message_loop
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #import "base/message_loop/message_pump_mac.h"
      6 
      7 #import <Foundation/Foundation.h>
      8 
      9 #include <limits>
     10 #include <stack>
     11 
     12 #include "base/format_macros.h"
     13 #include "base/logging.h"
     14 #include "base/mac/scoped_cftyperef.h"
     15 #include "base/metrics/histogram.h"
     16 #include "base/run_loop.h"
     17 #include "base/strings/stringprintf.h"
     18 #include "base/time/time.h"
     19 
     20 #if !defined(OS_IOS)
     21 #import <AppKit/AppKit.h>
     22 #endif  // !defined(OS_IOS)
     23 
     24 namespace {
     25 
     26 void NoOp(void* info) {
     27 }
     28 
     29 const CFTimeInterval kCFTimeIntervalMax =
     30     std::numeric_limits<CFTimeInterval>::max();
     31 
     32 #if !defined(OS_IOS)
     33 // Set to true if MessagePumpMac::Create() is called before NSApp is
     34 // initialized.  Only accessed from the main thread.
     35 bool g_not_using_cr_app = false;
     36 #endif
     37 
     38 }  // namespace
     39 
     40 namespace base {
     41 
     42 // A scoper for autorelease pools created from message pump run loops.
     43 // Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare
     44 // case where an autorelease pool needs to be passed in.
     45 class MessagePumpScopedAutoreleasePool {
     46  public:
     47   explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) :
     48       pool_(pump->CreateAutoreleasePool()) {
     49   }
     50    ~MessagePumpScopedAutoreleasePool() {
     51     [pool_ drain];
     52   }
     53 
     54  private:
     55   NSAutoreleasePool* pool_;
     56   DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool);
     57 };
     58 
     59 // This class is used to instrument the MessagePump to gather various timing
     60 // data about when the underlying run loop is entered, when it is waiting, and
     61 // when it is servicing its delegate.
     62 //
     63 // The metrics are gathered as UMA-tracked histograms. To gather the data over
     64 // time, sampling is used, such that a new histogram is created for each metric
     65 // every |sampling_interval| for |sampling_duration|. After sampling is
     66 // complete, this class deletes itself.
     67 class MessagePumpInstrumentation {
     68  public:
     69   // Creates an instrument for the MessagePump on the current thread. Every
     70   // |sampling_interval|, a new histogram will be created to track the metrics
     71   // over time. After |sampling_duration|, this will delete itself, causing the
     72   // WeakPtr to go NULL.
     73   static WeakPtr<MessagePumpInstrumentation> Create(
     74       const TimeDelta& sampling_interval,
     75       const TimeDelta& sampling_duration) {
     76     MessagePumpInstrumentation* instrument =
     77         new MessagePumpInstrumentation(sampling_interval, sampling_duration);
     78     return instrument->weak_ptr_factory_.GetWeakPtr();
     79   }
     80 
     81   // Starts the timer that runs the sampling instrumentation. Can be called
     82   // multiple times as a noop.
     83   void StartIfNeeded() {
     84     if (timer_)
     85       return;
     86 
     87     sampling_start_time_ = generation_start_time_ = TimeTicks::Now();
     88 
     89     CFRunLoopTimerContext timer_context = { .info = this };
     90     timer_.reset(CFRunLoopTimerCreate(
     91         NULL,  // allocator
     92         (Time::Now() + sampling_interval_).ToCFAbsoluteTime(),
     93         sampling_interval_.InSecondsF(),
     94         0,  // flags
     95         0,  // order
     96         &MessagePumpInstrumentation::TimerFired,
     97         &timer_context));
     98     CFRunLoopAddTimer(CFRunLoopGetCurrent(),
     99                       timer_,
    100                       kCFRunLoopCommonModes);
    101   }
    102 
    103   // Used to track kCFRunLoopEntry.
    104   void LoopEntered() {
    105     loop_run_times_.push(TimeTicks::Now());
    106   }
    107 
    108   // Used to track kCFRunLoopExit.
    109   void LoopExited() {
    110     TimeDelta duration = TimeTicks::Now() - loop_run_times_.top();
    111     loop_run_times_.pop();
    112     GetHistogram(LOOP_CYCLE)->AddTime(duration);
    113   }
    114 
    115   // Used to track kCFRunLoopBeforeWaiting.
    116   void WaitingStarted() {
    117     loop_wait_times_.push(TimeTicks::Now());
    118   }
    119 
    120   // Used to track kCFRunLoopAfterWaiting.
    121   void WaitingFinished() {
    122     TimeDelta duration = TimeTicks::Now() - loop_wait_times_.top();
    123     loop_wait_times_.pop();
    124     GetHistogram(LOOP_WAIT)->AddTime(duration);
    125   }
    126 
    127   // Used to track when the MessagePump will invoke its |delegate|.
    128   void WorkSourceEntered(MessagePump::Delegate* delegate) {
    129     work_source_times_.push(TimeTicks::Now());
    130     if (delegate) {
    131       size_t queue_size;
    132       TimeDelta queuing_delay;
    133       delegate->GetQueueingInformation(&queue_size, &queuing_delay);
    134       GetHistogram(QUEUE_SIZE)->Add(queue_size);
    135       GetHistogram(QUEUE_DELAY)->AddTime(queuing_delay);
    136     }
    137   }
    138 
    139   // Used to track the completion of servicing the MessagePump::Delegate.
    140   void WorkSourceExited() {
    141     TimeDelta duration = TimeTicks::Now() - work_source_times_.top();
    142     work_source_times_.pop();
    143     GetHistogram(WORK_SOURCE)->AddTime(duration);
    144   }
    145 
    146  private:
    147   enum HistogramEvent {
    148     // Time-based histograms:
    149     LOOP_CYCLE,  // LoopEntered/LoopExited
    150     LOOP_WAIT,  // WaitingStarted/WaitingEnded
    151     WORK_SOURCE,  // WorkSourceExited
    152     QUEUE_DELAY,  // WorkSourceEntered
    153 
    154     // Value-based histograms:
    155     // NOTE: Do not add value-based histograms before this event, only after.
    156     QUEUE_SIZE,  // WorkSourceEntered
    157 
    158     HISTOGRAM_EVENT_MAX,
    159   };
    160 
    161   MessagePumpInstrumentation(const TimeDelta& sampling_interval,
    162                              const TimeDelta& sampling_duration)
    163       : weak_ptr_factory_(this),
    164         sampling_interval_(sampling_interval),
    165         sampling_duration_(sampling_duration),
    166         sample_generation_(0) {
    167     // Create all the histogram objects that will be used for sampling.
    168     const char kHistogramName[] = "MessagePumpMac.%s.SampleMs.%" PRId64;
    169     for (TimeDelta i; i < sampling_duration_; i += sampling_interval_) {
    170       int64 sample = i.InMilliseconds();
    171 
    172       // Generate the time-based histograms.
    173       for (int j = LOOP_CYCLE; j < QUEUE_SIZE; ++j) {
    174         std::string name = StringPrintf(kHistogramName,
    175             NameForEnum(static_cast<HistogramEvent>(j)), sample);
    176         histograms_[j].push_back(
    177             Histogram::FactoryTimeGet(name, TimeDelta::FromMilliseconds(1),
    178                 sampling_interval_, 50,
    179                 HistogramBase::kUmaTargetedHistogramFlag));
    180       }
    181 
    182       // Generate the value-based histograms.
    183       for (int j = QUEUE_SIZE; j < HISTOGRAM_EVENT_MAX; ++j) {
    184         std::string name = StringPrintf(kHistogramName,
    185             NameForEnum(static_cast<HistogramEvent>(j)), sample);
    186         histograms_[j].push_back(
    187             Histogram::FactoryGet(name, 1, 10000, 50,
    188                 HistogramBase::kUmaTargetedHistogramFlag));
    189       }
    190     }
    191   }
    192 
    193   ~MessagePumpInstrumentation() {
    194     if (timer_)
    195       CFRunLoopTimerInvalidate(timer_);
    196   }
    197 
    198   const char* NameForEnum(HistogramEvent event) {
    199     switch (event) {
    200       case LOOP_CYCLE: return "LoopCycle";
    201       case LOOP_WAIT: return "Waiting";
    202       case WORK_SOURCE: return "WorkSource";
    203       case QUEUE_DELAY: return "QueueingDelay";
    204       case QUEUE_SIZE: return "QueueSize";
    205       default:
    206         NOTREACHED();
    207         return NULL;
    208     }
    209   }
    210 
    211   static void TimerFired(CFRunLoopTimerRef timer, void* context) {
    212     static_cast<MessagePumpInstrumentation*>(context)->TimerFired();
    213   }
    214 
    215   // Called by the run loop when the sampling_interval_ has elapsed. Advances
    216   // the sample_generation_, which controls into which histogram data is
    217   // recorded, while recording and accounting for timer skew. Will delete this
    218   // object after |sampling_duration_| has elapsed.
    219   void TimerFired() {
    220     TimeTicks now = TimeTicks::Now();
    221     TimeDelta delta = now - generation_start_time_;
    222 
    223     // The timer fired, so advance the generation by at least one.
    224     ++sample_generation_;
    225 
    226     // To account for large timer skew/drift, advance the generation by any
    227     // more completed intervals.
    228     for (TimeDelta skew_advance = delta - sampling_interval_;
    229          skew_advance >= sampling_interval_;
    230          skew_advance -= sampling_interval_) {
    231       ++sample_generation_;
    232     }
    233 
    234     generation_start_time_ = now;
    235     if (now >= sampling_start_time_ + sampling_duration_)
    236       delete this;
    237   }
    238 
    239   HistogramBase* GetHistogram(HistogramEvent event) {
    240     DCHECK_LT(sample_generation_, histograms_[event].size());
    241     return histograms_[event][sample_generation_];
    242   }
    243 
    244   // Vends the pointer to the Create()or.
    245   WeakPtrFactory<MessagePumpInstrumentation> weak_ptr_factory_;
    246 
    247   // The interval and duration of the sampling.
    248   TimeDelta sampling_interval_;
    249   TimeDelta sampling_duration_;
    250 
    251   // The time at which sampling started.
    252   TimeTicks sampling_start_time_;
    253 
    254   // The timer that advances the sample_generation_ and sets the
    255   // generation_start_time_ for the current sample interval.
    256   base::ScopedCFTypeRef<CFRunLoopTimerRef> timer_;
    257 
    258   // The two-dimensional array of histograms. The first dimension is the
    259   // HistogramEvent type. The second is for the sampling intervals.
    260   std::vector<HistogramBase*> histograms_[HISTOGRAM_EVENT_MAX];
    261 
    262   // The index in the second dimension of histograms_, which controls in which
    263   // sampled histogram events are recorded.
    264   size_t sample_generation_;
    265 
    266   // The last time at which the timer fired. This is used to track timer skew
    267   // (i.e. it did not fire on time) and properly account for it when advancing
    268   // samle_generation_.
    269   TimeTicks generation_start_time_;
    270 
    271   // MessagePump activations can be nested. Use a stack for each of the
    272   // possibly reentrant HistogramEvent types to properly balance and calculate
    273   // the timing information.
    274   std::stack<TimeTicks> loop_run_times_;
    275   std::stack<TimeTicks> loop_wait_times_;
    276   std::stack<TimeTicks> work_source_times_;
    277 
    278   DISALLOW_COPY_AND_ASSIGN(MessagePumpInstrumentation);
    279 };
    280 
    281 // Must be called on the run loop thread.
    282 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
    283     : delegate_(NULL),
    284       delayed_work_fire_time_(kCFTimeIntervalMax),
    285       nesting_level_(0),
    286       run_nesting_level_(0),
    287       deepest_nesting_level_(0),
    288       delegateless_work_(false),
    289       delegateless_idle_work_(false) {
    290   run_loop_ = CFRunLoopGetCurrent();
    291   CFRetain(run_loop_);
    292 
    293   // Set a repeating timer with a preposterous firing time and interval.  The
    294   // timer will effectively never fire as-is.  The firing time will be adjusted
    295   // as needed when ScheduleDelayedWork is called.
    296   CFRunLoopTimerContext timer_context = CFRunLoopTimerContext();
    297   timer_context.info = this;
    298   delayed_work_timer_ = CFRunLoopTimerCreate(NULL,                // allocator
    299                                              kCFTimeIntervalMax,  // fire time
    300                                              kCFTimeIntervalMax,  // interval
    301                                              0,                   // flags
    302                                              0,                   // priority
    303                                              RunDelayedWorkTimer,
    304                                              &timer_context);
    305   CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes);
    306 
    307   CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
    308   source_context.info = this;
    309   source_context.perform = RunWorkSource;
    310   work_source_ = CFRunLoopSourceCreate(NULL,  // allocator
    311                                        1,     // priority
    312                                        &source_context);
    313   CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes);
    314 
    315   source_context.perform = RunIdleWorkSource;
    316   idle_work_source_ = CFRunLoopSourceCreate(NULL,  // allocator
    317                                             2,     // priority
    318                                             &source_context);
    319   CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes);
    320 
    321   source_context.perform = RunNestingDeferredWorkSource;
    322   nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL,  // allocator
    323                                                         0,     // priority
    324                                                         &source_context);
    325   CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_,
    326                      kCFRunLoopCommonModes);
    327 
    328   CFRunLoopObserverContext observer_context = CFRunLoopObserverContext();
    329   observer_context.info = this;
    330   pre_wait_observer_ = CFRunLoopObserverCreate(NULL,  // allocator
    331                                                kCFRunLoopBeforeWaiting |
    332                                                   kCFRunLoopAfterWaiting,
    333                                                true,  // repeat
    334                                                0,     // priority
    335                                                StartOrEndWaitObserver,
    336                                                &observer_context);
    337   CFRunLoopAddObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes);
    338 
    339   pre_source_observer_ = CFRunLoopObserverCreate(NULL,  // allocator
    340                                                  kCFRunLoopBeforeSources,
    341                                                  true,  // repeat
    342                                                  0,     // priority
    343                                                  PreSourceObserver,
    344                                                  &observer_context);
    345   CFRunLoopAddObserver(run_loop_, pre_source_observer_, kCFRunLoopCommonModes);
    346 
    347   enter_exit_observer_ = CFRunLoopObserverCreate(NULL,  // allocator
    348                                                  kCFRunLoopEntry |
    349                                                      kCFRunLoopExit,
    350                                                  true,  // repeat
    351                                                  0,     // priority
    352                                                  EnterExitObserver,
    353                                                  &observer_context);
    354   CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes);
    355 }
    356 
    357 // Ideally called on the run loop thread.  If other run loops were running
    358 // lower on the run loop thread's stack when this object was created, the
    359 // same number of run loops must be running when this object is destroyed.
    360 MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() {
    361   CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_,
    362                           kCFRunLoopCommonModes);
    363   CFRelease(enter_exit_observer_);
    364 
    365   CFRunLoopRemoveObserver(run_loop_, pre_source_observer_,
    366                           kCFRunLoopCommonModes);
    367   CFRelease(pre_source_observer_);
    368 
    369   CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_,
    370                           kCFRunLoopCommonModes);
    371   CFRelease(pre_wait_observer_);
    372 
    373   CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_,
    374                         kCFRunLoopCommonModes);
    375   CFRelease(nesting_deferred_work_source_);
    376 
    377   CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes);
    378   CFRelease(idle_work_source_);
    379 
    380   CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes);
    381   CFRelease(work_source_);
    382 
    383   CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes);
    384   CFRelease(delayed_work_timer_);
    385 
    386   CFRelease(run_loop_);
    387 }
    388 
    389 // Must be called on the run loop thread.
    390 void MessagePumpCFRunLoopBase::Run(Delegate* delegate) {
    391   // nesting_level_ will be incremented in EnterExitRunLoop, so set
    392   // run_nesting_level_ accordingly.
    393   int last_run_nesting_level = run_nesting_level_;
    394   run_nesting_level_ = nesting_level_ + 1;
    395 
    396   Delegate* last_delegate = delegate_;
    397   SetDelegate(delegate);
    398 
    399   DoRun(delegate);
    400 
    401   // Restore the previous state of the object.
    402   SetDelegate(last_delegate);
    403   run_nesting_level_ = last_run_nesting_level;
    404 }
    405 
    406 void MessagePumpCFRunLoopBase::SetDelegate(Delegate* delegate) {
    407   delegate_ = delegate;
    408 
    409   if (delegate) {
    410     // If any work showed up but could not be dispatched for want of a
    411     // delegate, set it up for dispatch again now that a delegate is
    412     // available.
    413     if (delegateless_work_) {
    414       CFRunLoopSourceSignal(work_source_);
    415       delegateless_work_ = false;
    416     }
    417     if (delegateless_idle_work_) {
    418       CFRunLoopSourceSignal(idle_work_source_);
    419       delegateless_idle_work_ = false;
    420     }
    421   }
    422 }
    423 
    424 void MessagePumpCFRunLoopBase::EnableInstrumentation() {
    425   instrumentation_ = MessagePumpInstrumentation::Create(
    426       TimeDelta::FromSeconds(1), TimeDelta::FromSeconds(15));
    427 }
    428 
    429 // May be called on any thread.
    430 void MessagePumpCFRunLoopBase::ScheduleWork() {
    431   CFRunLoopSourceSignal(work_source_);
    432   CFRunLoopWakeUp(run_loop_);
    433 }
    434 
    435 // Must be called on the run loop thread.
    436 void MessagePumpCFRunLoopBase::ScheduleDelayedWork(
    437     const TimeTicks& delayed_work_time) {
    438   TimeDelta delta = delayed_work_time - TimeTicks::Now();
    439   delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF();
    440   CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_);
    441 }
    442 
    443 // Called from the run loop.
    444 // static
    445 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer,
    446                                                    void* info) {
    447   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    448 
    449   // The timer won't fire again until it's reset.
    450   self->delayed_work_fire_time_ = kCFTimeIntervalMax;
    451 
    452   // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources.
    453   // In order to establish the proper priority in which work and delayed work
    454   // are processed one for one, the timer used to schedule delayed work must
    455   // signal a CFRunLoopSource used to dispatch both work and delayed work.
    456   CFRunLoopSourceSignal(self->work_source_);
    457 }
    458 
    459 // Called from the run loop.
    460 // static
    461 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) {
    462   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    463   self->RunWork();
    464 }
    465 
    466 // Called by MessagePumpCFRunLoopBase::RunWorkSource.
    467 bool MessagePumpCFRunLoopBase::RunWork() {
    468   if (!delegate_) {
    469     // This point can be reached with a NULL delegate_ if Run is not on the
    470     // stack but foreign code is spinning the CFRunLoop.  Arrange to come back
    471     // here when a delegate is available.
    472     delegateless_work_ = true;
    473     return false;
    474   }
    475 
    476   if (instrumentation_)
    477     instrumentation_->WorkSourceEntered(delegate_);
    478 
    479   // The NSApplication-based run loop only drains the autorelease pool at each
    480   // UI event (NSEvent).  The autorelease pool is not drained for each
    481   // CFRunLoopSource target that's run.  Use a local pool for any autoreleased
    482   // objects if the app is not currently handling a UI event to ensure they're
    483   // released promptly even in the absence of UI events.
    484   MessagePumpScopedAutoreleasePool autorelease_pool(this);
    485 
    486   // Call DoWork and DoDelayedWork once, and if something was done, arrange to
    487   // come back here again as long as the loop is still running.
    488   bool did_work = delegate_->DoWork();
    489   bool resignal_work_source = did_work;
    490 
    491   TimeTicks next_time;
    492   delegate_->DoDelayedWork(&next_time);
    493   if (!did_work) {
    494     // Determine whether there's more delayed work, and if so, if it needs to
    495     // be done at some point in the future or if it's already time to do it.
    496     // Only do these checks if did_work is false. If did_work is true, this
    497     // function, and therefore any additional delayed work, will get another
    498     // chance to run before the loop goes to sleep.
    499     bool more_delayed_work = !next_time.is_null();
    500     if (more_delayed_work) {
    501       TimeDelta delay = next_time - TimeTicks::Now();
    502       if (delay > TimeDelta()) {
    503         // There's more delayed work to be done in the future.
    504         ScheduleDelayedWork(next_time);
    505       } else {
    506         // There's more delayed work to be done, and its time is in the past.
    507         // Arrange to come back here directly as long as the loop is still
    508         // running.
    509         resignal_work_source = true;
    510       }
    511     }
    512   }
    513 
    514   if (resignal_work_source) {
    515     CFRunLoopSourceSignal(work_source_);
    516   }
    517 
    518   if (instrumentation_)
    519     instrumentation_->WorkSourceExited();
    520 
    521   return resignal_work_source;
    522 }
    523 
    524 // Called from the run loop.
    525 // static
    526 void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) {
    527   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    528   self->RunIdleWork();
    529 }
    530 
    531 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource.
    532 bool MessagePumpCFRunLoopBase::RunIdleWork() {
    533   if (!delegate_) {
    534     // This point can be reached with a NULL delegate_ if Run is not on the
    535     // stack but foreign code is spinning the CFRunLoop.  Arrange to come back
    536     // here when a delegate is available.
    537     delegateless_idle_work_ = true;
    538     return false;
    539   }
    540 
    541   // The NSApplication-based run loop only drains the autorelease pool at each
    542   // UI event (NSEvent).  The autorelease pool is not drained for each
    543   // CFRunLoopSource target that's run.  Use a local pool for any autoreleased
    544   // objects if the app is not currently handling a UI event to ensure they're
    545   // released promptly even in the absence of UI events.
    546   MessagePumpScopedAutoreleasePool autorelease_pool(this);
    547 
    548   // Call DoIdleWork once, and if something was done, arrange to come back here
    549   // again as long as the loop is still running.
    550   bool did_work = delegate_->DoIdleWork();
    551   if (did_work) {
    552     CFRunLoopSourceSignal(idle_work_source_);
    553   }
    554 
    555   return did_work;
    556 }
    557 
    558 // Called from the run loop.
    559 // static
    560 void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) {
    561   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    562   self->RunNestingDeferredWork();
    563 }
    564 
    565 // Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource.
    566 bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() {
    567   if (!delegate_) {
    568     // This point can be reached with a NULL delegate_ if Run is not on the
    569     // stack but foreign code is spinning the CFRunLoop.  There's no sense in
    570     // attempting to do any work or signalling the work sources because
    571     // without a delegate, work is not possible.
    572     return false;
    573   }
    574 
    575   // Immediately try work in priority order.
    576   if (!RunWork()) {
    577     if (!RunIdleWork()) {
    578       return false;
    579     }
    580   } else {
    581     // Work was done.  Arrange for the loop to try non-nestable idle work on
    582     // a subsequent pass.
    583     CFRunLoopSourceSignal(idle_work_source_);
    584   }
    585 
    586   return true;
    587 }
    588 
    589 // Called before the run loop goes to sleep or exits, or processes sources.
    590 void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() {
    591   // deepest_nesting_level_ is set as run loops are entered.  If the deepest
    592   // level encountered is deeper than the current level, a nested loop
    593   // (relative to the current level) ran since the last time nesting-deferred
    594   // work was scheduled.  When that situation is encountered, schedule
    595   // nesting-deferred work in case any work was deferred because nested work
    596   // was disallowed.
    597   if (deepest_nesting_level_ > nesting_level_) {
    598     deepest_nesting_level_ = nesting_level_;
    599     CFRunLoopSourceSignal(nesting_deferred_work_source_);
    600   }
    601 }
    602 
    603 // Called from the run loop.
    604 // static
    605 void MessagePumpCFRunLoopBase::StartOrEndWaitObserver(
    606     CFRunLoopObserverRef observer,
    607     CFRunLoopActivity activity,
    608     void* info) {
    609   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    610 
    611   if (activity == kCFRunLoopAfterWaiting) {
    612     if (self->instrumentation_)
    613       self->instrumentation_->WaitingFinished();
    614     return;
    615   }
    616 
    617   // Attempt to do some idle work before going to sleep.
    618   self->RunIdleWork();
    619 
    620   // The run loop is about to go to sleep.  If any of the work done since it
    621   // started or woke up resulted in a nested run loop running,
    622   // nesting-deferred work may have accumulated.  Schedule it for processing
    623   // if appropriate.
    624   self->MaybeScheduleNestingDeferredWork();
    625 
    626   if (self->instrumentation_)
    627     self->instrumentation_->WaitingStarted();
    628 }
    629 
    630 // Called from the run loop.
    631 // static
    632 void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer,
    633                                                  CFRunLoopActivity activity,
    634                                                  void* info) {
    635   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    636 
    637   // The run loop has reached the top of the loop and is about to begin
    638   // processing sources.  If the last iteration of the loop at this nesting
    639   // level did not sleep or exit, nesting-deferred work may have accumulated
    640   // if a nested loop ran.  Schedule nesting-deferred work for processing if
    641   // appropriate.
    642   self->MaybeScheduleNestingDeferredWork();
    643 }
    644 
    645 // Called from the run loop.
    646 // static
    647 void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer,
    648                                                  CFRunLoopActivity activity,
    649                                                  void* info) {
    650   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
    651 
    652   switch (activity) {
    653     case kCFRunLoopEntry:
    654       if (self->instrumentation_)
    655         self->instrumentation_->LoopEntered();
    656 
    657       ++self->nesting_level_;
    658       if (self->nesting_level_ > self->deepest_nesting_level_) {
    659         self->deepest_nesting_level_ = self->nesting_level_;
    660       }
    661       break;
    662 
    663     case kCFRunLoopExit:
    664       // Not all run loops go to sleep.  If a run loop is stopped before it
    665       // goes to sleep due to a CFRunLoopStop call, or if the timeout passed
    666       // to CFRunLoopRunInMode expires, the run loop may proceed directly from
    667       // handling sources to exiting without any sleep.  This most commonly
    668       // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it
    669       // to make a single pass through the loop and exit without sleep.  Some
    670       // native loops use CFRunLoop in this way.  Because StartOrEndWaitObserver
    671       // will not be called in these case, MaybeScheduleNestingDeferredWork
    672       // needs to be called here, as the run loop exits.
    673       //
    674       // MaybeScheduleNestingDeferredWork consults self->nesting_level_
    675       // to determine whether to schedule nesting-deferred work.  It expects
    676       // the nesting level to be set to the depth of the loop that is going
    677       // to sleep or exiting.  It must be called before decrementing the
    678       // value so that the value still corresponds to the level of the exiting
    679       // loop.
    680       self->MaybeScheduleNestingDeferredWork();
    681       --self->nesting_level_;
    682 
    683       if (self->instrumentation_)
    684         self->instrumentation_->LoopExited();
    685       break;
    686 
    687     default:
    688       break;
    689   }
    690 
    691   self->EnterExitRunLoop(activity);
    692 }
    693 
    694 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop.  The default
    695 // implementation is a no-op.
    696 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
    697 }
    698 
    699 // Base version returns a standard NSAutoreleasePool.
    700 NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
    701   return [[NSAutoreleasePool alloc] init];
    702 }
    703 
    704 MessagePumpCFRunLoop::MessagePumpCFRunLoop()
    705     : quit_pending_(false) {
    706 }
    707 
    708 MessagePumpCFRunLoop::~MessagePumpCFRunLoop() {}
    709 
    710 // Called by MessagePumpCFRunLoopBase::DoRun.  If other CFRunLoopRun loops were
    711 // running lower on the run loop thread's stack when this object was created,
    712 // the same number of CFRunLoopRun loops must be running for the outermost call
    713 // to Run.  Run/DoRun are reentrant after that point.
    714 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) {
    715   // This is completely identical to calling CFRunLoopRun(), except autorelease
    716   // pool management is introduced.
    717   int result;
    718   do {
    719     MessagePumpScopedAutoreleasePool autorelease_pool(this);
    720     result = CFRunLoopRunInMode(kCFRunLoopDefaultMode,
    721                                 kCFTimeIntervalMax,
    722                                 false);
    723   } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished);
    724 }
    725 
    726 // Must be called on the run loop thread.
    727 void MessagePumpCFRunLoop::Quit() {
    728   // Stop the innermost run loop managed by this MessagePumpCFRunLoop object.
    729   if (nesting_level() == run_nesting_level()) {
    730     // This object is running the innermost loop, just stop it.
    731     CFRunLoopStop(run_loop());
    732   } else {
    733     // There's another loop running inside the loop managed by this object.
    734     // In other words, someone else called CFRunLoopRunInMode on the same
    735     // thread, deeper on the stack than the deepest Run call.  Don't preempt
    736     // other run loops, just mark this object to quit the innermost Run as
    737     // soon as the other inner loops not managed by Run are done.
    738     quit_pending_ = true;
    739   }
    740 }
    741 
    742 // Called by MessagePumpCFRunLoopBase::EnterExitObserver.
    743 void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) {
    744   if (activity == kCFRunLoopExit &&
    745       nesting_level() == run_nesting_level() &&
    746       quit_pending_) {
    747     // Quit was called while loops other than those managed by this object
    748     // were running further inside a run loop managed by this object.  Now
    749     // that all unmanaged inner run loops are gone, stop the loop running
    750     // just inside Run.
    751     CFRunLoopStop(run_loop());
    752     quit_pending_ = false;
    753   }
    754 }
    755 
    756 MessagePumpNSRunLoop::MessagePumpNSRunLoop()
    757     : keep_running_(true) {
    758   CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
    759   source_context.perform = NoOp;
    760   quit_source_ = CFRunLoopSourceCreate(NULL,  // allocator
    761                                        0,     // priority
    762                                        &source_context);
    763   CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
    764 }
    765 
    766 MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
    767   CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
    768   CFRelease(quit_source_);
    769 }
    770 
    771 void MessagePumpNSRunLoop::DoRun(Delegate* delegate) {
    772   while (keep_running_) {
    773     // NSRunLoop manages autorelease pools itself.
    774     [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
    775                              beforeDate:[NSDate distantFuture]];
    776   }
    777 
    778   keep_running_ = true;
    779 }
    780 
    781 void MessagePumpNSRunLoop::Quit() {
    782   keep_running_ = false;
    783   CFRunLoopSourceSignal(quit_source_);
    784   CFRunLoopWakeUp(run_loop());
    785 }
    786 
    787 #if defined(OS_IOS)
    788 MessagePumpUIApplication::MessagePumpUIApplication()
    789     : run_loop_(NULL) {
    790 }
    791 
    792 MessagePumpUIApplication::~MessagePumpUIApplication() {}
    793 
    794 void MessagePumpUIApplication::DoRun(Delegate* delegate) {
    795   NOTREACHED();
    796 }
    797 
    798 void MessagePumpUIApplication::Quit() {
    799   NOTREACHED();
    800 }
    801 
    802 void MessagePumpUIApplication::Attach(Delegate* delegate) {
    803   DCHECK(!run_loop_);
    804   run_loop_ = new RunLoop();
    805   CHECK(run_loop_->BeforeRun());
    806   SetDelegate(delegate);
    807 }
    808 
    809 #else
    810 
    811 MessagePumpNSApplication::MessagePumpNSApplication()
    812     : keep_running_(true),
    813       running_own_loop_(false) {
    814   EnableInstrumentation();
    815 }
    816 
    817 MessagePumpNSApplication::~MessagePumpNSApplication() {}
    818 
    819 void MessagePumpNSApplication::DoRun(Delegate* delegate) {
    820   if (instrumentation_)
    821     instrumentation_->StartIfNeeded();
    822 
    823   bool last_running_own_loop_ = running_own_loop_;
    824 
    825   // NSApp must be initialized by calling:
    826   // [{some class which implements CrAppProtocol} sharedApplication]
    827   // Most likely candidates are CrApplication or BrowserCrApplication.
    828   // These can be initialized from C++ code by calling
    829   // RegisterCrApp() or RegisterBrowserCrApp().
    830   CHECK(NSApp);
    831 
    832   if (![NSApp isRunning]) {
    833     running_own_loop_ = false;
    834     // NSApplication manages autorelease pools itself when run this way.
    835     [NSApp run];
    836   } else {
    837     running_own_loop_ = true;
    838     NSDate* distant_future = [NSDate distantFuture];
    839     while (keep_running_) {
    840       MessagePumpScopedAutoreleasePool autorelease_pool(this);
    841       NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
    842                                           untilDate:distant_future
    843                                              inMode:NSDefaultRunLoopMode
    844                                             dequeue:YES];
    845       if (event) {
    846         [NSApp sendEvent:event];
    847       }
    848     }
    849     keep_running_ = true;
    850   }
    851 
    852   running_own_loop_ = last_running_own_loop_;
    853 }
    854 
    855 void MessagePumpNSApplication::Quit() {
    856   if (!running_own_loop_) {
    857     [[NSApplication sharedApplication] stop:nil];
    858   } else {
    859     keep_running_ = false;
    860   }
    861 
    862   // Send a fake event to wake the loop up.
    863   [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
    864                                       location:NSZeroPoint
    865                                  modifierFlags:0
    866                                      timestamp:0
    867                                   windowNumber:0
    868                                        context:NULL
    869                                        subtype:0
    870                                          data1:0
    871                                          data2:0]
    872            atStart:NO];
    873 }
    874 
    875 MessagePumpCrApplication::MessagePumpCrApplication() {
    876 }
    877 
    878 MessagePumpCrApplication::~MessagePumpCrApplication() {
    879 }
    880 
    881 // Prevents an autorelease pool from being created if the app is in the midst of
    882 // handling a UI event because various parts of AppKit depend on objects that
    883 // are created while handling a UI event to be autoreleased in the event loop.
    884 // An example of this is NSWindowController. When a window with a window
    885 // controller is closed it goes through a stack like this:
    886 // (Several stack frames elided for clarity)
    887 //
    888 // #0 [NSWindowController autorelease]
    889 // #1 DoAClose
    890 // #2 MessagePumpCFRunLoopBase::DoWork()
    891 // #3 [NSRunLoop run]
    892 // #4 [NSButton performClick:]
    893 // #5 [NSWindow sendEvent:]
    894 // #6 [NSApp sendEvent:]
    895 // #7 [NSApp run]
    896 //
    897 // -performClick: spins a nested run loop. If the pool created in DoWork was a
    898 // standard NSAutoreleasePool, it would release the objects that were
    899 // autoreleased into it once DoWork released it. This would cause the window
    900 // controller, which autoreleased itself in frame #0, to release itself, and
    901 // possibly free itself. Unfortunately this window controller controls the
    902 // window in frame #5. When the stack is unwound to frame #5, the window would
    903 // no longer exists and crashes may occur. Apple gets around this by never
    904 // releasing the pool it creates in frame #4, and letting frame #7 clean it up
    905 // when it cleans up the pool that wraps frame #7. When an autorelease pool is
    906 // released it releases all other pools that were created after it on the
    907 // autorelease pool stack.
    908 //
    909 // CrApplication is responsible for setting handlingSendEvent to true just
    910 // before it sends the event through the event handling mechanism, and
    911 // returning it to its previous value once the event has been sent.
    912 NSAutoreleasePool* MessagePumpCrApplication::CreateAutoreleasePool() {
    913   if (MessagePumpMac::IsHandlingSendEvent())
    914     return nil;
    915   return MessagePumpNSApplication::CreateAutoreleasePool();
    916 }
    917 
    918 // static
    919 bool MessagePumpMac::UsingCrApp() {
    920   DCHECK([NSThread isMainThread]);
    921 
    922   // If NSApp is still not initialized, then the subclass used cannot
    923   // be determined.
    924   DCHECK(NSApp);
    925 
    926   // The pump was created using MessagePumpNSApplication.
    927   if (g_not_using_cr_app)
    928     return false;
    929 
    930   return [NSApp conformsToProtocol:@protocol(CrAppProtocol)];
    931 }
    932 
    933 // static
    934 bool MessagePumpMac::IsHandlingSendEvent() {
    935   DCHECK([NSApp conformsToProtocol:@protocol(CrAppProtocol)]);
    936   NSObject<CrAppProtocol>* app = static_cast<NSObject<CrAppProtocol>*>(NSApp);
    937   return [app isHandlingSendEvent];
    938 }
    939 #endif  // !defined(OS_IOS)
    940 
    941 // static
    942 MessagePump* MessagePumpMac::Create() {
    943   if ([NSThread isMainThread]) {
    944 #if defined(OS_IOS)
    945     return new MessagePumpUIApplication;
    946 #else
    947     if ([NSApp conformsToProtocol:@protocol(CrAppProtocol)])
    948       return new MessagePumpCrApplication;
    949 
    950     // The main-thread MessagePump implementations REQUIRE an NSApp.
    951     // Executables which have specific requirements for their
    952     // NSApplication subclass should initialize appropriately before
    953     // creating an event loop.
    954     [NSApplication sharedApplication];
    955     g_not_using_cr_app = true;
    956     return new MessagePumpNSApplication;
    957 #endif
    958   }
    959 
    960   return new MessagePumpNSRunLoop;
    961 }
    962 
    963 }  // namespace base
    964