Chromium Code Reviews| 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 |