summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
Diffstat (limited to 'base')
-rw-r--r--base/message_pump_mac.h45
-rw-r--r--base/message_pump_mac.mm145
2 files changed, 117 insertions, 73 deletions
diff --git a/base/message_pump_mac.h b/base/message_pump_mac.h
index 34390c0..9c24d8b 100644
--- a/base/message_pump_mac.h
+++ b/base/message_pump_mac.h
@@ -54,17 +54,10 @@ class MessagePumpCFRunLoopBase : public MessagePump {
virtual void ScheduleDelayedWork(const Time& delayed_work_time);
protected:
- // The thread's run loop.
- CFRunLoopRef run_loop_;
-
- // The recursion depth of the currently-executing CFRunLoopRun loop on the
- // run loop's thread. 0 if no run loops are running inside of whatever scope
- // 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_;
+ // Accessors for private data members to be used by subclasses.
+ CFRunLoopRef run_loop() const { return run_loop_; }
+ int nesting_level() const { return nesting_level_; }
+ int run_nesting_level() const { return run_nesting_level_; }
private:
// Timer callback scheduled by ScheduleDelayedWork. This does not do any
@@ -96,13 +89,19 @@ class MessagePumpCFRunLoopBase : public MessagePump {
// Perform work that may have been deferred because it was not runnable
// within a nested run loop. This is associated with
- // nesting_deferred_work_source_ and is signalled by EnterExitObserver when
- // a run loop exits, so that an outer loop will be able to perform the
- // necessary tasks. The static method calls the instance method; the
- // instance method returns true if anything was done.
+ // nesting_deferred_work_source_ and is signalled by
+ // MaybeScheduleNestingDeferredWork when returning from a nested loop,
+ // so that an outer loop will be able to perform the necessary tasks if it
+ // permits nestable tasks.
static void RunNestingDeferredWorkSource(void* info);
bool RunNestingDeferredWork();
+ // Schedules possible nesting-deferred work to be processed before the run
+ // loop goes to sleep or exits. If this function detects that a nested loop
+ // had run since the previous attempt to schedule nesting-deferred work, it
+ // will schedule a call to RunNestingDeferredWorkSource.
+ void MaybeScheduleNestingDeferredWork();
+
// Observer callback responsible for performing idle-priority work, before
// the run loop goes to sleep. Associated with idle_work_observer_.
static void PreWaitObserver(CFRunLoopObserverRef observer,
@@ -119,6 +118,9 @@ class MessagePumpCFRunLoopBase : public MessagePump {
// the basis of run loops starting and stopping.
virtual void EnterExitRunLoop(CFRunLoopActivity activity);
+ // The thread's run loop.
+ CFRunLoopRef run_loop_;
+
// The timer, sources, and observers are described above alongside their
// callbacks.
CFRunLoopTimerRef delayed_work_timer_;
@@ -132,6 +134,19 @@ class MessagePumpCFRunLoopBase : public MessagePump {
// (weak) Delegate passed as an argument to the innermost Run call.
Delegate* delegate_;
+ // The recursion depth of the currently-executing CFRunLoopRun loop on the
+ // run loop's thread. 0 if no run loops are running inside of whatever scope
+ // 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_;
+
+ // The deepest (numerically highest) recursion depth encountered since the
+ // most recent attempt to run nesting-deferred work.
+ int deepest_nesting_level_;
+
// "Delegateless" work flags are set when work is ready to be performed but
// must wait until a delegate is available to process it. This can happen
// when a MessagePumpCFRunLoopBase is instantiated and work arrives without
diff --git a/base/message_pump_mac.mm b/base/message_pump_mac.mm
index 8ed7491..3c448a0 100644
--- a/base/message_pump_mac.mm
+++ b/base/message_pump_mac.mm
@@ -22,13 +22,13 @@ namespace base {
// 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 @@ void MessagePumpCFRunLoopBase::Run(Delegate* delegate) {
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 (delegateless_delayed_work_) {
- CFRunLoopSourceSignal(delayed_work_source_);
- delegateless_delayed_work_ = false;
- }
- if (delegateless_idle_work_) {
- CFRunLoopSourceSignal(idle_work_source_);
- delegateless_idle_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;
+ }
}
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 @@ bool MessagePumpCFRunLoopBase::RunWork() {
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 @@ bool MessagePumpCFRunLoopBase::RunDelayedWork() {
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 @@ bool MessagePumpCFRunLoopBase::RunIdleWork() {
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 @@ bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() {
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 @@ void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer,
// 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 @@ void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer,
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 @@ void MessagePumpCFRunLoop::DoRun(Delegate* delegate) {
// 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 @@ void MessagePumpCFRunLoop::Quit() {
// 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 @@ MessagePumpNSRunLoop::MessagePumpNSRunLoop()
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::DoRun(Delegate* delegate) {
void MessagePumpNSRunLoop::Quit() {
keep_running_ = false;
CFRunLoopSourceSignal(quit_source_);
- CFRunLoopWakeUp(run_loop_);
+ CFRunLoopWakeUp(run_loop());
}
MessagePumpNSApplication::MessagePumpNSApplication()
@@ -519,7 +548,7 @@ void MessagePumpNSApplication::Quit() {
// Send a fake event to wake the loop up.
[NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
- location:NSMakePoint(0,0)
+ location:NSMakePoint(0, 0)
modifierFlags:0
timestamp:0
windowNumber:0