From c83d0103a4806d2d7b6d6bf96b814a54b51c2c75 Mon Sep 17 00:00:00 2001 From: "mark@chromium.org" Date: Tue, 13 Oct 2009 13:51:19 +0000 Subject: When a nested run loop is driven directly by Run/DoRun as opposed to being a nested native loop run by code outside of our control, and the nested run loop stops, don't schedule nesting-deferred work until returning fron Run/DoRun. BUG=24337, 24383 TEST=With r28745 backed out or an equivalent change recommitted: 1. Test case from bug 24337: the window should close 2. Test case from bug 24383: no Chromium processes should use 100% CPU Review URL: http://codereview.chromium.org/264042 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@28811 0039d316-1c4b-4281-b951-d872f2087c98 --- base/message_pump_mac.h | 8 ++++---- base/message_pump_mac.mm | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 30 insertions(+), 18 deletions(-) (limited to 'base') diff --git a/base/message_pump_mac.h b/base/message_pump_mac.h index f7e9a83..34390c0 100644 --- a/base/message_pump_mac.h +++ b/base/message_pump_mac.h @@ -62,6 +62,10 @@ class MessagePumpCFRunLoopBase : public MessagePump { // the object was created in. int nesting_level_; + // The recursion depth (calculated in the same way as nesting_level_) of the + // innermost executing CFRunLoopRun loop started by a call to Run. + int run_nesting_level_; + private: // Timer callback scheduled by ScheduleDelayedWork. This does not do any // work, but it signals delayed_work_source_ so that delayed work can be @@ -150,10 +154,6 @@ class MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase { private: virtual void EnterExitRunLoop(CFRunLoopActivity activity); - // The recursion depth (calculated in the same way as nesting_level_) of the - // innermost executing CFRunLoopRun loop started by a call to Run. - int innermost_quittable_; - // True if Quit is called to stop the innermost MessagePump // (innermost_quittable_) but some other CFRunLoopRun loop (nesting_level_) // is running inside the MessagePump's innermost Run call. diff --git a/base/message_pump_mac.mm b/base/message_pump_mac.mm index c9a0ea6..8ed7491 100644 --- a/base/message_pump_mac.mm +++ b/base/message_pump_mac.mm @@ -23,6 +23,7 @@ namespace base { // Must be called on the run loop thread. MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() : nesting_level_(0), + run_nesting_level_(0), delegate_(NULL), delegateless_work_(false), delegateless_delayed_work_(false), @@ -125,6 +126,11 @@ MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() { // Must be called on the run loop thread. void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { + // nesting_level_ will be incremented in EnterExitRunLoop, so set + // run_nesting_level_ accordingly. + int last_run_nesting_level = run_nesting_level_; + run_nesting_level_ = nesting_level_ + 1; + Delegate* last_delegate = delegate_; delegate_ = delegate; @@ -145,7 +151,14 @@ void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { 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; } // May be called on any thread. @@ -357,12 +370,20 @@ void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer, ++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. --self->nesting_level_; - if (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, signal the nesting-deferred work source to ensure that such + // 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; @@ -379,8 +400,7 @@ void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { } MessagePumpCFRunLoop::MessagePumpCFRunLoop() - : innermost_quittable_(0), - quit_pending_(false) { + : quit_pending_(false) { } // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were @@ -388,11 +408,6 @@ MessagePumpCFRunLoop::MessagePumpCFRunLoop() // 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. - int last_innermost_quittable = innermost_quittable_; - innermost_quittable_ = nesting_level_ + 1; - // This is completely identical to calling CFRunLoopRun(), except autorelease // pool management is introduced. int result; @@ -400,15 +415,12 @@ void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { ScopedNSAutoreleasePool autorelease_pool; result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, DBL_MAX, false); } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); - - // Restore the previous state of the object. - innermost_quittable_ = last_innermost_quittable; } // Must be called on the run loop thread. void MessagePumpCFRunLoop::Quit() { // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. - if (nesting_level_ == innermost_quittable_) { + if (nesting_level_ == run_nesting_level_) { // This object is running the innermost loop, just stop it. CFRunLoopStop(run_loop_); } else { @@ -424,7 +436,7 @@ void MessagePumpCFRunLoop::Quit() { // Called by MessagePumpCFRunLoopBase::EnterExitObserver. void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) { if (activity == kCFRunLoopExit && - nesting_level_ == innermost_quittable_ && + 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 -- cgit v1.1