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

Side by Side Diff: base/message_pump_mac.mm

Issue 345051: 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
« no previous file with comments | « base/chrome_application_mac.mm ('k') | chrome/app/app-Info.plist » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2008 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/scoped_nsautorelease_pool.h" 14 #import "base/chrome_application_mac.h"
15 #include "base/logging.h"
15 #include "base/time.h" 16 #include "base/time.h"
16 17
17 namespace { 18 namespace {
18 19
19 void NoOp(void* info) { 20 void NoOp(void* info) {
20 } 21 }
21 22
22 const CFTimeInterval kCFTimeIntervalMax = 23 const CFTimeInterval kCFTimeIntervalMax =
23 std::numeric_limits<CFTimeInterval>::max(); 24 std::numeric_limits<CFTimeInterval>::max();
24 25
25 } // namespace 26 } // namespace
26 27
27 namespace base { 28 namespace base {
28 29
30 // A scoper for autorelease pools created from message pump run loops.
31 // Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare
32 // case where an autorelease pool needs to be passed in.
33 class MessagePumpScopedAutoreleasePool {
34 public:
35 explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) :
36 pool_(pump->CreateAutoreleasePool()) {
Mark Mentovai 2009/11/05 20:28:40 Final really tiny minor insignificant nit that you
37 }
38 ~MessagePumpScopedAutoreleasePool() {
Scott Hess - ex-Googler 2009/11/05 20:36:12 Nit: indentation.
39 [pool_ drain];
40 }
41
42 private:
43 NSAutoreleasePool* pool_;
44 DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool);
45 };
46
29 // Must be called on the run loop thread. 47 // Must be called on the run loop thread.
30 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() 48 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
31 : delegate_(NULL), 49 : delegate_(NULL),
32 delayed_work_fire_time_(kCFTimeIntervalMax), 50 delayed_work_fire_time_(kCFTimeIntervalMax),
33 nesting_level_(0), 51 nesting_level_(0),
34 run_nesting_level_(0), 52 run_nesting_level_(0),
35 deepest_nesting_level_(0), 53 deepest_nesting_level_(0),
36 delegateless_work_(false), 54 delegateless_work_(false),
37 delegateless_delayed_work_(false), 55 delegateless_delayed_work_(false),
38 delegateless_idle_work_(false) { 56 delegateless_idle_work_(false) {
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
257 // This point can be reached with a NULL delegate_ if Run is not on the 275 // 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 276 // stack but foreign code is spinning the CFRunLoop. Arrange to come back
259 // here when a delegate is available. 277 // here when a delegate is available.
260 delegateless_work_ = true; 278 delegateless_work_ = true;
261 return false; 279 return false;
262 } 280 }
263 281
264 // The NSApplication-based run loop only drains the autorelease pool at each 282 // The NSApplication-based run loop only drains the autorelease pool at each
265 // UI event (NSEvent). The autorelease pool is not drained for each 283 // UI event (NSEvent). The autorelease pool is not drained for each
266 // CFRunLoopSource target that's run. Use a local pool for any autoreleased 284 // 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 285 // objects if the app is not currently handling a UI event to ensure they're
268 // events. 286 // released promptly even in the absence of UI events.
269 ScopedNSAutoreleasePool autorelease_pool; 287 MessagePumpScopedAutoreleasePool autorelease_pool(this);
270 288
271 // Call DoWork once, and if something was done, arrange to come back here 289 // Call DoWork once, and if something was done, arrange to come back here
272 // again as long as the loop is still running. 290 // again as long as the loop is still running.
273 bool did_work = delegate_->DoWork(); 291 bool did_work = delegate_->DoWork();
274 if (did_work) { 292 if (did_work) {
275 CFRunLoopSourceSignal(work_source_); 293 CFRunLoopSourceSignal(work_source_);
276 } 294 }
277 295
278 return did_work; 296 return did_work;
279 } 297 }
(...skipping 11 matching lines...) Expand all
291 // This point can be reached with a NULL delegate_ if Run is not on the 309 // 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 310 // stack but foreign code is spinning the CFRunLoop. Arrange to come back
293 // here when a delegate is available. 311 // here when a delegate is available.
294 delegateless_delayed_work_ = true; 312 delegateless_delayed_work_ = true;
295 return false; 313 return false;
296 } 314 }
297 315
298 // The NSApplication-based run loop only drains the autorelease pool at each 316 // The NSApplication-based run loop only drains the autorelease pool at each
299 // UI event (NSEvent). The autorelease pool is not drained for each 317 // UI event (NSEvent). The autorelease pool is not drained for each
300 // CFRunLoopSource target that's run. Use a local pool for any autoreleased 318 // 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 319 // objects if the app is not currently handling a UI event to ensure they're
302 // events. 320 // released promptly even in the absence of UI events.
303 ScopedNSAutoreleasePool autorelease_pool; 321 MessagePumpScopedAutoreleasePool autorelease_pool(this);
304 322
305 Time next_time; 323 Time next_time;
306 delegate_->DoDelayedWork(&next_time); 324 delegate_->DoDelayedWork(&next_time);
307 325
308 bool more_work = !next_time.is_null(); 326 bool more_work = !next_time.is_null();
309 if (more_work) { 327 if (more_work) {
310 TimeDelta delay = next_time - Time::Now(); 328 TimeDelta delay = next_time - Time::Now();
311 if (delay > TimeDelta()) { 329 if (delay > TimeDelta()) {
312 // There's more delayed work to be done in the future. 330 // There's more delayed work to be done in the future.
313 ScheduleDelayedWork(next_time); 331 ScheduleDelayedWork(next_time);
(...skipping 21 matching lines...) Expand all
335 // This point can be reached with a NULL delegate_ if Run is not on the 353 // 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 354 // stack but foreign code is spinning the CFRunLoop. Arrange to come back
337 // here when a delegate is available. 355 // here when a delegate is available.
338 delegateless_idle_work_ = true; 356 delegateless_idle_work_ = true;
339 return false; 357 return false;
340 } 358 }
341 359
342 // The NSApplication-based run loop only drains the autorelease pool at each 360 // The NSApplication-based run loop only drains the autorelease pool at each
343 // UI event (NSEvent). The autorelease pool is not drained for each 361 // UI event (NSEvent). The autorelease pool is not drained for each
344 // CFRunLoopSource target that's run. Use a local pool for any autoreleased 362 // 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 363 // objects if the app is not currently handling a UI event to ensure they're
346 // events. 364 // released promptly even in the absence of UI events.
347 ScopedNSAutoreleasePool autorelease_pool; 365 MessagePumpScopedAutoreleasePool autorelease_pool(this);
348 366
349 // Call DoIdleWork once, and if something was done, arrange to come back here 367 // Call DoIdleWork once, and if something was done, arrange to come back here
350 // again as long as the loop is still running. 368 // again as long as the loop is still running.
351 bool did_work = delegate_->DoIdleWork(); 369 bool did_work = delegate_->DoIdleWork();
352 if (did_work) { 370 if (did_work) {
353 CFRunLoopSourceSignal(idle_work_source_); 371 CFRunLoopSourceSignal(idle_work_source_);
354 } 372 }
355 373
356 return did_work; 374 return did_work;
357 } 375 }
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
548 default: 566 default:
549 break; 567 break;
550 } 568 }
551 } 569 }
552 570
553 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default 571 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default
554 // implementation is a no-op. 572 // implementation is a no-op.
555 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { 573 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
556 } 574 }
557 575
576 // Base version returns a standard NSAutoreleasePool.
577 NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
578 return [[NSAutoreleasePool alloc] init];
579 }
580
558 MessagePumpCFRunLoop::MessagePumpCFRunLoop() 581 MessagePumpCFRunLoop::MessagePumpCFRunLoop()
559 : quit_pending_(false) { 582 : quit_pending_(false) {
560 } 583 }
561 584
562 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were 585 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were
563 // running lower on the run loop thread's stack when this object was created, 586 // 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 587 // the same number of CFRunLoopRun loops must be running for the outermost call
565 // to Run. Run/DoRun are reentrant after that point. 588 // to Run. Run/DoRun are reentrant after that point.
566 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { 589 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) {
567 // This is completely identical to calling CFRunLoopRun(), except autorelease 590 // This is completely identical to calling CFRunLoopRun(), except autorelease
568 // pool management is introduced. 591 // pool management is introduced.
569 int result; 592 int result;
570 do { 593 do {
571 ScopedNSAutoreleasePool autorelease_pool; 594 MessagePumpScopedAutoreleasePool autorelease_pool(this);
572 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 595 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode,
573 kCFTimeIntervalMax, 596 kCFTimeIntervalMax,
574 false); 597 false);
575 } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); 598 } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished);
576 } 599 }
577 600
578 // Must be called on the run loop thread. 601 // Must be called on the run loop thread.
579 void MessagePumpCFRunLoop::Quit() { 602 void MessagePumpCFRunLoop::Quit() {
580 // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. 603 // Stop the innermost run loop managed by this MessagePumpCFRunLoop object.
581 if (nesting_level() == run_nesting_level()) { 604 if (nesting_level() == run_nesting_level()) {
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
637 } 660 }
638 661
639 MessagePumpNSApplication::MessagePumpNSApplication() 662 MessagePumpNSApplication::MessagePumpNSApplication()
640 : keep_running_(true), 663 : keep_running_(true),
641 running_own_loop_(false) { 664 running_own_loop_(false) {
642 } 665 }
643 666
644 void MessagePumpNSApplication::DoRun(Delegate* delegate) { 667 void MessagePumpNSApplication::DoRun(Delegate* delegate) {
645 bool last_running_own_loop_ = running_own_loop_; 668 bool last_running_own_loop_ = running_own_loop_;
646 669
647 [NSApplication sharedApplication]; 670 // TODO(dmaclach): Get rid of this gratuitous sharedApplication.
671 // Tests should be setting up their applications on their own.
672 [CrApplication sharedApplication];
648 673
649 if (![NSApp isRunning]) { 674 if (![NSApp isRunning]) {
650 running_own_loop_ = false; 675 running_own_loop_ = false;
651 // NSApplication manages autorelease pools itself when run this way. 676 // NSApplication manages autorelease pools itself when run this way.
652 [NSApp run]; 677 [NSApp run];
653 } else { 678 } else {
654 running_own_loop_ = true; 679 running_own_loop_ = true;
655 NSDate* distant_future = [NSDate distantFuture]; 680 NSDate* distant_future = [NSDate distantFuture];
656 while (keep_running_) { 681 while (keep_running_) {
657 ScopedNSAutoreleasePool autorelease_pool; 682 MessagePumpScopedAutoreleasePool autorelease_pool(this);
658 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask 683 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
659 untilDate:distant_future 684 untilDate:distant_future
660 inMode:NSDefaultRunLoopMode 685 inMode:NSDefaultRunLoopMode
661 dequeue:YES]; 686 dequeue:YES];
662 if (event) { 687 if (event) {
663 [NSApp sendEvent:event]; 688 [NSApp sendEvent:event];
664 } 689 }
665 } 690 }
666 keep_running_ = true; 691 keep_running_ = true;
667 } 692 }
(...skipping 14 matching lines...) Expand all
682 modifierFlags:0 707 modifierFlags:0
683 timestamp:0 708 timestamp:0
684 windowNumber:0 709 windowNumber:0
685 context:NULL 710 context:NULL
686 subtype:0 711 subtype:0
687 data1:0 712 data1:0
688 data2:0] 713 data2:0]
689 atStart:NO]; 714 atStart:NO];
690 } 715 }
691 716
717 // Prevents an autorelease pool from being created if the app is in the midst of
718 // handling a UI event because various parts of AppKit depend on objects that
719 // are created while handling a UI event to be autoreleased in the event loop.
720 // An example of this is NSWindowController. When a window with a window
721 // controller is closed it goes through a stack like this:
722 // (Several stack frames elided for clarity)
723 //
724 // #0 [NSWindowController autorelease]
725 // #1 DoAClose
726 // #2 MessagePumpCFRunLoopBase::DoWork()
727 // #3 [NSRunLoop run]
728 // #4 [NSButton performClick:]
729 // #5 [NSWindow sendEvent:]
730 // #6 [NSApp sendEvent:]
731 // #7 [NSApp run]
732 //
733 // -performClick: spins a nested run loop. If the pool created in DoWork was a
734 // standard NSAutoreleasePool, it would release the objects that were
735 // autoreleased into it once DoWork released it. This would cause the window
736 // controller, which autoreleased itself in frame #0, to release itself, and
737 // possibly free itself. Unfortunately this window controller controls the
738 // window in frame #5. When the stack is unwound to frame #5, the window would
739 // no longer exists and crashes may occur. Apple gets around this by never
740 // releasing the pool it creates in frame #4, and letting frame #7 clean it up
741 // when it cleans up the pool that wraps frame #7. When an autorelease pool is
742 // released it releases all other pools that were created after it on the
743 // autorelease pool stack.
744 //
745 // CrApplication is responsible for setting handlingSendEvent to true just
746 // before it sends the event throught the event handling mechanism, and
747 // returning it to its previous value once the event has been sent.
748 NSAutoreleasePool* MessagePumpNSApplication::CreateAutoreleasePool() {
749 NSAutoreleasePool* pool = nil;
750 DCHECK([NSApp isKindOfClass:[CrApplication class]]);
751 if (![static_cast<CrApplication*>(NSApp) isHandlingSendEvent]) {
752 pool = MessagePumpCFRunLoopBase::CreateAutoreleasePool();
753 }
754 return pool;
755 }
756
692 // static 757 // static
693 MessagePump* MessagePumpMac::Create() { 758 MessagePump* MessagePumpMac::Create() {
694 if ([NSThread isMainThread]) { 759 if ([NSThread isMainThread]) {
695 return new MessagePumpNSApplication; 760 return new MessagePumpNSApplication;
696 } 761 }
697 762
698 return new MessagePumpNSRunLoop; 763 return new MessagePumpNSRunLoop;
699 } 764 }
700 765
701 } // namespace base 766 } // namespace base
OLDNEW
« no previous file with comments | « base/chrome_application_mac.mm ('k') | chrome/app/app-Info.plist » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698