OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 | |
OLD | NEW |