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