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 22namespace base { 23 24namespace { 25 26void CFRunLoopAddSourceToAllModes(CFRunLoopRef rl, CFRunLoopSourceRef source) { 27 CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes); 28 CFRunLoopAddSource(rl, source, kMessageLoopExclusiveRunLoopMode); 29} 30 31void CFRunLoopRemoveSourceFromAllModes(CFRunLoopRef rl, 32 CFRunLoopSourceRef source) { 33 CFRunLoopRemoveSource(rl, source, kCFRunLoopCommonModes); 34 CFRunLoopRemoveSource(rl, source, kMessageLoopExclusiveRunLoopMode); 35} 36 37void CFRunLoopAddTimerToAllModes(CFRunLoopRef rl, CFRunLoopTimerRef timer) { 38 CFRunLoopAddTimer(rl, timer, kCFRunLoopCommonModes); 39 CFRunLoopAddTimer(rl, timer, kMessageLoopExclusiveRunLoopMode); 40} 41 42void CFRunLoopRemoveTimerFromAllModes(CFRunLoopRef rl, 43 CFRunLoopTimerRef timer) { 44 CFRunLoopRemoveTimer(rl, timer, kCFRunLoopCommonModes); 45 CFRunLoopRemoveTimer(rl, timer, kMessageLoopExclusiveRunLoopMode); 46} 47 48void CFRunLoopAddObserverToAllModes(CFRunLoopRef rl, 49 CFRunLoopObserverRef observer) { 50 CFRunLoopAddObserver(rl, observer, kCFRunLoopCommonModes); 51 CFRunLoopAddObserver(rl, observer, kMessageLoopExclusiveRunLoopMode); 52} 53 54void CFRunLoopRemoveObserverFromAllModes(CFRunLoopRef rl, 55 CFRunLoopObserverRef observer) { 56 CFRunLoopRemoveObserver(rl, observer, kCFRunLoopCommonModes); 57 CFRunLoopRemoveObserver(rl, observer, kMessageLoopExclusiveRunLoopMode); 58} 59 60void NoOp(void* /* info */) { 61} 62 63const 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. 69bool g_not_using_cr_app = false; 70#endif 71 72// Call through to CFRunLoopTimerSetTolerance(), which is only available on 73// OS X 10.9. 74void 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 102const 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. 108class 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. 123MessagePumpCFRunLoopBase::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. 200MessagePumpCFRunLoopBase::~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. 226void 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 242void 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. 261void MessagePumpCFRunLoopBase::ScheduleWork() { 262 CFRunLoopSourceSignal(work_source_); 263 CFRunLoopWakeUp(run_loop_); 264} 265 266// Must be called on the run loop thread. 267void 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 279void MessagePumpCFRunLoopBase::SetTimerSlack(TimerSlack timer_slack) { 280 timer_slack_ = timer_slack; 281} 282 283// Called from the run loop. 284// static 285void 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 302void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { 303 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 304 self->RunWork(); 305} 306 307// Called by MessagePumpCFRunLoopBase::RunWorkSource. 308bool 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 361void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) { 362 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 363 self->RunIdleWork(); 364} 365 366// Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. 367bool 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 395void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) { 396 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 397 self->RunNestingDeferredWork(); 398} 399 400// Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource. 401bool 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. 425void 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 440void 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 458void 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 474void 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. 518void MessagePumpCFRunLoopBase::EnterExitRunLoop( 519 CFRunLoopActivity /* activity */) { 520} 521 522// Base version returns a standard NSAutoreleasePool. 523AutoreleasePoolType* MessagePumpCFRunLoopBase::CreateAutoreleasePool() { 524 return [[NSAutoreleasePool alloc] init]; 525} 526 527MessagePumpCFRunLoop::MessagePumpCFRunLoop() 528 : quit_pending_(false) { 529} 530 531MessagePumpCFRunLoop::~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. 537void 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. 550void 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. 566void 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 579MessagePumpNSRunLoop::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 589MessagePumpNSRunLoop::~MessagePumpNSRunLoop() { 590 CFRunLoopRemoveSourceFromAllModes(run_loop(), quit_source_); 591 CFRelease(quit_source_); 592} 593 594void 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 604void MessagePumpNSRunLoop::Quit() { 605 keep_running_ = false; 606 CFRunLoopSourceSignal(quit_source_); 607 CFRunLoopWakeUp(run_loop()); 608} 609 610#if defined(OS_IOS) 611MessagePumpUIApplication::MessagePumpUIApplication() 612 : run_loop_(NULL) { 613} 614 615MessagePumpUIApplication::~MessagePumpUIApplication() {} 616 617void MessagePumpUIApplication::DoRun(Delegate* delegate) { 618 NOTREACHED(); 619} 620 621void MessagePumpUIApplication::Quit() { 622 NOTREACHED(); 623} 624 625void 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 634MessagePumpNSApplication::MessagePumpNSApplication() 635 : keep_running_(true), 636 running_own_loop_(false) { 637} 638 639MessagePumpNSApplication::~MessagePumpNSApplication() {} 640 641void 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 674void 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 694MessagePumpCrApplication::MessagePumpCrApplication() { 695} 696 697MessagePumpCrApplication::~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. 731AutoreleasePoolType* MessagePumpCrApplication::CreateAutoreleasePool() { 732 if (MessagePumpMac::IsHandlingSendEvent()) 733 return nil; 734 return MessagePumpNSApplication::CreateAutoreleasePool(); 735} 736 737// static 738bool 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 753bool 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 761MessagePump* 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