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.

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

mercurial