OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2008 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" | |
15 #include "base/scoped_nsautorelease_pool.h" | 14 #include "base/scoped_nsautorelease_pool.h" |
16 #include "base/scoped_nsobject.h" | |
17 #include "base/scoped_ptr.h" | |
18 #include "base/time.h" | 15 #include "base/time.h" |
19 | 16 |
20 // This pool passes the objects stored in it to the pool below it in the | |
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 | |
38 namespace { | 17 namespace { |
39 | 18 |
40 void NoOp(void* info) { | 19 void NoOp(void* info) { |
41 } | 20 } |
42 | 21 |
43 const CFTimeInterval kCFTimeIntervalMax = | 22 const CFTimeInterval kCFTimeIntervalMax = |
44 std::numeric_limits<CFTimeInterval>::max(); | 23 std::numeric_limits<CFTimeInterval>::max(); |
45 | 24 |
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 ScopedNSAutoreleasePool because having | |
49 // ScopedNSAutoreleasePool(NSAutoreleasePool*) would just clutter | |
50 // ScopedNSAutoreleasePool'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 | |
64 } // namespace | 25 } // namespace |
65 | 26 |
66 namespace base { | 27 namespace base { |
67 | 28 |
68 // Must be called on the run loop thread. | 29 // Must be called on the run loop thread. |
69 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() | 30 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() |
70 : delegate_(NULL), | 31 : delegate_(NULL), |
71 delayed_work_fire_time_(kCFTimeIntervalMax), | 32 delayed_work_fire_time_(kCFTimeIntervalMax), |
72 nesting_level_(0), | 33 nesting_level_(0), |
73 run_nesting_level_(0), | 34 run_nesting_level_(0), |
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
293 // Called by MessagePumpCFRunLoopBase::RunWorkSource. | 254 // Called by MessagePumpCFRunLoopBase::RunWorkSource. |
294 bool MessagePumpCFRunLoopBase::RunWork() { | 255 bool MessagePumpCFRunLoopBase::RunWork() { |
295 if (!delegate_) { | 256 if (!delegate_) { |
296 // This point can be reached with a NULL delegate_ if Run is not on the | 257 // This point can be reached with a NULL delegate_ if Run is not on the |
297 // stack but foreign code is spinning the CFRunLoop. Arrange to come back | 258 // stack but foreign code is spinning the CFRunLoop. Arrange to come back |
298 // here when a delegate is available. | 259 // here when a delegate is available. |
299 delegateless_work_ = true; | 260 delegateless_work_ = true; |
300 return false; | 261 return false; |
301 } | 262 } |
302 | 263 |
303 AutoreleasePoolOwner pool(CreateAutoreleasePool()); | 264 // The NSApplication-based run loop only drains the autorelease pool at each |
| 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; |
304 | 270 |
305 // Call DoWork once, and if something was done, arrange to come back here | 271 // Call DoWork once, and if something was done, arrange to come back here |
306 // again as long as the loop is still running. | 272 // again as long as the loop is still running. |
307 bool did_work = delegate_->DoWork(); | 273 bool did_work = delegate_->DoWork(); |
308 if (did_work) { | 274 if (did_work) { |
309 CFRunLoopSourceSignal(work_source_); | 275 CFRunLoopSourceSignal(work_source_); |
310 } | 276 } |
311 | 277 |
312 return did_work; | 278 return did_work; |
313 } | 279 } |
314 | 280 |
315 // Called from the run loop. | 281 // Called from the run loop. |
316 // static | 282 // static |
317 void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) { | 283 void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) { |
318 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | 284 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
319 self->RunDelayedWork(); | 285 self->RunDelayedWork(); |
320 } | 286 } |
321 | 287 |
322 // Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource. | 288 // Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource. |
323 bool MessagePumpCFRunLoopBase::RunDelayedWork() { | 289 bool MessagePumpCFRunLoopBase::RunDelayedWork() { |
324 if (!delegate_) { | 290 if (!delegate_) { |
325 // This point can be reached with a NULL delegate_ if Run is not on the | 291 // This point can be reached with a NULL delegate_ if Run is not on the |
326 // stack but foreign code is spinning the CFRunLoop. Arrange to come back | 292 // stack but foreign code is spinning the CFRunLoop. Arrange to come back |
327 // here when a delegate is available. | 293 // here when a delegate is available. |
328 delegateless_delayed_work_ = true; | 294 delegateless_delayed_work_ = true; |
329 return false; | 295 return false; |
330 } | 296 } |
331 | 297 |
332 AutoreleasePoolOwner pool(CreateAutoreleasePool()); | 298 // The NSApplication-based run loop only drains the autorelease pool at each |
| 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; |
333 | 304 |
334 Time next_time; | 305 Time next_time; |
335 delegate_->DoDelayedWork(&next_time); | 306 delegate_->DoDelayedWork(&next_time); |
336 | 307 |
337 bool more_work = !next_time.is_null(); | 308 bool more_work = !next_time.is_null(); |
338 if (more_work) { | 309 if (more_work) { |
339 TimeDelta delay = next_time - Time::Now(); | 310 TimeDelta delay = next_time - Time::Now(); |
340 if (delay > TimeDelta()) { | 311 if (delay > TimeDelta()) { |
341 // There's more delayed work to be done in the future. | 312 // There's more delayed work to be done in the future. |
342 ScheduleDelayedWork(next_time); | 313 ScheduleDelayedWork(next_time); |
(...skipping 18 matching lines...) Expand all Loading... |
361 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. | 332 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. |
362 bool MessagePumpCFRunLoopBase::RunIdleWork() { | 333 bool MessagePumpCFRunLoopBase::RunIdleWork() { |
363 if (!delegate_) { | 334 if (!delegate_) { |
364 // This point can be reached with a NULL delegate_ if Run is not on the | 335 // This point can be reached with a NULL delegate_ if Run is not on the |
365 // stack but foreign code is spinning the CFRunLoop. Arrange to come back | 336 // stack but foreign code is spinning the CFRunLoop. Arrange to come back |
366 // here when a delegate is available. | 337 // here when a delegate is available. |
367 delegateless_idle_work_ = true; | 338 delegateless_idle_work_ = true; |
368 return false; | 339 return false; |
369 } | 340 } |
370 | 341 |
371 AutoreleasePoolOwner pool(CreateAutoreleasePool()); | 342 // The NSApplication-based run loop only drains the autorelease pool at each |
| 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; |
372 | 348 |
373 // Call DoIdleWork once, and if something was done, arrange to come back here | 349 // Call DoIdleWork once, and if something was done, arrange to come back here |
374 // again as long as the loop is still running. | 350 // again as long as the loop is still running. |
375 bool did_work = delegate_->DoIdleWork(); | 351 bool did_work = delegate_->DoIdleWork(); |
376 if (did_work) { | 352 if (did_work) { |
377 CFRunLoopSourceSignal(idle_work_source_); | 353 CFRunLoopSourceSignal(idle_work_source_); |
378 } | 354 } |
379 | 355 |
380 return did_work; | 356 return did_work; |
381 } | 357 } |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
572 default: | 548 default: |
573 break; | 549 break; |
574 } | 550 } |
575 } | 551 } |
576 | 552 |
577 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default | 553 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default |
578 // implementation is a no-op. | 554 // implementation is a no-op. |
579 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { | 555 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { |
580 } | 556 } |
581 | 557 |
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 | |
590 MessagePumpCFRunLoop::MessagePumpCFRunLoop() | 558 MessagePumpCFRunLoop::MessagePumpCFRunLoop() |
591 : quit_pending_(false) { | 559 : quit_pending_(false) { |
592 } | 560 } |
593 | 561 |
594 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were | 562 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were |
595 // running lower on the run loop thread's stack when this object was created, | 563 // running lower on the run loop thread's stack when this object was created, |
596 // the same number of CFRunLoopRun loops must be running for the outermost call | 564 // the same number of CFRunLoopRun loops must be running for the outermost call |
597 // to Run. Run/DoRun are reentrant after that point. | 565 // to Run. Run/DoRun are reentrant after that point. |
598 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { | 566 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { |
599 // This is completely identical to calling CFRunLoopRun(), except autorelease | 567 // This is completely identical to calling CFRunLoopRun(), except autorelease |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
663 } | 631 } |
664 | 632 |
665 void MessagePumpNSRunLoop::Quit() { | 633 void MessagePumpNSRunLoop::Quit() { |
666 keep_running_ = false; | 634 keep_running_ = false; |
667 CFRunLoopSourceSignal(quit_source_); | 635 CFRunLoopSourceSignal(quit_source_); |
668 CFRunLoopWakeUp(run_loop()); | 636 CFRunLoopWakeUp(run_loop()); |
669 } | 637 } |
670 | 638 |
671 MessagePumpNSApplication::MessagePumpNSApplication() | 639 MessagePumpNSApplication::MessagePumpNSApplication() |
672 : keep_running_(true), | 640 : keep_running_(true), |
673 running_own_loop_(false), | 641 running_own_loop_(false) { |
674 needs_event_loop_wake_up_(false) { | |
675 } | 642 } |
676 | 643 |
677 void MessagePumpNSApplication::DoRun(Delegate* delegate) { | 644 void MessagePumpNSApplication::DoRun(Delegate* delegate) { |
678 bool last_running_own_loop_ = running_own_loop_; | 645 bool last_running_own_loop_ = running_own_loop_; |
679 | 646 |
680 [NSApplication sharedApplication]; | 647 [NSApplication sharedApplication]; |
681 | 648 |
682 if (![NSApp isRunning]) { | 649 if (![NSApp isRunning]) { |
683 running_own_loop_ = false; | 650 running_own_loop_ = false; |
684 // NSApplication manages autorelease pools itself when run this way. | 651 // NSApplication manages autorelease pools itself when run this way. |
(...skipping 17 matching lines...) Expand all Loading... |
702 running_own_loop_ = last_running_own_loop_; | 669 running_own_loop_ = last_running_own_loop_; |
703 } | 670 } |
704 | 671 |
705 void MessagePumpNSApplication::Quit() { | 672 void MessagePumpNSApplication::Quit() { |
706 if (!running_own_loop_) { | 673 if (!running_own_loop_) { |
707 [NSApp stop:nil]; | 674 [NSApp stop:nil]; |
708 } else { | 675 } else { |
709 keep_running_ = false; | 676 keep_running_ = false; |
710 } | 677 } |
711 | 678 |
712 WakeUpEventLoop(); | 679 // Send a fake event to wake the loop up. |
713 } | 680 [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined |
714 | 681 location:NSMakePoint(0, 0) |
715 // Called by MessagePumpCFRunLoopBase::EnterExitObserver. | 682 modifierFlags:0 |
716 void MessagePumpNSApplication::EnterExitRunLoop(CFRunLoopActivity activity) { | 683 timestamp:0 |
717 if (activity == kCFRunLoopExit && needs_event_loop_wake_up_) { | 684 windowNumber:0 |
718 WakeUpEventLoop(); | 685 context:NULL |
719 needs_event_loop_wake_up_ = false; | 686 subtype:0 |
720 } | 687 data1:0 |
721 } | 688 data2:0] |
722 | 689 atStart:NO]; |
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. The event is sent just as the | |
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 } | |
827 } | 690 } |
828 | 691 |
829 // static | 692 // static |
830 MessagePump* MessagePumpMac::Create() { | 693 MessagePump* MessagePumpMac::Create() { |
831 if ([NSThread isMainThread]) { | 694 if ([NSThread isMainThread]) { |
832 return new MessagePumpNSApplication; | 695 return new MessagePumpNSApplication; |
833 } | 696 } |
834 | 697 |
835 return new MessagePumpNSRunLoop; | 698 return new MessagePumpNSRunLoop; |
836 } | 699 } |
837 | 700 |
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_wake_up_true(); | |
842 } | |
843 | |
844 } // namespace base | 701 } // 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 @implementation MessagePumpNSAppDeferredAutoReleasePool | |
905 | |
906 - (id)initWithPump:(base::MessagePumpNSApplication*)pump { | |
907 if ((self = [super init])) { | |
908 pump_ = pump; | |
909 } | |
910 return self; | |
911 } | |
912 | |
913 - (void)drain { | |
914 // Set a flag in pump_ so that it wakes up the event loop when exiting its | |
915 // run loop. Calling through a trampoline to get around problem where | |
916 // ObjC classes/methods cannot be friends of C++ classes. | |
917 base::SetNeedsEventLoopWakeUpTrue(pump_); | |
918 pump_ = nil; | |
919 [super drain]; | |
920 } | |
921 | |
922 @end | |
OLD | NEW |