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(CFRunLoopTimerRef timer, 286 void* info) { 287 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 288 289 // The timer won't fire again until it's reset. 290 self->delayed_work_fire_time_ = kCFTimeIntervalMax; 291 292 // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. 293 // In order to establish the proper priority in which work and delayed work 294 // are processed one for one, the timer used to schedule delayed work must 295 // signal a CFRunLoopSource used to dispatch both work and delayed work. 296 CFRunLoopSourceSignal(self->work_source_); 297 } 298 299 // Called from the run loop. 300 // static 301 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { 302 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 303 self->RunWork(); 304 } 305 306 // Called by MessagePumpCFRunLoopBase::RunWorkSource. 307 bool MessagePumpCFRunLoopBase::RunWork() { 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_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 // Call DoWork and DoDelayedWork once, and if something was done, arrange to 324 // come back here again as long as the loop is still running. 325 bool did_work = delegate_->DoWork(); 326 bool resignal_work_source = did_work; 327 328 TimeTicks next_time; 329 delegate_->DoDelayedWork(&next_time); 330 if (!did_work) { 331 // Determine whether there's more delayed work, and if so, if it needs to 332 // be done at some point in the future or if it's already time to do it. 333 // Only do these checks if did_work is false. If did_work is true, this 334 // function, and therefore any additional delayed work, will get another 335 // chance to run before the loop goes to sleep. 336 bool more_delayed_work = !next_time.is_null(); 337 if (more_delayed_work) { 338 TimeDelta delay = next_time - TimeTicks::Now(); 339 if (delay > TimeDelta()) { 340 // There's more delayed work to be done in the future. 341 ScheduleDelayedWork(next_time); 342 } else { 343 // There's more delayed work to be done, and its time is in the past. 344 // Arrange to come back here directly as long as the loop is still 345 // running. 346 resignal_work_source = true; 347 } 348 } 349 } 350 351 if (resignal_work_source) { 352 CFRunLoopSourceSignal(work_source_); 353 } 354 355 return resignal_work_source; 356 } 357 358 // Called from the run loop. 359 // static 360 void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) { 361 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 362 self->RunIdleWork(); 363 } 364 365 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. 366 bool MessagePumpCFRunLoopBase::RunIdleWork() { 367 if (!delegate_) { 368 // This point can be reached with a NULL delegate_ if Run is not on the 369 // stack but foreign code is spinning the CFRunLoop. Arrange to come back 370 // here when a delegate is available. 371 delegateless_idle_work_ = true; 372 return false; 373 } 374 375 // The NSApplication-based run loop only drains the autorelease pool at each 376 // UI event (NSEvent). The autorelease pool is not drained for each 377 // CFRunLoopSource target that's run. Use a local pool for any autoreleased 378 // objects if the app is not currently handling a UI event to ensure they're 379 // released promptly even in the absence of UI events. 380 MessagePumpScopedAutoreleasePool autorelease_pool(this); 381 382 // Call DoIdleWork once, and if something was done, arrange to come back here 383 // again as long as the loop is still running. 384 bool did_work = delegate_->DoIdleWork(); 385 if (did_work) { 386 CFRunLoopSourceSignal(idle_work_source_); 387 } 388 389 return did_work; 390 } 391 392 // Called from the run loop. 393 // static 394 void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) { 395 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 396 self->RunNestingDeferredWork(); 397 } 398 399 // Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource. 400 bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() { 401 if (!delegate_) { 402 // This point can be reached with a NULL delegate_ if Run is not on the 403 // stack but foreign code is spinning the CFRunLoop. There's no sense in 404 // attempting to do any work or signalling the work sources because 405 // without a delegate, work is not possible. 406 return false; 407 } 408 409 // Immediately try work in priority order. 410 if (!RunWork()) { 411 if (!RunIdleWork()) { 412 return false; 413 } 414 } else { 415 // Work was done. Arrange for the loop to try non-nestable idle work on 416 // a subsequent pass. 417 CFRunLoopSourceSignal(idle_work_source_); 418 } 419 420 return true; 421 } 422 423 // Called before the run loop goes to sleep or exits, or processes sources. 424 void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() { 425 // deepest_nesting_level_ is set as run loops are entered. If the deepest 426 // level encountered is deeper than the current level, a nested loop 427 // (relative to the current level) ran since the last time nesting-deferred 428 // work was scheduled. When that situation is encountered, schedule 429 // nesting-deferred work in case any work was deferred because nested work 430 // was disallowed. 431 if (deepest_nesting_level_ > nesting_level_) { 432 deepest_nesting_level_ = nesting_level_; 433 CFRunLoopSourceSignal(nesting_deferred_work_source_); 434 } 435 } 436 437 // Called from the run loop. 438 // static 439 void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer, 440 CFRunLoopActivity activity, 441 void* info) { 442 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 443 444 // Attempt to do some idle work before going to sleep. 445 self->RunIdleWork(); 446 447 // The run loop is about to go to sleep. If any of the work done since it 448 // started or woke up resulted in a nested run loop running, 449 // nesting-deferred work may have accumulated. Schedule it for processing 450 // if appropriate. 451 self->MaybeScheduleNestingDeferredWork(); 452 } 453 454 // Called from the run loop. 455 // static 456 void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer, 457 CFRunLoopActivity activity, 458 void* info) { 459 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 460 461 // The run loop has reached the top of the loop and is about to begin 462 // processing sources. If the last iteration of the loop at this nesting 463 // level did not sleep or exit, nesting-deferred work may have accumulated 464 // if a nested loop ran. Schedule nesting-deferred work for processing if 465 // appropriate. 466 self->MaybeScheduleNestingDeferredWork(); 467 } 468 469 // Called from the run loop. 470 // static 471 void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer, 472 CFRunLoopActivity activity, 473 void* info) { 474 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 475 476 switch (activity) { 477 case kCFRunLoopEntry: 478 ++self->nesting_level_; 479 if (self->nesting_level_ > self->deepest_nesting_level_) { 480 self->deepest_nesting_level_ = self->nesting_level_; 481 } 482 break; 483 484 case kCFRunLoopExit: 485 // Not all run loops go to sleep. If a run loop is stopped before it 486 // goes to sleep due to a CFRunLoopStop call, or if the timeout passed 487 // to CFRunLoopRunInMode expires, the run loop may proceed directly from 488 // handling sources to exiting without any sleep. This most commonly 489 // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it 490 // to make a single pass through the loop and exit without sleep. Some 491 // native loops use CFRunLoop in this way. Because PreWaitObserver will 492 // not be called in these case, MaybeScheduleNestingDeferredWork needs 493 // to be called here, as the run loop exits. 494 // 495 // MaybeScheduleNestingDeferredWork consults self->nesting_level_ 496 // to determine whether to schedule nesting-deferred work. It expects 497 // the nesting level to be set to the depth of the loop that is going 498 // to sleep or exiting. It must be called before decrementing the 499 // value so that the value still corresponds to the level of the exiting 500 // loop. 501 self->MaybeScheduleNestingDeferredWork(); 502 --self->nesting_level_; 503 break; 504 505 default: 506 break; 507 } 508 509 self->EnterExitRunLoop(activity); 510 } 511 512 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default 513 // implementation is a no-op. 514 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { 515 } 516 517 // Base version returns a standard NSAutoreleasePool. 518 AutoreleasePoolType* MessagePumpCFRunLoopBase::CreateAutoreleasePool() { 519 return [[NSAutoreleasePool alloc] init]; 520 } 521 522 MessagePumpCFRunLoop::MessagePumpCFRunLoop() 523 : quit_pending_(false) { 524 } 525 526 MessagePumpCFRunLoop::~MessagePumpCFRunLoop() {} 527 528 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were 529 // running lower on the run loop thread's stack when this object was created, 530 // the same number of CFRunLoopRun loops must be running for the outermost call 531 // to Run. Run/DoRun are reentrant after that point. 532 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { 533 // This is completely identical to calling CFRunLoopRun(), except autorelease 534 // pool management is introduced. 535 int result; 536 do { 537 MessagePumpScopedAutoreleasePool autorelease_pool(this); 538 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 539 kCFTimeIntervalMax, 540 false); 541 } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); 542 } 543 544 // Must be called on the run loop thread. 545 void MessagePumpCFRunLoop::Quit() { 546 // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. 547 if (nesting_level() == run_nesting_level()) { 548 // This object is running the innermost loop, just stop it. 549 CFRunLoopStop(run_loop()); 550 } else { 551 // There's another loop running inside the loop managed by this object. 552 // In other words, someone else called CFRunLoopRunInMode on the same 553 // thread, deeper on the stack than the deepest Run call. Don't preempt 554 // other run loops, just mark this object to quit the innermost Run as 555 // soon as the other inner loops not managed by Run are done. 556 quit_pending_ = true; 557 } 558 } 559 560 // Called by MessagePumpCFRunLoopBase::EnterExitObserver. 561 void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) { 562 if (activity == kCFRunLoopExit && 563 nesting_level() == run_nesting_level() && 564 quit_pending_) { 565 // Quit was called while loops other than those managed by this object 566 // were running further inside a run loop managed by this object. Now 567 // that all unmanaged inner run loops are gone, stop the loop running 568 // just inside Run. 569 CFRunLoopStop(run_loop()); 570 quit_pending_ = false; 571 } 572 } 573 574 MessagePumpNSRunLoop::MessagePumpNSRunLoop() 575 : keep_running_(true) { 576 CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); 577 source_context.perform = NoOp; 578 quit_source_ = CFRunLoopSourceCreate(NULL, // allocator 579 0, // priority 580 &source_context); 581 CFRunLoopAddSourceToAllModes(run_loop(), quit_source_); 582 } 583 584 MessagePumpNSRunLoop::~MessagePumpNSRunLoop() { 585 CFRunLoopRemoveSourceFromAllModes(run_loop(), quit_source_); 586 CFRelease(quit_source_); 587 } 588 589 void MessagePumpNSRunLoop::DoRun(Delegate* delegate) { 590 while (keep_running_) { 591 // NSRunLoop manages autorelease pools itself. 592 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 593 beforeDate:[NSDate distantFuture]]; 594 } 595 596 keep_running_ = true; 597 } 598 599 void MessagePumpNSRunLoop::Quit() { 600 keep_running_ = false; 601 CFRunLoopSourceSignal(quit_source_); 602 CFRunLoopWakeUp(run_loop()); 603 } 604 605 #if defined(OS_IOS) 606 MessagePumpUIApplication::MessagePumpUIApplication() 607 : run_loop_(NULL) { 608 } 609 610 MessagePumpUIApplication::~MessagePumpUIApplication() {} 611 612 void MessagePumpUIApplication::DoRun(Delegate* delegate) { 613 NOTREACHED(); 614 } 615 616 void MessagePumpUIApplication::Quit() { 617 NOTREACHED(); 618 } 619 620 void MessagePumpUIApplication::Attach(Delegate* delegate) { 621 DCHECK(!run_loop_); 622 run_loop_ = new RunLoop(); 623 CHECK(run_loop_->BeforeRun()); 624 SetDelegate(delegate); 625 } 626 627 #else 628 629 MessagePumpNSApplication::MessagePumpNSApplication() 630 : keep_running_(true), 631 running_own_loop_(false) { 632 } 633 634 MessagePumpNSApplication::~MessagePumpNSApplication() {} 635 636 void MessagePumpNSApplication::DoRun(Delegate* delegate) { 637 bool last_running_own_loop_ = running_own_loop_; 638 639 // NSApp must be initialized by calling: 640 // [{some class which implements CrAppProtocol} sharedApplication] 641 // Most likely candidates are CrApplication or BrowserCrApplication. 642 // These can be initialized from C++ code by calling 643 // RegisterCrApp() or RegisterBrowserCrApp(). 644 CHECK(NSApp); 645 646 if (![NSApp isRunning]) { 647 running_own_loop_ = false; 648 // NSApplication manages autorelease pools itself when run this way. 649 [NSApp run]; 650 } else { 651 running_own_loop_ = true; 652 NSDate* distant_future = [NSDate distantFuture]; 653 while (keep_running_) { 654 MessagePumpScopedAutoreleasePool autorelease_pool(this); 655 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask 656 untilDate:distant_future 657 inMode:NSDefaultRunLoopMode 658 dequeue:YES]; 659 if (event) { 660 [NSApp sendEvent:event]; 661 } 662 } 663 keep_running_ = true; 664 } 665 666 running_own_loop_ = last_running_own_loop_; 667 } 668 669 void MessagePumpNSApplication::Quit() { 670 if (!running_own_loop_) { 671 [[NSApplication sharedApplication] stop:nil]; 672 } else { 673 keep_running_ = false; 674 } 675 676 // Send a fake event to wake the loop up. 677 [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined 678 location:NSZeroPoint 679 modifierFlags:0 680 timestamp:0 681 windowNumber:0 682 context:NULL 683 subtype:0 684 data1:0 685 data2:0] 686 atStart:NO]; 687 } 688 689 MessagePumpCrApplication::MessagePumpCrApplication() { 690 } 691 692 MessagePumpCrApplication::~MessagePumpCrApplication() { 693 } 694 695 // Prevents an autorelease pool from being created if the app is in the midst of 696 // handling a UI event because various parts of AppKit depend on objects that 697 // are created while handling a UI event to be autoreleased in the event loop. 698 // An example of this is NSWindowController. When a window with a window 699 // controller is closed it goes through a stack like this: 700 // (Several stack frames elided for clarity) 701 // 702 // #0 [NSWindowController autorelease] 703 // #1 DoAClose 704 // #2 MessagePumpCFRunLoopBase::DoWork() 705 // #3 [NSRunLoop run] 706 // #4 [NSButton performClick:] 707 // #5 [NSWindow sendEvent:] 708 // #6 [NSApp sendEvent:] 709 // #7 [NSApp run] 710 // 711 // -performClick: spins a nested run loop. If the pool created in DoWork was a 712 // standard NSAutoreleasePool, it would release the objects that were 713 // autoreleased into it once DoWork released it. This would cause the window 714 // controller, which autoreleased itself in frame #0, to release itself, and 715 // possibly free itself. Unfortunately this window controller controls the 716 // window in frame #5. When the stack is unwound to frame #5, the window would 717 // no longer exists and crashes may occur. Apple gets around this by never 718 // releasing the pool it creates in frame #4, and letting frame #7 clean it up 719 // when it cleans up the pool that wraps frame #7. When an autorelease pool is 720 // released it releases all other pools that were created after it on the 721 // autorelease pool stack. 722 // 723 // CrApplication is responsible for setting handlingSendEvent to true just 724 // before it sends the event through the event handling mechanism, and 725 // returning it to its previous value once the event has been sent. 726 AutoreleasePoolType* MessagePumpCrApplication::CreateAutoreleasePool() { 727 if (MessagePumpMac::IsHandlingSendEvent()) 728 return nil; 729 return MessagePumpNSApplication::CreateAutoreleasePool(); 730 } 731 732 // static 733 bool MessagePumpMac::UsingCrApp() { 734 DCHECK([NSThread isMainThread]); 735 736 // If NSApp is still not initialized, then the subclass used cannot 737 // be determined. 738 DCHECK(NSApp); 739 740 // The pump was created using MessagePumpNSApplication. 741 if (g_not_using_cr_app) 742 return false; 743 744 return [NSApp conformsToProtocol:@protocol(CrAppProtocol)]; 745 } 746 747 // static 748 bool MessagePumpMac::IsHandlingSendEvent() { 749 DCHECK([NSApp conformsToProtocol:@protocol(CrAppProtocol)]); 750 NSObject<CrAppProtocol>* app = static_cast<NSObject<CrAppProtocol>*>(NSApp); 751 return [app isHandlingSendEvent]; 752 } 753 #endif // !defined(OS_IOS) 754 755 // static 756 MessagePump* MessagePumpMac::Create() { 757 if ([NSThread isMainThread]) { 758 #if defined(OS_IOS) 759 return new MessagePumpUIApplication; 760 #else 761 if ([NSApp conformsToProtocol:@protocol(CrAppProtocol)]) 762 return new MessagePumpCrApplication; 763 764 // The main-thread MessagePump implementations REQUIRE an NSApp. 765 // Executables which have specific requirements for their 766 // NSApplication subclass should initialize appropriately before 767 // creating an event loop. 768 [NSApplication sharedApplication]; 769 g_not_using_cr_app = true; 770 return new MessagePumpNSApplication; 771 #endif 772 } 773 774 return new MessagePumpNSRunLoop; 775 } 776 777 } // namespace base 778