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