diff options
author | mark@chromium.org <mark@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-25 19:23:11 +0000 |
---|---|---|
committer | mark@chromium.org <mark@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-25 19:23:11 +0000 |
commit | c4280a9a71af0cedbc434f4ebba6c6b5613758cb (patch) | |
tree | 42450c9343821ad5ccab03f6123fa686081d8247 /base/message_pump_mac.mm | |
parent | 23c0e183bcde11c4e3dc049a065389c8b2173834 (diff) | |
download | chromium_src-c4280a9a71af0cedbc434f4ebba6c6b5613758cb.zip chromium_src-c4280a9a71af0cedbc434f4ebba6c6b5613758cb.tar.gz chromium_src-c4280a9a71af0cedbc434f4ebba6c6b5613758cb.tar.bz2 |
Allow work that was deferred on account of not being runnable in a nested loop
to be processed when returning to an outer loop.
BUG=11470 13442 13468
TEST=base_unittests, test cases in bugs
Review URL: http://codereview.chromium.org/146006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@19272 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/message_pump_mac.mm')
-rw-r--r-- | base/message_pump_mac.mm | 267 |
1 files changed, 173 insertions, 94 deletions
diff --git a/base/message_pump_mac.mm b/base/message_pump_mac.mm index b030a16..5f9d805 100644 --- a/base/message_pump_mac.mm +++ b/base/message_pump_mac.mm @@ -22,7 +22,9 @@ namespace base { // Must be called on the run loop thread. MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() - : delegate_(NULL) { + : nesting_level_(0), + delegate_(NULL) + { run_loop_ = CFRunLoopGetCurrent(); CFRetain(run_loop_); @@ -42,34 +44,69 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); source_context.info = this; - source_context.perform = RunWork; + source_context.perform = RunWorkSource; work_source_ = CFRunLoopSourceCreate(NULL, // allocator - 0, // priority + 1, // priority &source_context); CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes); - source_context.perform = RunDelayedWork; + source_context.perform = RunDelayedWorkSource; delayed_work_source_ = CFRunLoopSourceCreate(NULL, // allocator - 1, // priority + 2, // priority &source_context); CFRunLoopAddSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes); + source_context.perform = RunIdleWorkSource; + idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator + 3, // priority + &source_context); + CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); + + source_context.perform = RunNestingDeferredWorkSource; + nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator + 0, // priority + &source_context); + CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_, + kCFRunLoopCommonModes); + CFRunLoopObserverContext observer_context = CFRunLoopObserverContext(); observer_context.info = this; - idle_work_observer_ = CFRunLoopObserverCreate(NULL, // allocator - kCFRunLoopBeforeWaiting, - true, // repeat - 0, // priority - RunIdleWork, - &observer_context); - CFRunLoopAddObserver(run_loop_, idle_work_observer_, kCFRunLoopCommonModes); + pre_wait_observer_ = CFRunLoopObserverCreate(NULL, // allocator + kCFRunLoopBeforeWaiting, + true, // repeat + 0, // priority + PreWaitObserver, + &observer_context); + CFRunLoopAddObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes); + + enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator + kCFRunLoopEntry | + kCFRunLoopExit, + true, // repeat + 0, // priority + EnterExitObserver, + &observer_context); + CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes); } -// Ideally called on the run loop thread. +// Ideally called on the run loop thread. If other run loops were running +// lower on the run loop thread's stack when this object was created, the +// same number of run loops must be running when this object is destroyed. MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() { - CFRunLoopRemoveObserver(run_loop_, idle_work_observer_, + CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_, + kCFRunLoopCommonModes); + CFRelease(enter_exit_observer_); + + CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes); - CFRelease(idle_work_observer_); + CFRelease(pre_wait_observer_); + + CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_, + kCFRunLoopCommonModes); + CFRelease(nesting_deferred_work_source_); + + CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); + CFRelease(idle_work_source_); CFRunLoopRemoveSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes); CFRelease(delayed_work_source_); @@ -125,7 +162,7 @@ void MessagePumpCFRunLoopBase::ScheduleDelayedWork( // static void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, void* info) { - MessagePumpCFRunLoop* self = static_cast<MessagePumpCFRunLoop*>(info); + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. // In order to establish the proper priority where delegate_->DoDelayedWork @@ -137,102 +174,159 @@ void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, // Called from the run loop. // static -void MessagePumpCFRunLoopBase::RunWork(void* info) { - MessagePumpCFRunLoop* self = static_cast<MessagePumpCFRunLoop*>(info); +void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + self->RunWork(); +} +// Called by MessagePumpCFRunLoopBase::RunWorkSource. +bool MessagePumpCFRunLoopBase::RunWork() { // If we're on the main event loop, the NSApp runloop won't clean up the - // autoreleasepool until there is UI event, so use a local one for any + // autorelease pool until there is a UI event, so use a local one for any // autoreleased objects to ensure they go away sooner. ScopedNSAutoreleasePool autorelease_pool; // Call DoWork once, and if something was done, arrange to come back here // again as long as the loop is still running. - if (self->delegate_->DoWork()) { - CFRunLoopSourceSignal(self->work_source_); + bool did_work = delegate_->DoWork(); + if (did_work) { + CFRunLoopSourceSignal(work_source_); } + + return did_work; } // Called from the run loop. // static -void MessagePumpCFRunLoopBase::RunDelayedWork(void* info) { - MessagePumpCFRunLoop* self = static_cast<MessagePumpCFRunLoop*>(info); +void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + self->RunDelayedWork(); +} +// Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource. +bool MessagePumpCFRunLoopBase::RunDelayedWork() { // If we're on the main event loop, the NSApp runloop won't clean up the - // autoreleasepool until there is UI event, so use a local one for any + // autorelease pool until there is a UI event, so use a local one for any // autoreleased objects to ensure they go away sooner. ScopedNSAutoreleasePool autorelease_pool; Time next_time; - self->delegate_->DoDelayedWork(&next_time); - if (!next_time.is_null()) { + delegate_->DoDelayedWork(&next_time); + + bool more_work = !next_time.is_null(); + if (more_work) { TimeDelta delay = next_time - Time::Now(); if (delay > TimeDelta()) { // There's more delayed work to be done in the future. - self->ScheduleDelayedWork(next_time); + ScheduleDelayedWork(next_time); } else { // There's more delayed work to be done, and its time is in the past. // Arrange to come back here directly as long as the loop is still // running. - CFRunLoopSourceSignal(self->delayed_work_source_); + CFRunLoopSourceSignal(delayed_work_source_); } } + + return more_work; } // Called from the run loop. // static -void MessagePumpCFRunLoopBase::RunIdleWork(CFRunLoopObserverRef observer, - CFRunLoopActivity activity, - void* info) { - MessagePumpCFRunLoop* self = static_cast<MessagePumpCFRunLoop*>(info); +void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + self->RunIdleWork(); +} +// Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. +bool MessagePumpCFRunLoopBase::RunIdleWork() { // If we're on the main event loop, the NSApp runloop won't clean up the - // autoreleasepool until there is UI event, so use a local one for any + // autorelease pool until there is a UI event, so use a local one for any // autoreleased objects to ensure they go away sooner. ScopedNSAutoreleasePool autorelease_pool; - // The "self->delegate_ &&" part of the clause is needed for the case of - // the temporary modal first run dialog. The dialog is displayed really - // early in the Chrome launch process at which time self->delegate_ is null. - // TODO: remove the "self->delegate_ &&" clause from the bellow condition once - // we remove the modal first run dialog. - if (self->delegate_ && self->delegate_->DoIdleWork()) { - // If idle work was done, don't let the loop go to sleep. More idle work - // might be waiting. - CFRunLoopWakeUp(self->run_loop_); + // Call DoIdleWork once, and if something was done, arrange to come back here + // again as long as the loop is still running. + bool did_work = delegate_->DoIdleWork(); + if (did_work) { + CFRunLoopSourceSignal(idle_work_source_); } + + return did_work; +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + self->RunNestingDeferredWork(); +} + +// Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource. +bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() { + if (!RunWork()) { + if (!RunDelayedWork()) { + if (!RunIdleWork()) { + return false; + } + } + } + + return true; +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, + void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + + // Attempt to do some idle work before going to sleep. + self->RunIdleWork(); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, + void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + + switch (activity) { + case kCFRunLoopEntry: + ++self->nesting_level_; + break; + case kCFRunLoopExit: + --self->nesting_level_; + if (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, signal the nesting-deferred work source to ensure that such + // work be afforded an opportunity to be processed if appropriate. + CFRunLoopSourceSignal(self->nesting_deferred_work_source_); + } + break; + default: + break; + } + + self->EnterExitRunLoop(activity); +} + +// Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default +// implementation is a no-op. +void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { } -// Must be called on the run loop thread. MessagePumpCFRunLoop::MessagePumpCFRunLoop() - : nesting_level_(0), - innermost_quittable_(0), + : innermost_quittable_(0), quit_pending_(false) { - CFRunLoopObserverContext observer_context = CFRunLoopObserverContext(); - observer_context.info = this; - enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator - kCFRunLoopEntry | - kCFRunLoopExit, - true, // repeat - 0, // priority - EnterExitRunLoop, - &observer_context); - CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes); } -// Ideally called on the run loop thread. If other CFRunLoopRun loops were +// Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were // running lower on the run loop thread's stack when this object was created, -// the same number of CFRunLoopRun loops must be running when this object is -// destroyed. -MessagePumpCFRunLoop::~MessagePumpCFRunLoop() { - CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_, - kCFRunLoopCommonModes); - CFRelease(enter_exit_observer_); -} - -// Called by CFRunLoopBase::DoRun. If other CFRunLoopRun loops were running -// lower on the run loop thread's stack when this object was created, the same -// number of CFRunLoopRun loops must be running for the outermost call to Run. -// Run/DoRun are reentrant after that point. +// the same number of CFRunLoopRun loops must be running for the outermost call +// to Run. Run/DoRun are reentrant after that point. void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { // nesting_level_ will be incremented in EnterExitRunLoop, so set // innermost_quittable_ accordingly. @@ -267,32 +361,17 @@ void MessagePumpCFRunLoop::Quit() { } } -// Called from the run loop. -// static -void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopObserverRef observer, - CFRunLoopActivity activity, - void* info) { - MessagePumpCFRunLoop* self = static_cast<MessagePumpCFRunLoop*>(info); - - switch (activity) { - case kCFRunLoopEntry: - // If the run loop was entered by a call to Run, this will properly - // balance the decrement done in Run before entering the loop. - ++self->nesting_level_; - break; - case kCFRunLoopExit: - if (--self->nesting_level_ == self->innermost_quittable_ && - self->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(self->run_loop_); - self->quit_pending_ = false; - } - break; - default: - break; +// Called by MessagePumpCFRunLoopBase::EnterExitObserver. +void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) { + if (activity == kCFRunLoopExit && + nesting_level_ == innermost_quittable_ && + 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_); + quit_pending_ = false; } } |