Index: base/message_pump_mac.mm |
=================================================================== |
--- base/message_pump_mac.mm (revision 30652) |
+++ base/message_pump_mac.mm (working copy) |
@@ -1,4 +1,4 @@ |
-// Copyright (c) 2009 The Chromium Authors. All rights reserved. |
+// Copyright (c) 2008 The Chromium Authors. All rights reserved. |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
@@ -11,30 +11,9 @@ |
#include <limits> |
-#include "base/logging.h" |
#include "base/scoped_nsautorelease_pool.h" |
-#include "base/scoped_nsobject.h" |
-#include "base/scoped_ptr.h" |
#include "base/time.h" |
-// This pool passes the objects stored in it to the pool below it in the |
-// autorelease pool stack when -drain is called. See its implementation |
-// for all the gory details. |
-@interface DeferredAutoreleasePool : NSAutoreleasePool { |
- @private |
- NSMutableArray* deferredPool_; // Strong. |
-} |
-@end |
- |
-// Informs the message pump that it has been drained. |
-@interface MessagePumpNSAppDeferredAutoReleasePool : DeferredAutoreleasePool { |
- @private |
- base::MessagePumpNSApplication* pump_; // Weak. |
-} |
- |
-- (id)initWithPump:(base::MessagePumpNSApplication*)pump; |
-@end |
- |
namespace { |
void NoOp(void* info) { |
@@ -43,24 +22,6 @@ |
const CFTimeInterval kCFTimeIntervalMax = |
std::numeric_limits<CFTimeInterval>::max(); |
-// Class for dealing with scoping autorelease pools when they need to be a |
-// special implementation of NSAutoreleasePool. |
-// This class is independant from ScopedNSAutoreleasePool because having |
-// ScopedNSAutoreleasePool(NSAutoreleasePool*) would just clutter |
-// ScopedNSAutoreleasePool's interface, potentially leading to misuse, and the |
-// case where using an externally created instance of a kind of |
-// NSAutoreleasePool is so rare. |
-class AutoreleasePoolOwner { |
- public: |
- explicit AutoreleasePoolOwner(NSAutoreleasePool *pool) : pool_(pool) {} |
- ~AutoreleasePoolOwner() { |
- [pool_ drain]; |
- } |
- private: |
- NSAutoreleasePool *pool_; |
- DISALLOW_COPY_AND_ASSIGN(AutoreleasePoolOwner); |
-}; |
- |
} // namespace |
namespace base { |
@@ -300,7 +261,12 @@ |
return false; |
} |
- AutoreleasePoolOwner pool(CreateAutoreleasePool()); |
+ // 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 |
// again as long as the loop is still running. |
@@ -329,7 +295,12 @@ |
return false; |
} |
- AutoreleasePoolOwner pool(CreateAutoreleasePool()); |
+ // 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; |
delegate_->DoDelayedWork(&next_time); |
@@ -368,7 +339,12 @@ |
return false; |
} |
- AutoreleasePoolOwner pool(CreateAutoreleasePool()); |
+ // 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 |
// again as long as the loop is still running. |
@@ -579,14 +555,6 @@ |
void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { |
} |
-// Called by MessagePumpCFRunLoopBase::RunWork, |
-// MessagePumpCFRunLoopBase::RunDelayedWork and |
-// MessagePumpCFRunLoopBase::RunIdleWork. Default implementation is a standard |
-// NSAutoreleasePool that can be used for eash source as it fires. |
-NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() { |
- return [[NSAutoreleasePool alloc] init]; |
-} |
- |
MessagePumpCFRunLoop::MessagePumpCFRunLoop() |
: quit_pending_(false) { |
} |
@@ -670,8 +638,7 @@ |
MessagePumpNSApplication::MessagePumpNSApplication() |
: keep_running_(true), |
- running_own_loop_(false), |
- needs_event_loop_wake_up_(false) { |
+ running_own_loop_(false) { |
} |
void MessagePumpNSApplication::DoRun(Delegate* delegate) { |
@@ -709,123 +676,19 @@ |
keep_running_ = false; |
} |
- WakeUpEventLoop(); |
+ // Send a fake event to wake the loop up. |
+ [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined |
+ location:NSMakePoint(0, 0) |
+ modifierFlags:0 |
+ timestamp:0 |
+ windowNumber:0 |
+ context:NULL |
+ subtype:0 |
+ data1:0 |
+ data2:0] |
+ atStart:NO]; |
} |
-// Called by MessagePumpCFRunLoopBase::EnterExitObserver. |
-void MessagePumpNSApplication::EnterExitRunLoop(CFRunLoopActivity activity) { |
- if (activity == kCFRunLoopExit && needs_event_loop_wake_up_) { |
- WakeUpEventLoop(); |
- needs_event_loop_wake_up_ = false; |
- } |
-} |
- |
-NSAutoreleasePool* MessagePumpNSApplication::CreateAutoreleasePool() { |
- // Return an instance of a special autorelease pool that deals |
- // correctly with nested autorelease pools in run loops. This is used by |
- // MessagePumpNSApplication instead of a standard NSAutoreleasePool |
- // because of how NSApplication interacts with CFRunLoops. |
- // |
- // A standard NSApplication event loop looks something like this: |
- // |
- // - (void)run { |
- // while ([self isRunning]) { |
- // NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; |
- // NSEvent* event = [self nextEventMatchingMask:NSAnyEventMask |
- // untilDate:[NSDate distantFuture] |
- // inMode:NSDefaultRunLoopMode |
- // dequeue:YES]; |
- // [self sendEvent:event]; |
- // [self updateWindows]; |
- // [pool release]; |
- // } |
- // } |
- // |
- // Notice that the autorelease pool only gets cleaned up when an NSEvent gets |
- // returned by nextEventMatchingMask:untilDate:inMode:dequeue: as this is an |
- // an important point in getting objects released in a timely manner. |
- // |
- // Message pumps have a variety of CFRunLoopSources attached to their |
- // CFRunLoops. These sources can cause actions to run from within |
- // [NSApplication -nextEventMatchingMask:untilDate:inMode:dequeue:]. These |
- // actions can call into Objective C code when they fire and add things to the |
- // autorelease pool. |
- // |
- // The problem is that the sources don't necessarily cause |
- // an NSEvent to be generated and therefore the autorelease pool in |
- // [NSApplication run] is not necessarily released in a timely fashion. This |
- // potentially leaves a lot of objects that can be cleaned up sitting around |
- // taking up memory until something, such as an NSEvent, causes [NSApplication |
- // run] to release its pool. |
- // |
- // The easy answer would seem to be to wrap all of the calls from the |
- // sources in generic NSAutoreleasePools. Unfortunately, there is a nested |
- // case where some bit of code can run its own event loop causing the sources |
- // to fire. In this case, code below the secondary event loop on the stack may |
- // be depending on objects above the secondary event loop on the stack. These |
- // objects above the event loop may have autorelease called on them, and then |
- // they will be released when the source completes. The code below the |
- // secondary event loop will now be pointing to freed memory. |
- // |
- // eg: |
- // (Several stack frames elided for clarity) |
- // |
- // #0 [NSWindowController autorelease] |
- // #1 DoAClose |
- // #2 MessagePumpCFRunLoopBase::DoWork() |
- // #3 [NSRunLoop run] |
- // #4 [NSButton performClick:] |
- // #5 [NSWindow sendEvent:] |
- // #6 [NSApp sendEvent:] |
- // #7 [NSApp run] |
- // |
- // PerformClick: spins a nested run loop. If the pool created in DoWork is a |
- // standard NSAutoreleasePool, it will release the objects that have been |
- // autoreleased into it once DoWork releases it. This will cause the window |
- // controller, which autoreleased itself in frame #0, to release itself, and |
- // possibly free itself. Unfortunately this window controller controls the |
- // window in frame #5. When the stack is unwound to frame #5, window no longer |
- // exists and crashes can occur. |
- // |
- // The current solution is to have a deferred autorelease pool that collects |
- // objects and then passes them up to the next autorelease pool on the |
- // autorelease pool stack when -drain is called on it. This is handled by the |
- // code in DeferredAutoReleasePool. This moves the objects to the proper |
- // autorelease pool, but doesn't drain them from that pool. |
- // |
- // The problem then becomes getting the NSApplication event loop to spin to |
- // release its autorelease pool. This is done by sending an empty event |
- // through the event loop. This is handled by |
- // MessagePumpNSAppDeferredAutoReleasePool setting |
- // a flag in MessagePumpNSApplication, which is acted on by |
- // MessagePumpNSApplication::EnterExitRunLoop. The event is sent just as the |
- // MessagePumpNSApplication's run loop is exiting so that the event isn't |
- // unintentionally swallowed by one of the other sources spinning its own |
- // event loop. |
- return [[MessagePumpNSAppDeferredAutoReleasePool alloc] initWithPump:this]; |
-} |
- |
-void MessagePumpNSApplication::WakeUpEventLoop() { |
- // If there is already an event waiting in the queue, there is no need |
- // to post another one. |
- NSString* currentMode = [[NSRunLoop currentRunLoop] currentMode]; |
- if (![NSApp nextEventMatchingMask:NSAnyEventMask |
- untilDate:nil |
- inMode:currentMode |
- dequeue:NO]) { |
- NSEvent* wakeUpEvent = [NSEvent otherEventWithType:NSApplicationDefined |
- location:NSZeroPoint |
- modifierFlags:0 |
- timestamp:0 |
- windowNumber:0 |
- context:NULL |
- subtype:0 |
- data1:0 |
- data2:0]; |
- [NSApp postEvent:wakeUpEvent atStart:NO]; |
- } |
-} |
- |
// static |
MessagePump* MessagePumpMac::Create() { |
if ([NSThread isMainThread]) { |
@@ -835,88 +698,4 @@ |
return new MessagePumpNSRunLoop; |
} |
-// A trampoline to get around problem where ObjC classes/methods cannot be |
-// friends of C++ classes. |
-void SetNeedsEventLoopWakeUpTrue(MessagePumpNSApplication* pump) { |
- pump->set_needs_event_loop_wake_up_true(); |
-} |
- |
} // namespace base |
- |
-@implementation DeferredAutoreleasePool |
- |
-- (void)addObject:(id)anObject { |
- // If the GarbageCollector is running, none of this should be necessary |
- // and it can be tossed out. |
- DCHECK([NSGarbageCollector defaultCollector] == nil); |
- if (!deferredPool_) { |
- // Create an array to use as a store for autoreleased objects. Not created |
- // in init because the existence of the pool as a flag is used to |
- // determine if the event loop needs to be sent an event to cause |
- // the app to release its autorelease pool. |
- deferredPool_ = [[NSMutableArray alloc] init]; |
- } |
- |
- // Store the object in deferredPool_. Don't call [super addObject] because the |
- // retain/release is taken care of here. |
- // When -addObject is called the retain count on anObject is 'n'. When -drain |
- // is called on a normal NSAutoreleasePool the user expects the retain count |
- // of anObject to become 'n - 1'. When anObject is added to deferredPool_ the |
- // array calls -retain so the retain count becomes 'n + 1'. Release is |
- // called to return it to 'n' so that when anObject is eventually released |
- // by the NSAutoreleasePool above self in the autorelease pool stack the |
- // retain count will become 'n - 1' as expected. |
- [deferredPool_ addObject:anObject]; |
- [anObject release]; |
- |
- // TODO(dmaclach): Is a call to |
- // NSRecordAllocationEvent(NSObjectAutoreleasedEvent, anObject) needed? I did |
- // some testing and set some breakpoints and nothing seems to call it. |
-} |
- |
-- (void)drain { |
- // Store off the address of deferredPool_ because -[super drain] calls |
- // free on "self" and deferredPool_ is needed after drain. NSAutoreleasePools |
- // are interesting in that dealloc is never called on them and instead they |
- // override release and call free directly. This means that deferredPool_ is |
- // still alive, and would be leaked if we didn't release it, but the memory |
- // owned by self is dead so even though what deferredPool_ pointed to before |
- // calling -drain is still valid, the actual pointer value of deferredPool_ |
- // is potentially garbage. By caching a copy of the pointer to deferredPool_ |
- // on the stack before calling drain a good reference to deferredPool_ is |
- // available to be used post [super drain]. |
- NSMutableArray* deferredPool = deferredPool_; |
- [super drain]; |
- |
- // This autorelease pool is now off the autorelease stack. Calling |
- // autorelease on deferredPool effectively transfers ownership of the |
- // objects in the array to the outer autorelease pool. |
- [deferredPool autorelease]; |
-} |
- |
-// Must call drain on DeferredAutoreleasePools. |
-- (oneway void)release { |
- CHECK(false); |
-} |
- |
-@end |
- |
-@implementation MessagePumpNSAppDeferredAutoReleasePool |
- |
-- (id)initWithPump:(base::MessagePumpNSApplication*)pump { |
- if ((self = [super init])) { |
- pump_ = pump; |
- } |
- return self; |
-} |
- |
-- (void)drain { |
- // Set a flag in pump_ so that it wakes up the event loop when exiting its |
- // run loop. Calling through a trampoline to get around problem where |
- // ObjC classes/methods cannot be friends of C++ classes. |
- base::SetNeedsEventLoopWakeUpTrue(pump_); |
- pump_ = nil; |
- [super drain]; |
-} |
- |
-@end |