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