ipc/chromium/src/base/message_pump_mac.mm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/ipc/chromium/src/base/message_pump_mac.mm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,752 @@
     1.4 +// Copyright (c) 2008 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +#include "base/message_pump_mac.h"
     1.9 +
    1.10 +#import <AppKit/AppKit.h>
    1.11 +#import <Foundation/Foundation.h>
    1.12 +#include <IOKit/IOMessage.h>
    1.13 +#include <IOKit/pwr_mgt/IOPMLib.h>
    1.14 +
    1.15 +#include <limits>
    1.16 +
    1.17 +#import "base/chrome_application_mac.h"
    1.18 +#include "base/logging.h"
    1.19 +#include "base/time.h"
    1.20 +
    1.21 +namespace {
    1.22 +
    1.23 +void NoOp(void* info) {
    1.24 +}
    1.25 +
    1.26 +const CFTimeInterval kCFTimeIntervalMax =
    1.27 +    std::numeric_limits<CFTimeInterval>::max();
    1.28 +
    1.29 +}  // namespace
    1.30 +
    1.31 +namespace base {
    1.32 +
    1.33 +// A scoper for autorelease pools created from message pump run loops.
    1.34 +// Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare
    1.35 +// case where an autorelease pool needs to be passed in.
    1.36 +class MessagePumpScopedAutoreleasePool {
    1.37 + public:
    1.38 +  explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) :
    1.39 +      pool_(pump->CreateAutoreleasePool()) {
    1.40 +  }
    1.41 +   ~MessagePumpScopedAutoreleasePool() {
    1.42 +    [pool_ drain];
    1.43 +  }
    1.44 +
    1.45 + private:
    1.46 +  NSAutoreleasePool* pool_;
    1.47 +  DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool);
    1.48 +};
    1.49 +
    1.50 +// Must be called on the run loop thread.
    1.51 +MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
    1.52 +    : delegate_(NULL),
    1.53 +      delayed_work_fire_time_(kCFTimeIntervalMax),
    1.54 +      nesting_level_(0),
    1.55 +      run_nesting_level_(0),
    1.56 +      deepest_nesting_level_(0),
    1.57 +      delegateless_work_(false),
    1.58 +      delegateless_delayed_work_(false),
    1.59 +      delegateless_idle_work_(false) {
    1.60 +  run_loop_ = CFRunLoopGetCurrent();
    1.61 +  CFRetain(run_loop_);
    1.62 +
    1.63 +  // Set a repeating timer with a preposterous firing time and interval.  The
    1.64 +  // timer will effectively never fire as-is.  The firing time will be adjusted
    1.65 +  // as needed when ScheduleDelayedWork is called.
    1.66 +  CFRunLoopTimerContext timer_context = CFRunLoopTimerContext();
    1.67 +  timer_context.info = this;
    1.68 +  delayed_work_timer_ = CFRunLoopTimerCreate(NULL,                // allocator
    1.69 +                                             kCFTimeIntervalMax,  // fire time
    1.70 +                                             kCFTimeIntervalMax,  // interval
    1.71 +                                             0,                   // flags
    1.72 +                                             0,                   // priority
    1.73 +                                             RunDelayedWorkTimer,
    1.74 +                                             &timer_context);
    1.75 +  CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes);
    1.76 +
    1.77 +  CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
    1.78 +  source_context.info = this;
    1.79 +  source_context.perform = RunWorkSource;
    1.80 +  work_source_ = CFRunLoopSourceCreate(NULL,  // allocator
    1.81 +                                       1,     // priority
    1.82 +                                       &source_context);
    1.83 +  CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes);
    1.84 +
    1.85 +  source_context.perform = RunDelayedWorkSource;
    1.86 +  delayed_work_source_ = CFRunLoopSourceCreate(NULL,  // allocator
    1.87 +                                               2,     // priority
    1.88 +                                               &source_context);
    1.89 +  CFRunLoopAddSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes);
    1.90 +
    1.91 +  source_context.perform = RunIdleWorkSource;
    1.92 +  idle_work_source_ = CFRunLoopSourceCreate(NULL,  // allocator
    1.93 +                                            3,     // priority
    1.94 +                                            &source_context);
    1.95 +  CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes);
    1.96 +
    1.97 +  source_context.perform = RunNestingDeferredWorkSource;
    1.98 +  nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL,  // allocator
    1.99 +                                                        0,     // priority
   1.100 +                                                        &source_context);
   1.101 +  CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_,
   1.102 +                     kCFRunLoopCommonModes);
   1.103 +
   1.104 +  CFRunLoopObserverContext observer_context = CFRunLoopObserverContext();
   1.105 +  observer_context.info = this;
   1.106 +  pre_wait_observer_ = CFRunLoopObserverCreate(NULL,  // allocator
   1.107 +                                               kCFRunLoopBeforeWaiting,
   1.108 +                                               true,  // repeat
   1.109 +                                               0,     // priority
   1.110 +                                               PreWaitObserver,
   1.111 +                                               &observer_context);
   1.112 +  CFRunLoopAddObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes);
   1.113 +
   1.114 +  pre_source_observer_ = CFRunLoopObserverCreate(NULL,  // allocator
   1.115 +                                                 kCFRunLoopBeforeSources,
   1.116 +                                                 true,  // repeat
   1.117 +                                                 0,     // priority
   1.118 +                                                 PreSourceObserver,
   1.119 +                                                 &observer_context);
   1.120 +  CFRunLoopAddObserver(run_loop_, pre_source_observer_, kCFRunLoopCommonModes);
   1.121 +
   1.122 +  enter_exit_observer_ = CFRunLoopObserverCreate(NULL,  // allocator
   1.123 +                                                 kCFRunLoopEntry |
   1.124 +                                                     kCFRunLoopExit,
   1.125 +                                                 true,  // repeat
   1.126 +                                                 0,     // priority
   1.127 +                                                 EnterExitObserver,
   1.128 +                                                 &observer_context);
   1.129 +  CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes);
   1.130 +
   1.131 +  root_power_domain_ = IORegisterForSystemPower(this,
   1.132 +                                                &power_notification_port_,
   1.133 +                                                PowerStateNotification,
   1.134 +                                                &power_notification_object_);
   1.135 +  if (root_power_domain_ != MACH_PORT_NULL) {
   1.136 +    CFRunLoopAddSource(
   1.137 +        run_loop_,
   1.138 +        IONotificationPortGetRunLoopSource(power_notification_port_),
   1.139 +        kCFRunLoopCommonModes);
   1.140 +  }
   1.141 +}
   1.142 +
   1.143 +// Ideally called on the run loop thread.  If other run loops were running
   1.144 +// lower on the run loop thread's stack when this object was created, the
   1.145 +// same number of run loops must be running when this object is destroyed.
   1.146 +MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() {
   1.147 +  if (root_power_domain_ != MACH_PORT_NULL) {
   1.148 +    CFRunLoopRemoveSource(
   1.149 +        run_loop_,
   1.150 +        IONotificationPortGetRunLoopSource(power_notification_port_),
   1.151 +        kCFRunLoopCommonModes);
   1.152 +    IODeregisterForSystemPower(&power_notification_object_);
   1.153 +    IOServiceClose(root_power_domain_);
   1.154 +    IONotificationPortDestroy(power_notification_port_);
   1.155 +  }
   1.156 +
   1.157 +  CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_,
   1.158 +                          kCFRunLoopCommonModes);
   1.159 +  CFRelease(enter_exit_observer_);
   1.160 +
   1.161 +  CFRunLoopRemoveObserver(run_loop_, pre_source_observer_,
   1.162 +                          kCFRunLoopCommonModes);
   1.163 +  CFRelease(pre_source_observer_);
   1.164 +
   1.165 +  CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_,
   1.166 +                          kCFRunLoopCommonModes);
   1.167 +  CFRelease(pre_wait_observer_);
   1.168 +
   1.169 +  CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_,
   1.170 +                        kCFRunLoopCommonModes);
   1.171 +  CFRelease(nesting_deferred_work_source_);
   1.172 +
   1.173 +  CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes);
   1.174 +  CFRelease(idle_work_source_);
   1.175 +
   1.176 +  CFRunLoopRemoveSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes);
   1.177 +  CFRelease(delayed_work_source_);
   1.178 +
   1.179 +  CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes);
   1.180 +  CFRelease(work_source_);
   1.181 +
   1.182 +  CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes);
   1.183 +  CFRelease(delayed_work_timer_);
   1.184 +
   1.185 +  CFRelease(run_loop_);
   1.186 +}
   1.187 +
   1.188 +// Must be called on the run loop thread.
   1.189 +void MessagePumpCFRunLoopBase::Run(Delegate* delegate) {
   1.190 +  // nesting_level_ will be incremented in EnterExitRunLoop, so set
   1.191 +  // run_nesting_level_ accordingly.
   1.192 +  int last_run_nesting_level = run_nesting_level_;
   1.193 +  run_nesting_level_ = nesting_level_ + 1;
   1.194 +
   1.195 +  Delegate* last_delegate = delegate_;
   1.196 +  delegate_ = delegate;
   1.197 +
   1.198 +  if (delegate) {
   1.199 +    // If any work showed up but could not be dispatched for want of a
   1.200 +    // delegate, set it up for dispatch again now that a delegate is
   1.201 +    // available.
   1.202 +    if (delegateless_work_) {
   1.203 +      CFRunLoopSourceSignal(work_source_);
   1.204 +      delegateless_work_ = false;
   1.205 +    }
   1.206 +    if (delegateless_delayed_work_) {
   1.207 +      CFRunLoopSourceSignal(delayed_work_source_);
   1.208 +      delegateless_delayed_work_ = false;
   1.209 +    }
   1.210 +    if (delegateless_idle_work_) {
   1.211 +      CFRunLoopSourceSignal(idle_work_source_);
   1.212 +      delegateless_idle_work_ = false;
   1.213 +    }
   1.214 +  }
   1.215 +
   1.216 +  DoRun(delegate);
   1.217 +
   1.218 +  // Restore the previous state of the object.
   1.219 +  delegate_ = last_delegate;
   1.220 +  run_nesting_level_ = last_run_nesting_level;
   1.221 +}
   1.222 +
   1.223 +// May be called on any thread.
   1.224 +void MessagePumpCFRunLoopBase::ScheduleWork() {
   1.225 +  CFRunLoopSourceSignal(work_source_);
   1.226 +  CFRunLoopWakeUp(run_loop_);
   1.227 +}
   1.228 +
   1.229 +// Must be called on the run loop thread.
   1.230 +void MessagePumpCFRunLoopBase::ScheduleDelayedWork(
   1.231 +    const TimeTicks& delayed_work_time) {
   1.232 +  TimeDelta delta = delayed_work_time - TimeTicks::Now();
   1.233 +  delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF();
   1.234 +  CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_);
   1.235 +}
   1.236 +
   1.237 +// Called from the run loop.
   1.238 +// static
   1.239 +void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer,
   1.240 +                                                   void* info) {
   1.241 +  MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   1.242 +
   1.243 +  // The timer won't fire again until it's reset.
   1.244 +  self->delayed_work_fire_time_ = kCFTimeIntervalMax;
   1.245 +
   1.246 +  // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources.
   1.247 +  // In order to establish the proper priority where delegate_->DoDelayedWork
   1.248 +  // can only be called if delegate_->DoWork returns false, the timer used
   1.249 +  // to schedule delayed work must signal a CFRunLoopSource set at a lower
   1.250 +  // priority than the one used for delegate_->DoWork.
   1.251 +  CFRunLoopSourceSignal(self->delayed_work_source_);
   1.252 +}
   1.253 +
   1.254 +// Called from the run loop.
   1.255 +// static
   1.256 +void MessagePumpCFRunLoopBase::RunWorkSource(void* info) {
   1.257 +  MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   1.258 +  self->RunWork();
   1.259 +}
   1.260 +
   1.261 +// Called by MessagePumpCFRunLoopBase::RunWorkSource.
   1.262 +bool MessagePumpCFRunLoopBase::RunWork() {
   1.263 +  if (!delegate_) {
   1.264 +    // This point can be reached with a NULL delegate_ if Run is not on the
   1.265 +    // stack but foreign code is spinning the CFRunLoop.  Arrange to come back
   1.266 +    // here when a delegate is available.
   1.267 +    delegateless_work_ = true;
   1.268 +    return false;
   1.269 +  }
   1.270 +
   1.271 +  // The NSApplication-based run loop only drains the autorelease pool at each
   1.272 +  // UI event (NSEvent).  The autorelease pool is not drained for each
   1.273 +  // CFRunLoopSource target that's run.  Use a local pool for any autoreleased
   1.274 +  // objects if the app is not currently handling a UI event to ensure they're
   1.275 +  // released promptly even in the absence of UI events.
   1.276 +  MessagePumpScopedAutoreleasePool autorelease_pool(this);
   1.277 +
   1.278 +  // Call DoWork once, and if something was done, arrange to come back here
   1.279 +  // again as long as the loop is still running.
   1.280 +  bool did_work = delegate_->DoWork();
   1.281 +  if (did_work) {
   1.282 +    CFRunLoopSourceSignal(work_source_);
   1.283 +  }
   1.284 +
   1.285 +  return did_work;
   1.286 +}
   1.287 +
   1.288 +// Called from the run loop.
   1.289 +// static
   1.290 +void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) {
   1.291 +  MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   1.292 +  self->RunDelayedWork();
   1.293 +}
   1.294 +
   1.295 +// Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource.
   1.296 +bool MessagePumpCFRunLoopBase::RunDelayedWork() {
   1.297 +  if (!delegate_) {
   1.298 +    // This point can be reached with a NULL delegate_ if Run is not on the
   1.299 +    // stack but foreign code is spinning the CFRunLoop.  Arrange to come back
   1.300 +    // here when a delegate is available.
   1.301 +    delegateless_delayed_work_ = true;
   1.302 +    return false;
   1.303 +  }
   1.304 +
   1.305 +  // The NSApplication-based run loop only drains the autorelease pool at each
   1.306 +  // UI event (NSEvent).  The autorelease pool is not drained for each
   1.307 +  // CFRunLoopSource target that's run.  Use a local pool for any autoreleased
   1.308 +  // objects if the app is not currently handling a UI event to ensure they're
   1.309 +  // released promptly even in the absence of UI events.
   1.310 +  MessagePumpScopedAutoreleasePool autorelease_pool(this);
   1.311 +
   1.312 +  TimeTicks next_time;
   1.313 +  delegate_->DoDelayedWork(&next_time);
   1.314 +
   1.315 +  bool more_work = !next_time.is_null();
   1.316 +  if (more_work) {
   1.317 +    TimeDelta delay = next_time - TimeTicks::Now();
   1.318 +    if (delay > TimeDelta()) {
   1.319 +      // There's more delayed work to be done in the future.
   1.320 +      ScheduleDelayedWork(next_time);
   1.321 +    } else {
   1.322 +      // There's more delayed work to be done, and its time is in the past.
   1.323 +      // Arrange to come back here directly as long as the loop is still
   1.324 +      // running.
   1.325 +      CFRunLoopSourceSignal(delayed_work_source_);
   1.326 +    }
   1.327 +  }
   1.328 +
   1.329 +  return more_work;
   1.330 +}
   1.331 +
   1.332 +// Called from the run loop.
   1.333 +// static
   1.334 +void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) {
   1.335 +  MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   1.336 +  self->RunIdleWork();
   1.337 +}
   1.338 +
   1.339 +// Called by MessagePumpCFRunLoopBase::RunIdleWorkSource.
   1.340 +bool MessagePumpCFRunLoopBase::RunIdleWork() {
   1.341 +  if (!delegate_) {
   1.342 +    // This point can be reached with a NULL delegate_ if Run is not on the
   1.343 +    // stack but foreign code is spinning the CFRunLoop.  Arrange to come back
   1.344 +    // here when a delegate is available.
   1.345 +    delegateless_idle_work_ = true;
   1.346 +    return false;
   1.347 +  }
   1.348 +
   1.349 +  // The NSApplication-based run loop only drains the autorelease pool at each
   1.350 +  // UI event (NSEvent).  The autorelease pool is not drained for each
   1.351 +  // CFRunLoopSource target that's run.  Use a local pool for any autoreleased
   1.352 +  // objects if the app is not currently handling a UI event to ensure they're
   1.353 +  // released promptly even in the absence of UI events.
   1.354 +  MessagePumpScopedAutoreleasePool autorelease_pool(this);
   1.355 +
   1.356 +  // Call DoIdleWork once, and if something was done, arrange to come back here
   1.357 +  // again as long as the loop is still running.
   1.358 +  bool did_work = delegate_->DoIdleWork();
   1.359 +  if (did_work) {
   1.360 +    CFRunLoopSourceSignal(idle_work_source_);
   1.361 +  }
   1.362 +
   1.363 +  return did_work;
   1.364 +}
   1.365 +
   1.366 +// Called from the run loop.
   1.367 +// static
   1.368 +void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) {
   1.369 +  MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   1.370 +  self->RunNestingDeferredWork();
   1.371 +}
   1.372 +
   1.373 +// Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource.
   1.374 +bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() {
   1.375 +  if (!delegate_) {
   1.376 +    // This point can be reached with a NULL delegate_ if Run is not on the
   1.377 +    // stack but foreign code is spinning the CFRunLoop.  There's no sense in
   1.378 +    // attempting to do any work or signalling the work sources because
   1.379 +    // without a delegate, work is not possible.
   1.380 +    return false;
   1.381 +  }
   1.382 +
   1.383 +  // Immediately try work in priority order.
   1.384 +  if (!RunWork()) {
   1.385 +    if (!RunDelayedWork()) {
   1.386 +      if (!RunIdleWork()) {
   1.387 +        return false;
   1.388 +      }
   1.389 +    } else {
   1.390 +      // There was no work, and delayed work was done.  Arrange for the loop
   1.391 +      // to try non-nestable idle work on a subsequent pass.
   1.392 +      CFRunLoopSourceSignal(idle_work_source_);
   1.393 +    }
   1.394 +  } else {
   1.395 +    // Work was done.  Arrange for the loop to try non-nestable delayed and
   1.396 +    // idle work on a subsequent pass.
   1.397 +    CFRunLoopSourceSignal(delayed_work_source_);
   1.398 +    CFRunLoopSourceSignal(idle_work_source_);
   1.399 +  }
   1.400 +
   1.401 +  return true;
   1.402 +}
   1.403 +
   1.404 +// Called before the run loop goes to sleep or exits, or processes sources.
   1.405 +void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() {
   1.406 +  // deepest_nesting_level_ is set as run loops are entered.  If the deepest
   1.407 +  // level encountered is deeper than the current level, a nested loop
   1.408 +  // (relative to the current level) ran since the last time nesting-deferred
   1.409 +  // work was scheduled.  When that situation is encountered, schedule
   1.410 +  // nesting-deferred work in case any work was deferred because nested work
   1.411 +  // was disallowed.
   1.412 +  if (deepest_nesting_level_ > nesting_level_) {
   1.413 +    deepest_nesting_level_ = nesting_level_;
   1.414 +    CFRunLoopSourceSignal(nesting_deferred_work_source_);
   1.415 +  }
   1.416 +}
   1.417 +
   1.418 +// Called from the run loop.
   1.419 +// static
   1.420 +void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer,
   1.421 +                                               CFRunLoopActivity activity,
   1.422 +                                               void* info) {
   1.423 +  MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   1.424 +
   1.425 +  // Attempt to do some idle work before going to sleep.
   1.426 +  self->RunIdleWork();
   1.427 +
   1.428 +  // The run loop is about to go to sleep.  If any of the work done since it
   1.429 +  // started or woke up resulted in a nested run loop running,
   1.430 +  // nesting-deferred work may have accumulated.  Schedule it for processing
   1.431 +  // if appropriate.
   1.432 +  self->MaybeScheduleNestingDeferredWork();
   1.433 +}
   1.434 +
   1.435 +// Called from the run loop.
   1.436 +// static
   1.437 +void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer,
   1.438 +                                                 CFRunLoopActivity activity,
   1.439 +                                                 void* info) {
   1.440 +  MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   1.441 +
   1.442 +  // The run loop has reached the top of the loop and is about to begin
   1.443 +  // processing sources.  If the last iteration of the loop at this nesting
   1.444 +  // level did not sleep or exit, nesting-deferred work may have accumulated
   1.445 +  // if a nested loop ran.  Schedule nesting-deferred work for processing if
   1.446 +  // appropriate.
   1.447 +  self->MaybeScheduleNestingDeferredWork();
   1.448 +}
   1.449 +
   1.450 +// Called from the run loop.
   1.451 +// static
   1.452 +void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer,
   1.453 +                                                 CFRunLoopActivity activity,
   1.454 +                                                 void* info) {
   1.455 +  MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   1.456 +
   1.457 +  switch (activity) {
   1.458 +    case kCFRunLoopEntry:
   1.459 +      ++self->nesting_level_;
   1.460 +      if (self->nesting_level_ > self->deepest_nesting_level_) {
   1.461 +        self->deepest_nesting_level_ = self->nesting_level_;
   1.462 +      }
   1.463 +      break;
   1.464 +
   1.465 +    case kCFRunLoopExit:
   1.466 +      // Not all run loops go to sleep.  If a run loop is stopped before it
   1.467 +      // goes to sleep due to a CFRunLoopStop call, or if the timeout passed
   1.468 +      // to CFRunLoopRunInMode expires, the run loop may proceed directly from
   1.469 +      // handling sources to exiting without any sleep.  This most commonly
   1.470 +      // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it
   1.471 +      // to make a single pass through the loop and exit without sleep.  Some
   1.472 +      // native loops use CFRunLoop in this way.  Because PreWaitObserver will
   1.473 +      // not be called in these case, MaybeScheduleNestingDeferredWork needs
   1.474 +      // to be called here, as the run loop exits.
   1.475 +      //
   1.476 +      // MaybeScheduleNestingDeferredWork consults self->nesting_level_
   1.477 +      // to determine whether to schedule nesting-deferred work.  It expects
   1.478 +      // the nesting level to be set to the depth of the loop that is going
   1.479 +      // to sleep or exiting.  It must be called before decrementing the
   1.480 +      // value so that the value still corresponds to the level of the exiting
   1.481 +      // loop.
   1.482 +      self->MaybeScheduleNestingDeferredWork();
   1.483 +      --self->nesting_level_;
   1.484 +      break;
   1.485 +
   1.486 +    default:
   1.487 +      break;
   1.488 +  }
   1.489 +
   1.490 +  self->EnterExitRunLoop(activity);
   1.491 +}
   1.492 +
   1.493 +// Called from the run loop.
   1.494 +// static
   1.495 +void MessagePumpCFRunLoopBase::PowerStateNotification(void* info,
   1.496 +                                                      io_service_t service,
   1.497 +                                                      uint32_t message_type,
   1.498 +                                                      void* message_argument) {
   1.499 +  // CFRunLoopTimer (NSTimer) is scheduled in terms of CFAbsoluteTime, which
   1.500 +  // measures the number of seconds since 2001-01-01 00:00:00.0 Z.  It is
   1.501 +  // implemented in terms of kernel ticks, as in mach_absolute_time.  While an
   1.502 +  // offset and scale factor can be applied to convert between the two time
   1.503 +  // bases at any time after boot, the kernel clock stops while the system is
   1.504 +  // asleep, altering the offset.  (The offset will also change when the
   1.505 +  // real-time clock is adjusted.)  CFRunLoopTimers are not readjusted to take
   1.506 +  // this into account when the system wakes up, so any timers that were
   1.507 +  // pending while the system was asleep will be delayed by the sleep
   1.508 +  // duration.
   1.509 +  //
   1.510 +  // The MessagePump interface assumes that scheduled delayed work will be
   1.511 +  // performed at the time ScheduleDelayedWork was asked to perform it.  The
   1.512 +  // delay caused by the CFRunLoopTimer not firing at the appropriate time
   1.513 +  // results in a stall of queued delayed work when the system wakes up.
   1.514 +  // With this limitation, scheduled work would not be performed until
   1.515 +  // (system wake time + scheduled work time - system sleep time), while it
   1.516 +  // would be expected to be performed at (scheduled work time).
   1.517 +  //
   1.518 +  // To work around this problem, when the system wakes up from sleep, if a
   1.519 +  // delayed work timer is pending, it is rescheduled to fire at the original
   1.520 +  // time that it was scheduled to fire.
   1.521 +  //
   1.522 +  // This mechanism is not resilient if the real-time clock does not maintain
   1.523 +  // stable time while the system is sleeping, but it matches the behavior of
   1.524 +  // the various other MessagePump implementations, and MessageLoop seems to
   1.525 +  // be limited in the same way.
   1.526 +  //
   1.527 +  // References
   1.528 +  //  - Chris Kane, "NSTimer and deep sleep," cocoa-dev@lists.apple.com,
   1.529 +  //    http://lists.apple.com/archives/Cocoa-dev/2002/May/msg01547.html
   1.530 +  //  - Apple Technical Q&A QA1340, "Registering and unregistering for sleep
   1.531 +  //    and wake notifications,"
   1.532 +  //    http://developer.apple.com/mac/library/qa/qa2004/qa1340.html
   1.533 +  //  - Core Foundation source code, CF-550/CFRunLoop.c and CF-550/CFDate.c,
   1.534 +  //    http://www.opensource.apple.com/
   1.535 +
   1.536 +  MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
   1.537 +
   1.538 +  switch (message_type) {
   1.539 +    case kIOMessageSystemWillPowerOn:
   1.540 +      if (self->delayed_work_fire_time_ != kCFTimeIntervalMax) {
   1.541 +        CFRunLoopTimerSetNextFireDate(self->delayed_work_timer_,
   1.542 +                                      self->delayed_work_fire_time_);
   1.543 +      }
   1.544 +      break;
   1.545 +
   1.546 +    case kIOMessageSystemWillSleep:
   1.547 +    case kIOMessageCanSystemSleep:
   1.548 +      // The system will wait for 30 seconds before entering sleep if neither
   1.549 +      // IOAllowPowerChange nor IOCancelPowerChange are called.  That would be
   1.550 +      // pretty antisocial.
   1.551 +      IOAllowPowerChange(self->root_power_domain_,
   1.552 +                         reinterpret_cast<long>(message_argument));
   1.553 +      break;
   1.554 +
   1.555 +    default:
   1.556 +      break;
   1.557 +  }
   1.558 +}
   1.559 +
   1.560 +// Called by MessagePumpCFRunLoopBase::EnterExitRunLoop.  The default
   1.561 +// implementation is a no-op.
   1.562 +void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
   1.563 +}
   1.564 +
   1.565 +// Base version returns a standard NSAutoreleasePool.
   1.566 +NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
   1.567 +  return [[NSAutoreleasePool alloc] init];
   1.568 +}
   1.569 +
   1.570 +MessagePumpCFRunLoop::MessagePumpCFRunLoop()
   1.571 +    : quit_pending_(false) {
   1.572 +}
   1.573 +
   1.574 +// Called by MessagePumpCFRunLoopBase::DoRun.  If other CFRunLoopRun loops were
   1.575 +// running lower on the run loop thread's stack when this object was created,
   1.576 +// the same number of CFRunLoopRun loops must be running for the outermost call
   1.577 +// to Run.  Run/DoRun are reentrant after that point.
   1.578 +void MessagePumpCFRunLoop::DoRun(Delegate* delegate) {
   1.579 +  // This is completely identical to calling CFRunLoopRun(), except autorelease
   1.580 +  // pool management is introduced.
   1.581 +  int result;
   1.582 +  do {
   1.583 +    MessagePumpScopedAutoreleasePool autorelease_pool(this);
   1.584 +    result = CFRunLoopRunInMode(kCFRunLoopDefaultMode,
   1.585 +                                kCFTimeIntervalMax,
   1.586 +                                false);
   1.587 +  } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished);
   1.588 +}
   1.589 +
   1.590 +// Must be called on the run loop thread.
   1.591 +void MessagePumpCFRunLoop::Quit() {
   1.592 +  // Stop the innermost run loop managed by this MessagePumpCFRunLoop object.
   1.593 +  if (nesting_level() == run_nesting_level()) {
   1.594 +    // This object is running the innermost loop, just stop it.
   1.595 +    CFRunLoopStop(run_loop());
   1.596 +  } else {
   1.597 +    // There's another loop running inside the loop managed by this object.
   1.598 +    // In other words, someone else called CFRunLoopRunInMode on the same
   1.599 +    // thread, deeper on the stack than the deepest Run call.  Don't preempt
   1.600 +    // other run loops, just mark this object to quit the innermost Run as
   1.601 +    // soon as the other inner loops not managed by Run are done.
   1.602 +    quit_pending_ = true;
   1.603 +  }
   1.604 +}
   1.605 +
   1.606 +// Called by MessagePumpCFRunLoopBase::EnterExitObserver.
   1.607 +void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) {
   1.608 +  if (activity == kCFRunLoopExit &&
   1.609 +      nesting_level() == run_nesting_level() &&
   1.610 +      quit_pending_) {
   1.611 +    // Quit was called while loops other than those managed by this object
   1.612 +    // were running further inside a run loop managed by this object.  Now
   1.613 +    // that all unmanaged inner run loops are gone, stop the loop running
   1.614 +    // just inside Run.
   1.615 +    CFRunLoopStop(run_loop());
   1.616 +    quit_pending_ = false;
   1.617 +  }
   1.618 +}
   1.619 +
   1.620 +MessagePumpNSRunLoop::MessagePumpNSRunLoop()
   1.621 +    : keep_running_(true) {
   1.622 +  CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
   1.623 +  source_context.perform = NoOp;
   1.624 +  quit_source_ = CFRunLoopSourceCreate(NULL,  // allocator
   1.625 +                                       0,     // priority
   1.626 +                                       &source_context);
   1.627 +  CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
   1.628 +}
   1.629 +
   1.630 +MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
   1.631 +  CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
   1.632 +  CFRelease(quit_source_);
   1.633 +}
   1.634 +
   1.635 +void MessagePumpNSRunLoop::DoRun(Delegate* delegate) {
   1.636 +  while (keep_running_) {
   1.637 +    // NSRunLoop manages autorelease pools itself.
   1.638 +    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
   1.639 +                             beforeDate:[NSDate distantFuture]];
   1.640 +  }
   1.641 +
   1.642 +  keep_running_ = true;
   1.643 +}
   1.644 +
   1.645 +void MessagePumpNSRunLoop::Quit() {
   1.646 +  keep_running_ = false;
   1.647 +  CFRunLoopSourceSignal(quit_source_);
   1.648 +  CFRunLoopWakeUp(run_loop());
   1.649 +}
   1.650 +
   1.651 +MessagePumpNSApplication::MessagePumpNSApplication()
   1.652 +    : keep_running_(true),
   1.653 +      running_own_loop_(false) {
   1.654 +}
   1.655 +
   1.656 +void MessagePumpNSApplication::DoRun(Delegate* delegate) {
   1.657 +  bool last_running_own_loop_ = running_own_loop_;
   1.658 +
   1.659 +  // TODO(dmaclach): Get rid of this gratuitous sharedApplication.
   1.660 +  // Tests should be setting up their applications on their own.
   1.661 +  [CrApplication sharedApplication];
   1.662 +
   1.663 +  if (![NSApp isRunning]) {
   1.664 +    running_own_loop_ = false;
   1.665 +    // NSApplication manages autorelease pools itself when run this way.
   1.666 +    [NSApp run];
   1.667 +  } else {
   1.668 +    running_own_loop_ = true;
   1.669 +    NSDate* distant_future = [NSDate distantFuture];
   1.670 +    while (keep_running_) {
   1.671 +      MessagePumpScopedAutoreleasePool autorelease_pool(this);
   1.672 +      NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
   1.673 +                                          untilDate:distant_future
   1.674 +                                             inMode:NSDefaultRunLoopMode
   1.675 +                                            dequeue:YES];
   1.676 +      if (event) {
   1.677 +        [NSApp sendEvent:event];
   1.678 +      }
   1.679 +    }
   1.680 +    keep_running_ = true;
   1.681 +  }
   1.682 +
   1.683 +  running_own_loop_ = last_running_own_loop_;
   1.684 +}
   1.685 +
   1.686 +void MessagePumpNSApplication::Quit() {
   1.687 +  if (!running_own_loop_) {
   1.688 +    [NSApp stop:nil];
   1.689 +  } else {
   1.690 +    keep_running_ = false;
   1.691 +  }
   1.692 +
   1.693 +  // Send a fake event to wake the loop up.
   1.694 +  [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
   1.695 +                                      location:NSMakePoint(0, 0)
   1.696 +                                 modifierFlags:0
   1.697 +                                     timestamp:0
   1.698 +                                  windowNumber:0
   1.699 +                                       context:NULL
   1.700 +                                       subtype:0
   1.701 +                                         data1:0
   1.702 +                                         data2:0]
   1.703 +           atStart:NO];
   1.704 +}
   1.705 +
   1.706 +// Prevents an autorelease pool from being created if the app is in the midst of
   1.707 +// handling a UI event because various parts of AppKit depend on objects that
   1.708 +// are created while handling a UI event to be autoreleased in the event loop.
   1.709 +// An example of this is NSWindowController. When a window with a window
   1.710 +// controller is closed it goes through a stack like this:
   1.711 +// (Several stack frames elided for clarity)
   1.712 +//
   1.713 +// #0 [NSWindowController autorelease]
   1.714 +// #1 DoAClose
   1.715 +// #2 MessagePumpCFRunLoopBase::DoWork()
   1.716 +// #3 [NSRunLoop run]
   1.717 +// #4 [NSButton performClick:]
   1.718 +// #5 [NSWindow sendEvent:]
   1.719 +// #6 [NSApp sendEvent:]
   1.720 +// #7 [NSApp run]
   1.721 +//
   1.722 +// -performClick: spins a nested run loop. If the pool created in DoWork was a
   1.723 +// standard NSAutoreleasePool, it would release the objects that were
   1.724 +// autoreleased into it once DoWork released it. This would cause the window
   1.725 +// controller, which autoreleased itself in frame #0, to release itself, and
   1.726 +// possibly free itself. Unfortunately this window controller controls the
   1.727 +// window in frame #5. When the stack is unwound to frame #5, the window would
   1.728 +// no longer exists and crashes may occur. Apple gets around this by never
   1.729 +// releasing the pool it creates in frame #4, and letting frame #7 clean it up
   1.730 +// when it cleans up the pool that wraps frame #7. When an autorelease pool is
   1.731 +// released it releases all other pools that were created after it on the
   1.732 +// autorelease pool stack.
   1.733 +//
   1.734 +// CrApplication is responsible for setting handlingSendEvent to true just
   1.735 +// before it sends the event throught the event handling mechanism, and
   1.736 +// returning it to its previous value once the event has been sent.
   1.737 +NSAutoreleasePool* MessagePumpNSApplication::CreateAutoreleasePool() {
   1.738 +  NSAutoreleasePool* pool = nil;
   1.739 +  DCHECK([NSApp isKindOfClass:[CrApplication class]]);
   1.740 +  if (![static_cast<CrApplication*>(NSApp) isHandlingSendEvent]) {
   1.741 +    pool = MessagePumpCFRunLoopBase::CreateAutoreleasePool();
   1.742 +  }
   1.743 +  return pool;
   1.744 +}
   1.745 +
   1.746 +// static
   1.747 +MessagePump* MessagePumpMac::Create() {
   1.748 +  if ([NSThread isMainThread]) {
   1.749 +    return new MessagePumpNSApplication;
   1.750 +  }
   1.751 +
   1.752 +  return new MessagePumpNSRunLoop;
   1.753 +}
   1.754 +
   1.755 +}  // namespace base

mercurial