| Index: base/message_pump_mac.mm
|
| ===================================================================
|
| --- base/message_pump_mac.mm (revision 29677)
|
| +++ base/message_pump_mac.mm (working copy)
|
| @@ -22,13 +22,13 @@
|
|
|
| // Must be called on the run loop thread.
|
| MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
|
| - : nesting_level_(0),
|
| + : delegate_(NULL),
|
| + nesting_level_(0),
|
| run_nesting_level_(0),
|
| - delegate_(NULL),
|
| + deepest_nesting_level_(0),
|
| delegateless_work_(false),
|
| delegateless_delayed_work_(false),
|
| - delegateless_idle_work_(false)
|
| - {
|
| + delegateless_idle_work_(false) {
|
| run_loop_ = CFRunLoopGetCurrent();
|
| CFRetain(run_loop_);
|
|
|
| @@ -134,28 +134,26 @@
|
| Delegate* last_delegate = delegate_;
|
| delegate_ = delegate;
|
|
|
| - // If any work showed up but could not be dispatched for want of a delegate,
|
| - // set it up for dispatch again now that a delegate is available.
|
| - if (delegateless_work_) {
|
| - CFRunLoopSourceSignal(work_source_);
|
| - delegateless_work_ = false;
|
| + if (delegate) {
|
| + // If any work showed up but could not be dispatched for want of a
|
| + // delegate, set it up for dispatch again now that a delegate is
|
| + // available.
|
| + if (delegateless_work_) {
|
| + CFRunLoopSourceSignal(work_source_);
|
| + delegateless_work_ = false;
|
| + }
|
| + if (delegateless_delayed_work_) {
|
| + CFRunLoopSourceSignal(delayed_work_source_);
|
| + delegateless_delayed_work_ = false;
|
| + }
|
| + if (delegateless_idle_work_) {
|
| + CFRunLoopSourceSignal(idle_work_source_);
|
| + delegateless_idle_work_ = false;
|
| + }
|
| }
|
| - if (delegateless_delayed_work_) {
|
| - CFRunLoopSourceSignal(delayed_work_source_);
|
| - delegateless_delayed_work_ = false;
|
| - }
|
| - if (delegateless_idle_work_) {
|
| - CFRunLoopSourceSignal(idle_work_source_);
|
| - delegateless_idle_work_ = false;
|
| - }
|
|
|
| DoRun(delegate);
|
|
|
| - // If this was an inner Run invocation, arrange to run nesting-deferred work
|
| - // when the stack has unwound to an outer invocation.
|
| - if (nesting_level_)
|
| - CFRunLoopSourceSignal(nesting_deferred_work_source_);
|
| -
|
| // Restore the previous state of the object.
|
| delegate_ = last_delegate;
|
| run_nesting_level_ = last_run_nesting_level;
|
| @@ -220,9 +218,11 @@
|
| return false;
|
| }
|
|
|
| - // If we're on the main event loop, the NSApp runloop won't clean up the
|
| - // autorelease pool until there is a UI event, so use a local one for any
|
| - // autoreleased objects to ensure they go away sooner.
|
| + // The NSApplication-based run loop only drains the autorelease pool at each
|
| + // UI event (NSEvent). The autorelease pool is not drained for each
|
| + // CFRunLoopSource target that's run. Use a local pool for any autoreleased
|
| + // objects to ensure they're released promptly even in the absence of UI
|
| + // events.
|
| ScopedNSAutoreleasePool autorelease_pool;
|
|
|
| // Call DoWork once, and if something was done, arrange to come back here
|
| @@ -252,9 +252,11 @@
|
| return false;
|
| }
|
|
|
| - // If we're on the main event loop, the NSApp runloop won't clean up the
|
| - // autorelease pool until there is a UI event, so use a local one for any
|
| - // autoreleased objects to ensure they go away sooner.
|
| + // The NSApplication-based run loop only drains the autorelease pool at each
|
| + // UI event (NSEvent). The autorelease pool is not drained for each
|
| + // CFRunLoopSource target that's run. Use a local pool for any autoreleased
|
| + // objects to ensure they're released promptly even in the absence of UI
|
| + // events.
|
| ScopedNSAutoreleasePool autorelease_pool;
|
|
|
| Time next_time;
|
| @@ -294,9 +296,11 @@
|
| return false;
|
| }
|
|
|
| - // If we're on the main event loop, the NSApp runloop won't clean up the
|
| - // autorelease pool until there is a UI event, so use a local one for any
|
| - // autoreleased objects to ensure they go away sooner.
|
| + // The NSApplication-based run loop only drains the autorelease pool at each
|
| + // UI event (NSEvent). The autorelease pool is not drained for each
|
| + // CFRunLoopSource target that's run. Use a local pool for any autoreleased
|
| + // objects to ensure they're released promptly even in the absence of UI
|
| + // events.
|
| ScopedNSAutoreleasePool autorelease_pool;
|
|
|
| // Call DoIdleWork once, and if something was done, arrange to come back here
|
| @@ -347,6 +351,20 @@
|
| return true;
|
| }
|
|
|
| +// Called before the run loop goes to sleep or exits.
|
| +void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() {
|
| + // deepest_nesting_level_ is set as run loops are entered. If the deepest
|
| + // level encountered is deeper than the current level (about to sleep or
|
| + // exit), a nested loop (relative to the current level) ran since the last
|
| + // time nesting-deferred work was scheduled. When that situation is
|
| + // encountered, schedule nesting-deferred work in case any work was deferred
|
| + // because nested work was disallowed.
|
| + if (deepest_nesting_level_ > nesting_level_) {
|
| + deepest_nesting_level_ = nesting_level_;
|
| + CFRunLoopSourceSignal(nesting_deferred_work_source_);
|
| + }
|
| +}
|
| +
|
| // Called from the run loop.
|
| // static
|
| void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer,
|
| @@ -356,6 +374,12 @@
|
|
|
| // Attempt to do some idle work before going to sleep.
|
| self->RunIdleWork();
|
| +
|
| + // The run loop is about to go to sleep. If any of the work done since it
|
| + // started or woke up resulted in a nested run loop running,
|
| + // nesting-deferred work may have accumulated. Schedule it for processing
|
| + // if appropriate.
|
| + self->MaybeScheduleNestingDeferredWork();
|
| }
|
|
|
| // Called from the run loop.
|
| @@ -368,24 +392,29 @@
|
| switch (activity) {
|
| case kCFRunLoopEntry:
|
| ++self->nesting_level_;
|
| + if (self->nesting_level_ > self->deepest_nesting_level_) {
|
| + self->deepest_nesting_level_ = self->nesting_level_;
|
| + }
|
| break;
|
| case kCFRunLoopExit:
|
| - // After decrementing self->nesting_level_, it will be one less than
|
| - // self->run_nesting_level_ if the loop that is now exiting was directly
|
| - // started by a DoRun call.
|
| + // Not all run loops go to sleep. If a run loop is stopped before it
|
| + // goes to sleep due to a CFRunLoopStop call, or if the timeout passed
|
| + // to CFRunLoopRunInMode expires, the run loop may proceed directly from
|
| + // handling sources to exiting without any sleep. This most commonly
|
| + // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it
|
| + // to make a single pass through the loop and exit without sleep. Some
|
| + // native loops use CFRunLoop in this way. Because PreWaitObserver will
|
| + // not be called in these case, MaybeScheduleNestingDeferredWork needs
|
| + // to be called here, as the run loop exits.
|
| + //
|
| + // MaybeScheduleNestingDeferredWork consults self->nesting_level_
|
| + // to determine whether to schedule nesting-deferred work. It expects
|
| + // the nesting level to be set to the depth of the loop that is going
|
| + // to sleep or exiting. It must be called before decrementing the
|
| + // value so that the value still corresponds to the level of the exiting
|
| + // loop.
|
| + self->MaybeScheduleNestingDeferredWork();
|
| --self->nesting_level_;
|
| -
|
| - if (self->nesting_level_ >= self->run_nesting_level_ &&
|
| - self->nesting_level_) {
|
| - // It's possible that some work was not performed because it was
|
| - // inappropriate to do within a nested loop. When leaving any inner
|
| - // loop not directly supervised by a DoRun call, such as nested native
|
| - // loops, signal the nesting-deferred work source to ensure that such
|
| - // work be afforded an opportunity to be processed if appropriate.
|
| - // This is not done for loops being run directly by Run/DoRun because
|
| - // it can be done directly as Run exits.
|
| - CFRunLoopSourceSignal(self->nesting_deferred_work_source_);
|
| - }
|
| break;
|
| default:
|
| break;
|
| @@ -420,15 +449,15 @@
|
| // Must be called on the run loop thread.
|
| void MessagePumpCFRunLoop::Quit() {
|
| // Stop the innermost run loop managed by this MessagePumpCFRunLoop object.
|
| - if (nesting_level_ == run_nesting_level_) {
|
| + if (nesting_level() == run_nesting_level()) {
|
| // This object is running the innermost loop, just stop it.
|
| - CFRunLoopStop(run_loop_);
|
| + CFRunLoopStop(run_loop());
|
| } else {
|
| // There's another loop running inside the loop managed by this object.
|
| - // In other words, someone else called CFRunLoopRun on the same thread,
|
| - // higher on the stack than our highest Run call. Don't preempt other
|
| - // run loops, just mark the object to quit our innermost run loop as soon
|
| - // as the other inner loops we don't manage are done.
|
| + // In other words, someone else called CFRunLoopRunInMode on the same
|
| + // thread, deeper on the stack than the deepest Run call. Don't preempt
|
| + // other run loops, just mark this object to quit the innermost Run as
|
| + // soon as the other inner loops not managed by Run are done.
|
| quit_pending_ = true;
|
| }
|
| }
|
| @@ -436,13 +465,13 @@
|
| // Called by MessagePumpCFRunLoopBase::EnterExitObserver.
|
| void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) {
|
| if (activity == kCFRunLoopExit &&
|
| - nesting_level_ == run_nesting_level_ &&
|
| + nesting_level() == run_nesting_level() &&
|
| quit_pending_) {
|
| // Quit was called while loops other than those managed by this object
|
| // were running further inside a run loop managed by this object. Now
|
| // that all unmanaged inner run loops are gone, stop the loop running
|
| // just inside Run.
|
| - CFRunLoopStop(run_loop_);
|
| + CFRunLoopStop(run_loop());
|
| quit_pending_ = false;
|
| }
|
| }
|
| @@ -454,11 +483,11 @@
|
| quit_source_ = CFRunLoopSourceCreate(NULL, // allocator
|
| 0, // priority
|
| &source_context);
|
| - CFRunLoopAddSource(run_loop_, quit_source_, kCFRunLoopCommonModes);
|
| + CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
|
| }
|
|
|
| MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
|
| - CFRunLoopRemoveSource(run_loop_, quit_source_, kCFRunLoopCommonModes);
|
| + CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
|
| CFRelease(quit_source_);
|
| }
|
|
|
| @@ -475,7 +504,7 @@
|
| void MessagePumpNSRunLoop::Quit() {
|
| keep_running_ = false;
|
| CFRunLoopSourceSignal(quit_source_);
|
| - CFRunLoopWakeUp(run_loop_);
|
| + CFRunLoopWakeUp(run_loop());
|
| }
|
|
|
| MessagePumpNSApplication::MessagePumpNSApplication()
|
|
|