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