Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(610)

Side by Side Diff: base/message_pump_mac.mm

Issue 343024: Cleans up our autorelease handling so that we don't create a layered ... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 11 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/message_pump_mac.h" 5 #include "base/message_pump_mac.h"
6 6
7 #import <AppKit/AppKit.h> 7 #import <AppKit/AppKit.h>
8 #import <Foundation/Foundation.h> 8 #import <Foundation/Foundation.h>
9 #include <IOKit/IOMessage.h> 9 #include <IOKit/IOMessage.h>
10 #include <IOKit/pwr_mgt/IOPMLib.h> 10 #include <IOKit/pwr_mgt/IOPMLib.h>
11 11
12 #include <limits> 12 #include <limits>
13 13
14 #include "base/logging.h"
14 #include "base/scoped_nsautorelease_pool.h" 15 #include "base/scoped_nsautorelease_pool.h"
16 #include "base/scoped_nsobject.h"
17 #include "base/scoped_ptr.h"
15 #include "base/time.h" 18 #include "base/time.h"
16 19
20 // This pool passes the objects stored in it to the pool above it in the
Mark Mentovai 2009/10/30 21:47:04 pool above or pool below? Stacks are usually desc
21 // autorelease pool stack when -drain is called. See its implementation
22 // for all the gory details.
23 @interface DeferredAutoreleasePool : NSAutoreleasePool {
24 @private
25 NSMutableArray* deferredPool_; // Strong.
26 }
27 @end
28
29 // Informs the message pump that it has been drained.
30 @interface MessagePumpNSAppDeferredAutoReleasePool : DeferredAutoreleasePool {
31 @private
32 base::MessagePumpNSApplication* pump_; // Weak.
33 }
34
35 - (id)initWithPump:(base::MessagePumpNSApplication*)pump;
36 @end
37
17 namespace { 38 namespace {
18 39
19 void NoOp(void* info) { 40 void NoOp(void* info) {
20 } 41 }
21 42
22 const CFTimeInterval kCFTimeIntervalMax = 43 const CFTimeInterval kCFTimeIntervalMax =
23 std::numeric_limits<CFTimeInterval>::max(); 44 std::numeric_limits<CFTimeInterval>::max();
24 45
46 // Class for dealing with scoping autorelease pools when they need to be a
47 // special implementation of NSAutoreleasePool.
48 // This class is independant from scoped_nsautoreleasepool because having
Mark Mentovai 2009/10/30 21:47:04 The name of the class is actually ScopedNSAutorele
49 // scoped_nsautoreleasepool(NSAutoreleasePool*) would just clutter
50 // scoped_nsautoreleasepool's interface, potentially leading to misuse, and the
51 // case where using an externally created instance of a kind of
52 // NSAutoreleasePool is so rare.
53 class AutoreleasePoolOwner {
54 public:
55 explicit AutoreleasePoolOwner(NSAutoreleasePool *pool) : pool_(pool) {}
56 ~AutoreleasePoolOwner() {
57 [pool_ drain];
58 }
59 private:
60 NSAutoreleasePool *pool_;
61 DISALLOW_COPY_AND_ASSIGN(AutoreleasePoolOwner);
62 };
63
25 } // namespace 64 } // namespace
26 65
27 namespace base { 66 namespace base {
28 67
29 // Must be called on the run loop thread. 68 // Must be called on the run loop thread.
30 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() 69 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
31 : delegate_(NULL), 70 : delegate_(NULL),
32 delayed_work_fire_time_(kCFTimeIntervalMax), 71 delayed_work_fire_time_(kCFTimeIntervalMax),
33 nesting_level_(0), 72 nesting_level_(0),
34 run_nesting_level_(0), 73 run_nesting_level_(0),
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after
254 // Called by MessagePumpCFRunLoopBase::RunWorkSource. 293 // Called by MessagePumpCFRunLoopBase::RunWorkSource.
255 bool MessagePumpCFRunLoopBase::RunWork() { 294 bool MessagePumpCFRunLoopBase::RunWork() {
256 if (!delegate_) { 295 if (!delegate_) {
257 // This point can be reached with a NULL delegate_ if Run is not on the 296 // This point can be reached with a NULL delegate_ if Run is not on the
258 // stack but foreign code is spinning the CFRunLoop. Arrange to come back 297 // stack but foreign code is spinning the CFRunLoop. Arrange to come back
259 // here when a delegate is available. 298 // here when a delegate is available.
260 delegateless_work_ = true; 299 delegateless_work_ = true;
261 return false; 300 return false;
262 } 301 }
263 302
264 // The NSApplication-based run loop only drains the autorelease pool at each 303 AutoreleasePoolOwner pool(CreateAutoreleasePool());
265 // UI event (NSEvent). The autorelease pool is not drained for each
266 // CFRunLoopSource target that's run. Use a local pool for any autoreleased
267 // objects to ensure they're released promptly even in the absence of UI
268 // events.
269 ScopedNSAutoreleasePool autorelease_pool;
270 304
271 // Call DoWork once, and if something was done, arrange to come back here 305 // Call DoWork once, and if something was done, arrange to come back here
272 // again as long as the loop is still running. 306 // again as long as the loop is still running.
273 bool did_work = delegate_->DoWork(); 307 bool did_work = delegate_->DoWork();
274 if (did_work) { 308 if (did_work) {
275 CFRunLoopSourceSignal(work_source_); 309 CFRunLoopSourceSignal(work_source_);
276 } 310 }
277 311
278 return did_work; 312 return did_work;
279 } 313 }
280 314
281 // Called from the run loop. 315 // Called from the run loop.
282 // static 316 // static
283 void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) { 317 void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) {
284 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 318 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
285 self->RunDelayedWork(); 319 self->RunDelayedWork();
286 } 320 }
287 321
288 // Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource. 322 // Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource.
289 bool MessagePumpCFRunLoopBase::RunDelayedWork() { 323 bool MessagePumpCFRunLoopBase::RunDelayedWork() {
290 if (!delegate_) { 324 if (!delegate_) {
291 // This point can be reached with a NULL delegate_ if Run is not on the 325 // This point can be reached with a NULL delegate_ if Run is not on the
292 // stack but foreign code is spinning the CFRunLoop. Arrange to come back 326 // stack but foreign code is spinning the CFRunLoop. Arrange to come back
293 // here when a delegate is available. 327 // here when a delegate is available.
294 delegateless_delayed_work_ = true; 328 delegateless_delayed_work_ = true;
295 return false; 329 return false;
296 } 330 }
297 331
298 // The NSApplication-based run loop only drains the autorelease pool at each 332 AutoreleasePoolOwner pool(CreateAutoreleasePool());
299 // UI event (NSEvent). The autorelease pool is not drained for each
300 // CFRunLoopSource target that's run. Use a local pool for any autoreleased
301 // objects to ensure they're released promptly even in the absence of UI
302 // events.
303 ScopedNSAutoreleasePool autorelease_pool;
304 333
305 Time next_time; 334 Time next_time;
306 delegate_->DoDelayedWork(&next_time); 335 delegate_->DoDelayedWork(&next_time);
307 336
308 bool more_work = !next_time.is_null(); 337 bool more_work = !next_time.is_null();
309 if (more_work) { 338 if (more_work) {
310 TimeDelta delay = next_time - Time::Now(); 339 TimeDelta delay = next_time - Time::Now();
311 if (delay > TimeDelta()) { 340 if (delay > TimeDelta()) {
312 // There's more delayed work to be done in the future. 341 // There's more delayed work to be done in the future.
313 ScheduleDelayedWork(next_time); 342 ScheduleDelayedWork(next_time);
(...skipping 18 matching lines...) Expand all
332 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. 361 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource.
333 bool MessagePumpCFRunLoopBase::RunIdleWork() { 362 bool MessagePumpCFRunLoopBase::RunIdleWork() {
334 if (!delegate_) { 363 if (!delegate_) {
335 // This point can be reached with a NULL delegate_ if Run is not on the 364 // This point can be reached with a NULL delegate_ if Run is not on the
336 // stack but foreign code is spinning the CFRunLoop. Arrange to come back 365 // stack but foreign code is spinning the CFRunLoop. Arrange to come back
337 // here when a delegate is available. 366 // here when a delegate is available.
338 delegateless_idle_work_ = true; 367 delegateless_idle_work_ = true;
339 return false; 368 return false;
340 } 369 }
341 370
342 // The NSApplication-based run loop only drains the autorelease pool at each 371 AutoreleasePoolOwner pool(CreateAutoreleasePool());
343 // UI event (NSEvent). The autorelease pool is not drained for each
344 // CFRunLoopSource target that's run. Use a local pool for any autoreleased
345 // objects to ensure they're released promptly even in the absence of UI
346 // events.
347 ScopedNSAutoreleasePool autorelease_pool;
348 372
349 // Call DoIdleWork once, and if something was done, arrange to come back here 373 // Call DoIdleWork once, and if something was done, arrange to come back here
350 // again as long as the loop is still running. 374 // again as long as the loop is still running.
351 bool did_work = delegate_->DoIdleWork(); 375 bool did_work = delegate_->DoIdleWork();
352 if (did_work) { 376 if (did_work) {
353 CFRunLoopSourceSignal(idle_work_source_); 377 CFRunLoopSourceSignal(idle_work_source_);
354 } 378 }
355 379
356 return did_work; 380 return did_work;
357 } 381 }
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
548 default: 572 default:
549 break; 573 break;
550 } 574 }
551 } 575 }
552 576
553 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default 577 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default
554 // implementation is a no-op. 578 // implementation is a no-op.
555 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { 579 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
556 } 580 }
557 581
582 // Called by MessagePumpCFRunLoopBase::RunWork,
583 // MessagePumpCFRunLoopBase::RunDelayedWork and
584 // MessagePumpCFRunLoopBase::RunIdleWork. Default implementation is a standard
585 // NSAutoreleasePool that can be used for eash source as it fires.
586 NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
587 return [[NSAutoreleasePool alloc] init];
588 }
589
558 MessagePumpCFRunLoop::MessagePumpCFRunLoop() 590 MessagePumpCFRunLoop::MessagePumpCFRunLoop()
559 : quit_pending_(false) { 591 : quit_pending_(false) {
560 } 592 }
561 593
562 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were 594 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were
563 // running lower on the run loop thread's stack when this object was created, 595 // running lower on the run loop thread's stack when this object was created,
564 // the same number of CFRunLoopRun loops must be running for the outermost call 596 // the same number of CFRunLoopRun loops must be running for the outermost call
565 // to Run. Run/DoRun are reentrant after that point. 597 // to Run. Run/DoRun are reentrant after that point.
566 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { 598 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) {
567 // This is completely identical to calling CFRunLoopRun(), except autorelease 599 // This is completely identical to calling CFRunLoopRun(), except autorelease
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
631 } 663 }
632 664
633 void MessagePumpNSRunLoop::Quit() { 665 void MessagePumpNSRunLoop::Quit() {
634 keep_running_ = false; 666 keep_running_ = false;
635 CFRunLoopSourceSignal(quit_source_); 667 CFRunLoopSourceSignal(quit_source_);
636 CFRunLoopWakeUp(run_loop()); 668 CFRunLoopWakeUp(run_loop());
637 } 669 }
638 670
639 MessagePumpNSApplication::MessagePumpNSApplication() 671 MessagePumpNSApplication::MessagePumpNSApplication()
640 : keep_running_(true), 672 : keep_running_(true),
641 running_own_loop_(false) { 673 running_own_loop_(false),
674 needs_event_loop_wake_up_(false) {
642 } 675 }
643 676
644 void MessagePumpNSApplication::DoRun(Delegate* delegate) { 677 void MessagePumpNSApplication::DoRun(Delegate* delegate) {
645 bool last_running_own_loop_ = running_own_loop_; 678 bool last_running_own_loop_ = running_own_loop_;
646 679
647 [NSApplication sharedApplication]; 680 [NSApplication sharedApplication];
648 681
649 if (![NSApp isRunning]) { 682 if (![NSApp isRunning]) {
650 running_own_loop_ = false; 683 running_own_loop_ = false;
651 // NSApplication manages autorelease pools itself when run this way. 684 // NSApplication manages autorelease pools itself when run this way.
(...skipping 17 matching lines...) Expand all
669 running_own_loop_ = last_running_own_loop_; 702 running_own_loop_ = last_running_own_loop_;
670 } 703 }
671 704
672 void MessagePumpNSApplication::Quit() { 705 void MessagePumpNSApplication::Quit() {
673 if (!running_own_loop_) { 706 if (!running_own_loop_) {
674 [NSApp stop:nil]; 707 [NSApp stop:nil];
675 } else { 708 } else {
676 keep_running_ = false; 709 keep_running_ = false;
677 } 710 }
678 711
679 // Send a fake event to wake the loop up. 712 WakeUpEventLoop();
680 [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined 713 }
681 location:NSMakePoint(0, 0) 714
682 modifierFlags:0 715 // Called by MessagePumpCFRunLoopBase::EnterExitObserver.
683 timestamp:0 716 void MessagePumpNSApplication::EnterExitRunLoop(CFRunLoopActivity activity) {
684 windowNumber:0 717 if (activity == kCFRunLoopExit && needs_event_loop_wake_up_) {
685 context:NULL 718 WakeUpEventLoop();
686 subtype:0 719 needs_event_loop_wake_up_ = false;
687 data1:0 720 }
688 data2:0] 721 }
689 atStart:NO]; 722
723 NSAutoreleasePool* MessagePumpNSApplication::CreateAutoreleasePool() {
724 // Return an instance of a special autorelease pool that deals
725 // correctly with nested autorelease pools in run loops. This is used by
726 // MessagePumpNSApplication instead of a standard NSAutoreleasePool
727 // because of how NSApplication interacts with CFRunLoops.
728 //
729 // A standard NSApplication event loop looks something like this:
730 //
731 // - (void)run {
732 // while ([self isRunning]) {
733 // NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
734 // NSEvent* event = [self nextEventMatchingMask:NSAnyEventMask
735 // untilDate:[NSDate distantFuture]
736 // inMode:NSDefaultRunLoopMode
737 // dequeue:YES];
738 // [self sendEvent:event];
739 // [self updateWindows];
740 // [pool release];
741 // }
742 // }
743 //
744 // Notice that the autorelease pool only gets cleaned up when an NSEvent gets
745 // returned by nextEventMatchingMask:untilDate:inMode:dequeue: as this is an
746 // an important point in getting objects released in a timely manner.
747 //
748 // Message pumps have a variety of CFRunLoopSources attached to their
749 // CFRunLoops. These sources can cause actions to run from within
750 // [NSApplication -nextEventMatchingMask:untilDate:inMode:dequeue:]. These
751 // actions can call into Objective C code when they fire and add things to the
752 // autorelease pool.
753 //
754 // The problem is that the sources don't necessarily cause
755 // an NSEvent to be generated and therefore the autorelease pool in
756 // [NSApplication run] is not necessarily released in a timely fashion. This
757 // potentially leaves a lot of objects that can be cleaned up sitting around
758 // taking up memory until something, such as an NSEvent, causes [NSApplication
759 // run] to release its pool.
760 //
761 // The easy answer would seem to be to wrap all of the calls from the
762 // sources in generic NSAutoreleasePools. Unfortunately, there is a nested
763 // case where some bit of code can run its own event loop causing the sources
764 // to fire. In this case, code below the secondary event loop on the stack may
765 // be depending on objects above the secondary event loop on the stack. These
766 // objects above the event loop may have autorelease called on them, and then
767 // they will be released when the source completes. The code below the
768 // secondary event loop will now be pointing to freed memory.
769 //
770 // eg:
771 // (Several stack frames elided for clarity)
772 //
773 // #0 [NSWindowController autorelease]
774 // #1 DoAClose
775 // #2 MessagePumpCFRunLoopBase::DoWork()
776 // #3 [NSRunLoop run]
777 // #4 [NSButton performClick:]
778 // #5 [NSWindow sendEvent:]
779 // #6 [NSApp sendEvent:]
780 // #7 [NSApp run]
781 //
782 // PerformClick: spins a nested run loop. If the pool created in DoWork is a
783 // standard NSAutoreleasePool, it will release the objects that have been
784 // autoreleased into it once DoWork releases it. This will cause the window
785 // controller, which autoreleased itself in frame #0, to release itself, and
786 // possibly free itself. Unfortunately this window controller controls the
787 // window in frame #5. When the stack is unwound to frame #5, window no longer
788 // exists and crashes can occur.
789 //
790 // The current solution is to have a deferred autorelease pool that collects
791 // objects and then passes them up to the next autorelease pool on the
792 // autorelease pool stack when -drain is called on it. This is handled by the
793 // code in DeferredAutoReleasePool. This moves the objects to the proper
794 // autorelease pool, but doesn't drain them from that pool.
795 //
796 // The problem then becomes getting the NSApplication event loop to spin to
797 // release its autorelease pool. This is done by sending an empty event
798 // through the event loop. This is handled by
799 // MessagePumpNSAppDeferredAutoReleasePool setting
800 // a flag in MessagePumpNSApplication, which is acted on by
801 // MessagePumpNSApplication::EnterExitRunLoop. We send the event just as the
Mark Mentovai 2009/10/30 21:47:04 Final we.
802 // MessagePumpNSApplication's run loop is exiting so that the event isn't
803 // unintentionally swallowed by one of the other sources spinning its own
804 // event loop.
805 return [[MessagePumpNSAppDeferredAutoReleasePool alloc] initWithPump:this];
806 }
807
808 void MessagePumpNSApplication::WakeUpEventLoop() {
809 // If there is already an event waiting in the queue, there is no need
810 // to post another one.
811 NSString* currentMode = [[NSRunLoop currentRunLoop] currentMode];
812 if (![NSApp nextEventMatchingMask:NSAnyEventMask
813 untilDate:nil
814 inMode:currentMode
815 dequeue:NO]) {
816 NSEvent* wakeUpEvent = [NSEvent otherEventWithType:NSApplicationDefined
817 location:NSZeroPoint
818 modifierFlags:0
819 timestamp:0
820 windowNumber:0
821 context:NULL
822 subtype:0
823 data1:0
824 data2:0];
825 [NSApp postEvent:wakeUpEvent atStart:NO];
826 }
690 } 827 }
691 828
692 // static 829 // static
693 MessagePump* MessagePumpMac::Create() { 830 MessagePump* MessagePumpMac::Create() {
694 if ([NSThread isMainThread]) { 831 if ([NSThread isMainThread]) {
695 return new MessagePumpNSApplication; 832 return new MessagePumpNSApplication;
696 } 833 }
697 834
698 return new MessagePumpNSRunLoop; 835 return new MessagePumpNSRunLoop;
699 } 836 }
700 837
838 // A trampoline to get around problem where ObjC classes/methods cannot be
839 // friends of C++ classes.
840 void SetNeedsEventLoopWakeUpTrue(MessagePumpNSApplication* pump) {
841 pump->set_needs_event_loop_wakeup_true();
842 }
843
701 } // namespace base 844 } // namespace base
845
846 @implementation DeferredAutoreleasePool
847
848 - (void)addObject:(id)anObject {
849 // If the GarbageCollector is running, none of this should be necessary
850 // and it can be tossed out.
851 DCHECK([NSGarbageCollector defaultCollector] == nil);
852 if (!deferredPool_) {
853 // Create an array to use as a store for autoreleased objects. Not created
854 // in init because the existence of the pool as a flag is used to
855 // determine if the event loop needs to be sent an event to cause
856 // the app to release its autorelease pool.
857 deferredPool_ = [[NSMutableArray alloc] init];
858 }
859
860 // Store the object in deferredPool_. Don't call [super addObject] because the
861 // retain/release is taken care of here.
862 // When -addObject is called the retain count on anObject is 'n'. When -drain
863 // is called on a normal NSAutoreleasePool the user expects the retain count
864 // of anObject to become 'n - 1'. When anObject is added to deferredPool_ the
865 // array calls -retain so the retain count becomes 'n + 1'. Release is
866 // called to return it to 'n' so that when anObject is eventually released
867 // by the NSAutoreleasePool above self in the autorelease pool stack the
868 // retain count will become 'n - 1' as expected.
869 [deferredPool_ addObject:anObject];
870 [anObject release];
871
872 // TODO(dmaclach): Is a call to
873 // NSRecordAllocationEvent(NSObjectAutoreleasedEvent, anObject) needed? I did
874 // some testing and set some breakpoints and nothing seems to call it.
875 }
876
877 - (void)drain {
878 // Store off the address of deferredPool_ because -[super drain] calls
879 // free on "self" and deferredPool_ is needed after drain. NSAutoreleasePools
880 // are interesting in that dealloc is never called on them and instead they
881 // override release and call free directly. This means that deferredPool_ is
882 // still alive, and would be leaked if we didn't release it, but the memory
883 // owned by self is dead so even though what deferredPool_ pointed to before
884 // calling -drain is still valid, the actual pointer value of deferredPool_
885 // is potentially garbage. By caching a copy of the pointer to deferredPool_
886 // on the stack before calling drain a good reference to deferredPool_ is
887 // available to be used post [super drain].
888 NSMutableArray* deferredPool = deferredPool_;
889 [super drain];
890
891 // This autorelease pool is now off the autorelease stack. Calling
892 // autorelease on deferredPool effectively transfers ownership of the
893 // objects in the array to the outer autorelease pool.
894 [deferredPool autorelease];
895 }
896
897 // Must call drain on DeferredAutoreleasePools.
898 - (oneway void)release {
899 CHECK(false);
900 }
901
902 @end
903
904
905 @implementation MessagePumpNSAppDeferredAutoReleasePool
906
907 - (id)initWithPump:(base::MessagePumpNSApplication*)pump {
908 if ((self = [super init])) {
909 pump_ = pump;
910 }
911 return self;
912 }
913
914 - (void)drain {
915 // Set a flag in pump_ so that it wakes up the event loop when exiting its
916 // run loop. Calling through a trampoline to get around problem where
917 // ObjC classes/methods cannot be friends of C++ classes.
918 base::SetNeedsEventLoopWakeUpTrue(pump_);
919 pump_ = nil;
920 [super drain];
921 }
922
923 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698