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 #import <Foundation/Foundation.h> 8 9 #include <limits> 10 #include <stack> 11 12 #include "base/format_macros.h" 13 #include "base/logging.h" 14 #include "base/mac/scoped_cftyperef.h" 15 #include "base/metrics/histogram.h" 16 #include "base/run_loop.h" 17 #include "base/strings/stringprintf.h" 18 #include "base/time/time.h" 19 20 #if !defined(OS_IOS) 21 #import <AppKit/AppKit.h> 22 #endif // !defined(OS_IOS) 23 24 namespace { 25 26 void NoOp(void* info) { 27 } 28 29 const CFTimeInterval kCFTimeIntervalMax = 30 std::numeric_limits<CFTimeInterval>::max(); 31 32 #if !defined(OS_IOS) 33 // Set to true if MessagePumpMac::Create() is called before NSApp is 34 // initialized. Only accessed from the main thread. 35 bool g_not_using_cr_app = false; 36 #endif 37 38 } // namespace 39 40 namespace base { 41 42 // A scoper for autorelease pools created from message pump run loops. 43 // Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare 44 // case where an autorelease pool needs to be passed in. 45 class MessagePumpScopedAutoreleasePool { 46 public: 47 explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) : 48 pool_(pump->CreateAutoreleasePool()) { 49 } 50 ~MessagePumpScopedAutoreleasePool() { 51 [pool_ drain]; 52 } 53 54 private: 55 NSAutoreleasePool* pool_; 56 DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool); 57 }; 58 59 // This class is used to instrument the MessagePump to gather various timing 60 // data about when the underlying run loop is entered, when it is waiting, and 61 // when it is servicing its delegate. 62 // 63 // The metrics are gathered as UMA-tracked histograms. To gather the data over 64 // time, sampling is used, such that a new histogram is created for each metric 65 // every |sampling_interval| for |sampling_duration|. After sampling is 66 // complete, this class deletes itself. 67 class MessagePumpInstrumentation { 68 public: 69 // Creates an instrument for the MessagePump on the current thread. Every 70 // |sampling_interval|, a new histogram will be created to track the metrics 71 // over time. After |sampling_duration|, this will delete itself, causing the 72 // WeakPtr to go NULL. 73 static WeakPtr<MessagePumpInstrumentation> Create( 74 const TimeDelta& sampling_interval, 75 const TimeDelta& sampling_duration) { 76 MessagePumpInstrumentation* instrument = 77 new MessagePumpInstrumentation(sampling_interval, sampling_duration); 78 return instrument->weak_ptr_factory_.GetWeakPtr(); 79 } 80 81 // Starts the timer that runs the sampling instrumentation. Can be called 82 // multiple times as a noop. 83 void StartIfNeeded() { 84 if (timer_) 85 return; 86 87 sampling_start_time_ = generation_start_time_ = TimeTicks::Now(); 88 89 CFRunLoopTimerContext timer_context = { .info = this }; 90 timer_.reset(CFRunLoopTimerCreate( 91 NULL, // allocator 92 (Time::Now() + sampling_interval_).ToCFAbsoluteTime(), 93 sampling_interval_.InSecondsF(), 94 0, // flags 95 0, // order 96 &MessagePumpInstrumentation::TimerFired, 97 &timer_context)); 98 CFRunLoopAddTimer(CFRunLoopGetCurrent(), 99 timer_, 100 kCFRunLoopCommonModes); 101 } 102 103 // Used to track kCFRunLoopEntry. 104 void LoopEntered() { 105 loop_run_times_.push(TimeTicks::Now()); 106 } 107 108 // Used to track kCFRunLoopExit. 109 void LoopExited() { 110 TimeDelta duration = TimeTicks::Now() - loop_run_times_.top(); 111 loop_run_times_.pop(); 112 GetHistogram(LOOP_CYCLE)->AddTime(duration); 113 } 114 115 // Used to track kCFRunLoopBeforeWaiting. 116 void WaitingStarted() { 117 loop_wait_times_.push(TimeTicks::Now()); 118 } 119 120 // Used to track kCFRunLoopAfterWaiting. 121 void WaitingFinished() { 122 TimeDelta duration = TimeTicks::Now() - loop_wait_times_.top(); 123 loop_wait_times_.pop(); 124 GetHistogram(LOOP_WAIT)->AddTime(duration); 125 } 126 127 // Used to track when the MessagePump will invoke its |delegate|. 128 void WorkSourceEntered(MessagePump::Delegate* delegate) { 129 work_source_times_.push(TimeTicks::Now()); 130 if (delegate) { 131 size_t queue_size; 132 TimeDelta queuing_delay; 133 delegate->GetQueueingInformation(&queue_size, &queuing_delay); 134 GetHistogram(QUEUE_SIZE)->Add(queue_size); 135 GetHistogram(QUEUE_DELAY)->AddTime(queuing_delay); 136 } 137 } 138 139 // Used to track the completion of servicing the MessagePump::Delegate. 140 void WorkSourceExited() { 141 TimeDelta duration = TimeTicks::Now() - work_source_times_.top(); 142 work_source_times_.pop(); 143 GetHistogram(WORK_SOURCE)->AddTime(duration); 144 } 145 146 private: 147 enum HistogramEvent { 148 // Time-based histograms: 149 LOOP_CYCLE, // LoopEntered/LoopExited 150 LOOP_WAIT, // WaitingStarted/WaitingEnded 151 WORK_SOURCE, // WorkSourceExited 152 QUEUE_DELAY, // WorkSourceEntered 153 154 // Value-based histograms: 155 // NOTE: Do not add value-based histograms before this event, only after. 156 QUEUE_SIZE, // WorkSourceEntered 157 158 HISTOGRAM_EVENT_MAX, 159 }; 160 161 MessagePumpInstrumentation(const TimeDelta& sampling_interval, 162 const TimeDelta& sampling_duration) 163 : weak_ptr_factory_(this), 164 sampling_interval_(sampling_interval), 165 sampling_duration_(sampling_duration), 166 sample_generation_(0) { 167 // Create all the histogram objects that will be used for sampling. 168 const char kHistogramName[] = "MessagePumpMac.%s.SampleMs.%" PRId64; 169 for (TimeDelta i; i < sampling_duration_; i += sampling_interval_) { 170 int64 sample = i.InMilliseconds(); 171 172 // Generate the time-based histograms. 173 for (int j = LOOP_CYCLE; j < QUEUE_SIZE; ++j) { 174 std::string name = StringPrintf(kHistogramName, 175 NameForEnum(static_cast<HistogramEvent>(j)), sample); 176 histograms_[j].push_back( 177 Histogram::FactoryTimeGet(name, TimeDelta::FromMilliseconds(1), 178 sampling_interval_, 50, 179 HistogramBase::kUmaTargetedHistogramFlag)); 180 } 181 182 // Generate the value-based histograms. 183 for (int j = QUEUE_SIZE; j < HISTOGRAM_EVENT_MAX; ++j) { 184 std::string name = StringPrintf(kHistogramName, 185 NameForEnum(static_cast<HistogramEvent>(j)), sample); 186 histograms_[j].push_back( 187 Histogram::FactoryGet(name, 1, 10000, 50, 188 HistogramBase::kUmaTargetedHistogramFlag)); 189 } 190 } 191 } 192 193 ~MessagePumpInstrumentation() { 194 if (timer_) 195 CFRunLoopTimerInvalidate(timer_); 196 } 197 198 const char* NameForEnum(HistogramEvent event) { 199 switch (event) { 200 case LOOP_CYCLE: return "LoopCycle"; 201 case LOOP_WAIT: return "Waiting"; 202 case WORK_SOURCE: return "WorkSource"; 203 case QUEUE_DELAY: return "QueueingDelay"; 204 case QUEUE_SIZE: return "QueueSize"; 205 default: 206 NOTREACHED(); 207 return NULL; 208 } 209 } 210 211 static void TimerFired(CFRunLoopTimerRef timer, void* context) { 212 static_cast<MessagePumpInstrumentation*>(context)->TimerFired(); 213 } 214 215 // Called by the run loop when the sampling_interval_ has elapsed. Advances 216 // the sample_generation_, which controls into which histogram data is 217 // recorded, while recording and accounting for timer skew. Will delete this 218 // object after |sampling_duration_| has elapsed. 219 void TimerFired() { 220 TimeTicks now = TimeTicks::Now(); 221 TimeDelta delta = now - generation_start_time_; 222 223 // The timer fired, so advance the generation by at least one. 224 ++sample_generation_; 225 226 // To account for large timer skew/drift, advance the generation by any 227 // more completed intervals. 228 for (TimeDelta skew_advance = delta - sampling_interval_; 229 skew_advance >= sampling_interval_; 230 skew_advance -= sampling_interval_) { 231 ++sample_generation_; 232 } 233 234 generation_start_time_ = now; 235 if (now >= sampling_start_time_ + sampling_duration_) 236 delete this; 237 } 238 239 HistogramBase* GetHistogram(HistogramEvent event) { 240 DCHECK_LT(sample_generation_, histograms_[event].size()); 241 return histograms_[event][sample_generation_]; 242 } 243 244 // Vends the pointer to the Create()or. 245 WeakPtrFactory<MessagePumpInstrumentation> weak_ptr_factory_; 246 247 // The interval and duration of the sampling. 248 TimeDelta sampling_interval_; 249 TimeDelta sampling_duration_; 250 251 // The time at which sampling started. 252 TimeTicks sampling_start_time_; 253 254 // The timer that advances the sample_generation_ and sets the 255 // generation_start_time_ for the current sample interval. 256 base::ScopedCFTypeRef<CFRunLoopTimerRef> timer_; 257 258 // The two-dimensional array of histograms. The first dimension is the 259 // HistogramEvent type. The second is for the sampling intervals. 260 std::vector<HistogramBase*> histograms_[HISTOGRAM_EVENT_MAX]; 261 262 // The index in the second dimension of histograms_, which controls in which 263 // sampled histogram events are recorded. 264 size_t sample_generation_; 265 266 // The last time at which the timer fired. This is used to track timer skew 267 // (i.e. it did not fire on time) and properly account for it when advancing 268 // samle_generation_. 269 TimeTicks generation_start_time_; 270 271 // MessagePump activations can be nested. Use a stack for each of the 272 // possibly reentrant HistogramEvent types to properly balance and calculate 273 // the timing information. 274 std::stack<TimeTicks> loop_run_times_; 275 std::stack<TimeTicks> loop_wait_times_; 276 std::stack<TimeTicks> work_source_times_; 277 278 DISALLOW_COPY_AND_ASSIGN(MessagePumpInstrumentation); 279 }; 280 281 // Must be called on the run loop thread. 282 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() 283 : delegate_(NULL), 284 delayed_work_fire_time_(kCFTimeIntervalMax), 285 nesting_level_(0), 286 run_nesting_level_(0), 287 deepest_nesting_level_(0), 288 delegateless_work_(false), 289 delegateless_idle_work_(false) { 290 run_loop_ = CFRunLoopGetCurrent(); 291 CFRetain(run_loop_); 292 293 // Set a repeating timer with a preposterous firing time and interval. The 294 // timer will effectively never fire as-is. The firing time will be adjusted 295 // as needed when ScheduleDelayedWork is called. 296 CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); 297 timer_context.info = this; 298 delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator 299 kCFTimeIntervalMax, // fire time 300 kCFTimeIntervalMax, // interval 301 0, // flags 302 0, // priority 303 RunDelayedWorkTimer, 304 &timer_context); 305 CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); 306 307 CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); 308 source_context.info = this; 309 source_context.perform = RunWorkSource; 310 work_source_ = CFRunLoopSourceCreate(NULL, // allocator 311 1, // priority 312 &source_context); 313 CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes); 314 315 source_context.perform = RunIdleWorkSource; 316 idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator 317 2, // priority 318 &source_context); 319 CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); 320 321 source_context.perform = RunNestingDeferredWorkSource; 322 nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator 323 0, // priority 324 &source_context); 325 CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_, 326 kCFRunLoopCommonModes); 327 328 CFRunLoopObserverContext observer_context = CFRunLoopObserverContext(); 329 observer_context.info = this; 330 pre_wait_observer_ = CFRunLoopObserverCreate(NULL, // allocator 331 kCFRunLoopBeforeWaiting | 332 kCFRunLoopAfterWaiting, 333 true, // repeat 334 0, // priority 335 StartOrEndWaitObserver, 336 &observer_context); 337 CFRunLoopAddObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes); 338 339 pre_source_observer_ = CFRunLoopObserverCreate(NULL, // allocator 340 kCFRunLoopBeforeSources, 341 true, // repeat 342 0, // priority 343 PreSourceObserver, 344 &observer_context); 345 CFRunLoopAddObserver(run_loop_, pre_source_observer_, kCFRunLoopCommonModes); 346 347 enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator 348 kCFRunLoopEntry | 349 kCFRunLoopExit, 350 true, // repeat 351 0, // priority 352 EnterExitObserver, 353 &observer_context); 354 CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes); 355 } 356 357 // Ideally called on the run loop thread. If other run loops were running 358 // lower on the run loop thread's stack when this object was created, the 359 // same number of run loops must be running when this object is destroyed. 360 MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() { 361 CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_, 362 kCFRunLoopCommonModes); 363 CFRelease(enter_exit_observer_); 364 365 CFRunLoopRemoveObserver(run_loop_, pre_source_observer_, 366 kCFRunLoopCommonModes); 367 CFRelease(pre_source_observer_); 368 369 CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_, 370 kCFRunLoopCommonModes); 371 CFRelease(pre_wait_observer_); 372 373 CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_, 374 kCFRunLoopCommonModes); 375 CFRelease(nesting_deferred_work_source_); 376 377 CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); 378 CFRelease(idle_work_source_); 379 380 CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes); 381 CFRelease(work_source_); 382 383 CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); 384 CFRelease(delayed_work_timer_); 385 386 CFRelease(run_loop_); 387 } 388 389 // Must be called on the run loop thread. 390 void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { 391 // nesting_level_ will be incremented in EnterExitRunLoop, so set 392 // run_nesting_level_ accordingly. 393 int last_run_nesting_level = run_nesting_level_; 394 run_nesting_level_ = nesting_level_ + 1; 395 396 Delegate* last_delegate = delegate_; 397 SetDelegate(delegate); 398 399 DoRun(delegate); 400 401 // Restore the previous state of the object. 402 SetDelegate(last_delegate); 403 run_nesting_level_ = last_run_nesting_level; 404 } 405 406 void MessagePumpCFRunLoopBase::SetDelegate(Delegate* delegate) { 407 delegate_ = delegate; 408 409 if (delegate) { 410 // If any work showed up but could not be dispatched for want of a 411 // delegate, set it up for dispatch again now that a delegate is 412 // available. 413 if (delegateless_work_) { 414 CFRunLoopSourceSignal(work_source_); 415 delegateless_work_ = false; 416 } 417 if (delegateless_idle_work_) { 418 CFRunLoopSourceSignal(idle_work_source_); 419 delegateless_idle_work_ = false; 420 } 421 } 422 } 423 424 void MessagePumpCFRunLoopBase::EnableInstrumentation() { 425 instrumentation_ = MessagePumpInstrumentation::Create( 426 TimeDelta::FromSeconds(1), TimeDelta::FromSeconds(15)); 427 } 428 429 // May be called on any thread. 430 void MessagePumpCFRunLoopBase::ScheduleWork() { 431 CFRunLoopSourceSignal(work_source_); 432 CFRunLoopWakeUp(run_loop_); 433 } 434 435 // Must be called on the run loop thread. 436 void MessagePumpCFRunLoopBase::ScheduleDelayedWork( 437 const TimeTicks& delayed_work_time) { 438 TimeDelta delta = delayed_work_time - TimeTicks::Now(); 439 delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF(); 440 CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_); 441 } 442 443 // Called from the run loop. 444 // static 445 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, 446 void* info) { 447 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 448 449 // The timer won't fire again until it's reset. 450 self->delayed_work_fire_time_ = kCFTimeIntervalMax; 451 452 // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. 453 // In order to establish the proper priority in which work and delayed work 454 // are processed one for one, the timer used to schedule delayed work must 455 // signal a CFRunLoopSource used to dispatch both work and delayed work. 456 CFRunLoopSourceSignal(self->work_source_); 457 } 458 459 // Called from the run loop. 460 // static 461 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { 462 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 463 self->RunWork(); 464 } 465 466 // Called by MessagePumpCFRunLoopBase::RunWorkSource. 467 bool MessagePumpCFRunLoopBase::RunWork() { 468 if (!delegate_) { 469 // This point can be reached with a NULL delegate_ if Run is not on the 470 // stack but foreign code is spinning the CFRunLoop. Arrange to come back 471 // here when a delegate is available. 472 delegateless_work_ = true; 473 return false; 474 } 475 476 if (instrumentation_) 477 instrumentation_->WorkSourceEntered(delegate_); 478 479 // The NSApplication-based run loop only drains the autorelease pool at each 480 // UI event (NSEvent). The autorelease pool is not drained for each 481 // CFRunLoopSource target that's run. Use a local pool for any autoreleased 482 // objects if the app is not currently handling a UI event to ensure they're 483 // released promptly even in the absence of UI events. 484 MessagePumpScopedAutoreleasePool autorelease_pool(this); 485 486 // Call DoWork and DoDelayedWork once, and if something was done, arrange to 487 // come back here again as long as the loop is still running. 488 bool did_work = delegate_->DoWork(); 489 bool resignal_work_source = did_work; 490 491 TimeTicks next_time; 492 delegate_->DoDelayedWork(&next_time); 493 if (!did_work) { 494 // Determine whether there's more delayed work, and if so, if it needs to 495 // be done at some point in the future or if it's already time to do it. 496 // Only do these checks if did_work is false. If did_work is true, this 497 // function, and therefore any additional delayed work, will get another 498 // chance to run before the loop goes to sleep. 499 bool more_delayed_work = !next_time.is_null(); 500 if (more_delayed_work) { 501 TimeDelta delay = next_time - TimeTicks::Now(); 502 if (delay > TimeDelta()) { 503 // There's more delayed work to be done in the future. 504 ScheduleDelayedWork(next_time); 505 } else { 506 // There's more delayed work to be done, and its time is in the past. 507 // Arrange to come back here directly as long as the loop is still 508 // running. 509 resignal_work_source = true; 510 } 511 } 512 } 513 514 if (resignal_work_source) { 515 CFRunLoopSourceSignal(work_source_); 516 } 517 518 if (instrumentation_) 519 instrumentation_->WorkSourceExited(); 520 521 return resignal_work_source; 522 } 523 524 // Called from the run loop. 525 // static 526 void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) { 527 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 528 self->RunIdleWork(); 529 } 530 531 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. 532 bool MessagePumpCFRunLoopBase::RunIdleWork() { 533 if (!delegate_) { 534 // This point can be reached with a NULL delegate_ if Run is not on the 535 // stack but foreign code is spinning the CFRunLoop. Arrange to come back 536 // here when a delegate is available. 537 delegateless_idle_work_ = true; 538 return false; 539 } 540 541 // The NSApplication-based run loop only drains the autorelease pool at each 542 // UI event (NSEvent). The autorelease pool is not drained for each 543 // CFRunLoopSource target that's run. Use a local pool for any autoreleased 544 // objects if the app is not currently handling a UI event to ensure they're 545 // released promptly even in the absence of UI events. 546 MessagePumpScopedAutoreleasePool autorelease_pool(this); 547 548 // Call DoIdleWork once, and if something was done, arrange to come back here 549 // again as long as the loop is still running. 550 bool did_work = delegate_->DoIdleWork(); 551 if (did_work) { 552 CFRunLoopSourceSignal(idle_work_source_); 553 } 554 555 return did_work; 556 } 557 558 // Called from the run loop. 559 // static 560 void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) { 561 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 562 self->RunNestingDeferredWork(); 563 } 564 565 // Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource. 566 bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() { 567 if (!delegate_) { 568 // This point can be reached with a NULL delegate_ if Run is not on the 569 // stack but foreign code is spinning the CFRunLoop. There's no sense in 570 // attempting to do any work or signalling the work sources because 571 // without a delegate, work is not possible. 572 return false; 573 } 574 575 // Immediately try work in priority order. 576 if (!RunWork()) { 577 if (!RunIdleWork()) { 578 return false; 579 } 580 } else { 581 // Work was done. Arrange for the loop to try non-nestable idle work on 582 // a subsequent pass. 583 CFRunLoopSourceSignal(idle_work_source_); 584 } 585 586 return true; 587 } 588 589 // Called before the run loop goes to sleep or exits, or processes sources. 590 void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() { 591 // deepest_nesting_level_ is set as run loops are entered. If the deepest 592 // level encountered is deeper than the current level, a nested loop 593 // (relative to the current level) ran since the last time nesting-deferred 594 // work was scheduled. When that situation is encountered, schedule 595 // nesting-deferred work in case any work was deferred because nested work 596 // was disallowed. 597 if (deepest_nesting_level_ > nesting_level_) { 598 deepest_nesting_level_ = nesting_level_; 599 CFRunLoopSourceSignal(nesting_deferred_work_source_); 600 } 601 } 602 603 // Called from the run loop. 604 // static 605 void MessagePumpCFRunLoopBase::StartOrEndWaitObserver( 606 CFRunLoopObserverRef observer, 607 CFRunLoopActivity activity, 608 void* info) { 609 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 610 611 if (activity == kCFRunLoopAfterWaiting) { 612 if (self->instrumentation_) 613 self->instrumentation_->WaitingFinished(); 614 return; 615 } 616 617 // Attempt to do some idle work before going to sleep. 618 self->RunIdleWork(); 619 620 // The run loop is about to go to sleep. If any of the work done since it 621 // started or woke up resulted in a nested run loop running, 622 // nesting-deferred work may have accumulated. Schedule it for processing 623 // if appropriate. 624 self->MaybeScheduleNestingDeferredWork(); 625 626 if (self->instrumentation_) 627 self->instrumentation_->WaitingStarted(); 628 } 629 630 // Called from the run loop. 631 // static 632 void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer, 633 CFRunLoopActivity activity, 634 void* info) { 635 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 636 637 // The run loop has reached the top of the loop and is about to begin 638 // processing sources. If the last iteration of the loop at this nesting 639 // level did not sleep or exit, nesting-deferred work may have accumulated 640 // if a nested loop ran. Schedule nesting-deferred work for processing if 641 // appropriate. 642 self->MaybeScheduleNestingDeferredWork(); 643 } 644 645 // Called from the run loop. 646 // static 647 void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer, 648 CFRunLoopActivity activity, 649 void* info) { 650 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 651 652 switch (activity) { 653 case kCFRunLoopEntry: 654 if (self->instrumentation_) 655 self->instrumentation_->LoopEntered(); 656 657 ++self->nesting_level_; 658 if (self->nesting_level_ > self->deepest_nesting_level_) { 659 self->deepest_nesting_level_ = self->nesting_level_; 660 } 661 break; 662 663 case kCFRunLoopExit: 664 // Not all run loops go to sleep. If a run loop is stopped before it 665 // goes to sleep due to a CFRunLoopStop call, or if the timeout passed 666 // to CFRunLoopRunInMode expires, the run loop may proceed directly from 667 // handling sources to exiting without any sleep. This most commonly 668 // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it 669 // to make a single pass through the loop and exit without sleep. Some 670 // native loops use CFRunLoop in this way. Because StartOrEndWaitObserver 671 // will not be called in these case, MaybeScheduleNestingDeferredWork 672 // needs to be called here, as the run loop exits. 673 // 674 // MaybeScheduleNestingDeferredWork consults self->nesting_level_ 675 // to determine whether to schedule nesting-deferred work. It expects 676 // the nesting level to be set to the depth of the loop that is going 677 // to sleep or exiting. It must be called before decrementing the 678 // value so that the value still corresponds to the level of the exiting 679 // loop. 680 self->MaybeScheduleNestingDeferredWork(); 681 --self->nesting_level_; 682 683 if (self->instrumentation_) 684 self->instrumentation_->LoopExited(); 685 break; 686 687 default: 688 break; 689 } 690 691 self->EnterExitRunLoop(activity); 692 } 693 694 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default 695 // implementation is a no-op. 696 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { 697 } 698 699 // Base version returns a standard NSAutoreleasePool. 700 NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() { 701 return [[NSAutoreleasePool alloc] init]; 702 } 703 704 MessagePumpCFRunLoop::MessagePumpCFRunLoop() 705 : quit_pending_(false) { 706 } 707 708 MessagePumpCFRunLoop::~MessagePumpCFRunLoop() {} 709 710 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were 711 // running lower on the run loop thread's stack when this object was created, 712 // the same number of CFRunLoopRun loops must be running for the outermost call 713 // to Run. Run/DoRun are reentrant after that point. 714 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { 715 // This is completely identical to calling CFRunLoopRun(), except autorelease 716 // pool management is introduced. 717 int result; 718 do { 719 MessagePumpScopedAutoreleasePool autorelease_pool(this); 720 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 721 kCFTimeIntervalMax, 722 false); 723 } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); 724 } 725 726 // Must be called on the run loop thread. 727 void MessagePumpCFRunLoop::Quit() { 728 // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. 729 if (nesting_level() == run_nesting_level()) { 730 // This object is running the innermost loop, just stop it. 731 CFRunLoopStop(run_loop()); 732 } else { 733 // There's another loop running inside the loop managed by this object. 734 // In other words, someone else called CFRunLoopRunInMode on the same 735 // thread, deeper on the stack than the deepest Run call. Don't preempt 736 // other run loops, just mark this object to quit the innermost Run as 737 // soon as the other inner loops not managed by Run are done. 738 quit_pending_ = true; 739 } 740 } 741 742 // Called by MessagePumpCFRunLoopBase::EnterExitObserver. 743 void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) { 744 if (activity == kCFRunLoopExit && 745 nesting_level() == run_nesting_level() && 746 quit_pending_) { 747 // Quit was called while loops other than those managed by this object 748 // were running further inside a run loop managed by this object. Now 749 // that all unmanaged inner run loops are gone, stop the loop running 750 // just inside Run. 751 CFRunLoopStop(run_loop()); 752 quit_pending_ = false; 753 } 754 } 755 756 MessagePumpNSRunLoop::MessagePumpNSRunLoop() 757 : keep_running_(true) { 758 CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); 759 source_context.perform = NoOp; 760 quit_source_ = CFRunLoopSourceCreate(NULL, // allocator 761 0, // priority 762 &source_context); 763 CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes); 764 } 765 766 MessagePumpNSRunLoop::~MessagePumpNSRunLoop() { 767 CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes); 768 CFRelease(quit_source_); 769 } 770 771 void MessagePumpNSRunLoop::DoRun(Delegate* delegate) { 772 while (keep_running_) { 773 // NSRunLoop manages autorelease pools itself. 774 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 775 beforeDate:[NSDate distantFuture]]; 776 } 777 778 keep_running_ = true; 779 } 780 781 void MessagePumpNSRunLoop::Quit() { 782 keep_running_ = false; 783 CFRunLoopSourceSignal(quit_source_); 784 CFRunLoopWakeUp(run_loop()); 785 } 786 787 #if defined(OS_IOS) 788 MessagePumpUIApplication::MessagePumpUIApplication() 789 : run_loop_(NULL) { 790 } 791 792 MessagePumpUIApplication::~MessagePumpUIApplication() {} 793 794 void MessagePumpUIApplication::DoRun(Delegate* delegate) { 795 NOTREACHED(); 796 } 797 798 void MessagePumpUIApplication::Quit() { 799 NOTREACHED(); 800 } 801 802 void MessagePumpUIApplication::Attach(Delegate* delegate) { 803 DCHECK(!run_loop_); 804 run_loop_ = new RunLoop(); 805 CHECK(run_loop_->BeforeRun()); 806 SetDelegate(delegate); 807 } 808 809 #else 810 811 MessagePumpNSApplication::MessagePumpNSApplication() 812 : keep_running_(true), 813 running_own_loop_(false) { 814 EnableInstrumentation(); 815 } 816 817 MessagePumpNSApplication::~MessagePumpNSApplication() {} 818 819 void MessagePumpNSApplication::DoRun(Delegate* delegate) { 820 if (instrumentation_) 821 instrumentation_->StartIfNeeded(); 822 823 bool last_running_own_loop_ = running_own_loop_; 824 825 // NSApp must be initialized by calling: 826 // [{some class which implements CrAppProtocol} sharedApplication] 827 // Most likely candidates are CrApplication or BrowserCrApplication. 828 // These can be initialized from C++ code by calling 829 // RegisterCrApp() or RegisterBrowserCrApp(). 830 CHECK(NSApp); 831 832 if (![NSApp isRunning]) { 833 running_own_loop_ = false; 834 // NSApplication manages autorelease pools itself when run this way. 835 [NSApp run]; 836 } else { 837 running_own_loop_ = true; 838 NSDate* distant_future = [NSDate distantFuture]; 839 while (keep_running_) { 840 MessagePumpScopedAutoreleasePool autorelease_pool(this); 841 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask 842 untilDate:distant_future 843 inMode:NSDefaultRunLoopMode 844 dequeue:YES]; 845 if (event) { 846 [NSApp sendEvent:event]; 847 } 848 } 849 keep_running_ = true; 850 } 851 852 running_own_loop_ = last_running_own_loop_; 853 } 854 855 void MessagePumpNSApplication::Quit() { 856 if (!running_own_loop_) { 857 [[NSApplication sharedApplication] stop:nil]; 858 } else { 859 keep_running_ = false; 860 } 861 862 // Send a fake event to wake the loop up. 863 [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined 864 location:NSZeroPoint 865 modifierFlags:0 866 timestamp:0 867 windowNumber:0 868 context:NULL 869 subtype:0 870 data1:0 871 data2:0] 872 atStart:NO]; 873 } 874 875 MessagePumpCrApplication::MessagePumpCrApplication() { 876 } 877 878 MessagePumpCrApplication::~MessagePumpCrApplication() { 879 } 880 881 // Prevents an autorelease pool from being created if the app is in the midst of 882 // handling a UI event because various parts of AppKit depend on objects that 883 // are created while handling a UI event to be autoreleased in the event loop. 884 // An example of this is NSWindowController. When a window with a window 885 // controller is closed it goes through a stack like this: 886 // (Several stack frames elided for clarity) 887 // 888 // #0 [NSWindowController autorelease] 889 // #1 DoAClose 890 // #2 MessagePumpCFRunLoopBase::DoWork() 891 // #3 [NSRunLoop run] 892 // #4 [NSButton performClick:] 893 // #5 [NSWindow sendEvent:] 894 // #6 [NSApp sendEvent:] 895 // #7 [NSApp run] 896 // 897 // -performClick: spins a nested run loop. If the pool created in DoWork was a 898 // standard NSAutoreleasePool, it would release the objects that were 899 // autoreleased into it once DoWork released it. This would cause the window 900 // controller, which autoreleased itself in frame #0, to release itself, and 901 // possibly free itself. Unfortunately this window controller controls the 902 // window in frame #5. When the stack is unwound to frame #5, the window would 903 // no longer exists and crashes may occur. Apple gets around this by never 904 // releasing the pool it creates in frame #4, and letting frame #7 clean it up 905 // when it cleans up the pool that wraps frame #7. When an autorelease pool is 906 // released it releases all other pools that were created after it on the 907 // autorelease pool stack. 908 // 909 // CrApplication is responsible for setting handlingSendEvent to true just 910 // before it sends the event through the event handling mechanism, and 911 // returning it to its previous value once the event has been sent. 912 NSAutoreleasePool* MessagePumpCrApplication::CreateAutoreleasePool() { 913 if (MessagePumpMac::IsHandlingSendEvent()) 914 return nil; 915 return MessagePumpNSApplication::CreateAutoreleasePool(); 916 } 917 918 // static 919 bool MessagePumpMac::UsingCrApp() { 920 DCHECK([NSThread isMainThread]); 921 922 // If NSApp is still not initialized, then the subclass used cannot 923 // be determined. 924 DCHECK(NSApp); 925 926 // The pump was created using MessagePumpNSApplication. 927 if (g_not_using_cr_app) 928 return false; 929 930 return [NSApp conformsToProtocol:@protocol(CrAppProtocol)]; 931 } 932 933 // static 934 bool MessagePumpMac::IsHandlingSendEvent() { 935 DCHECK([NSApp conformsToProtocol:@protocol(CrAppProtocol)]); 936 NSObject<CrAppProtocol>* app = static_cast<NSObject<CrAppProtocol>*>(NSApp); 937 return [app isHandlingSendEvent]; 938 } 939 #endif // !defined(OS_IOS) 940 941 // static 942 MessagePump* MessagePumpMac::Create() { 943 if ([NSThread isMainThread]) { 944 #if defined(OS_IOS) 945 return new MessagePumpUIApplication; 946 #else 947 if ([NSApp conformsToProtocol:@protocol(CrAppProtocol)]) 948 return new MessagePumpCrApplication; 949 950 // The main-thread MessagePump implementations REQUIRE an NSApp. 951 // Executables which have specific requirements for their 952 // NSApplication subclass should initialize appropriately before 953 // creating an event loop. 954 [NSApplication sharedApplication]; 955 g_not_using_cr_app = true; 956 return new MessagePumpNSApplication; 957 #endif 958 } 959 960 return new MessagePumpNSRunLoop; 961 } 962 963 } // namespace base 964