Index: chrome/browser/profiles/profile_destroyer.cc |
=================================================================== |
--- chrome/browser/profiles/profile_destroyer.cc (revision 145739) |
+++ chrome/browser/profiles/profile_destroyer.cc (working copy) |
@@ -12,8 +12,18 @@ |
#include "content/public/browser/notification_types.h" |
#include "content/public/browser/render_process_host.h" |
+ |
+namespace { |
+ |
+const int64 kTimerDelaySeconds = 1; |
+ |
+} // namespace |
+ |
+std::vector<ProfileDestroyer*>* ProfileDestroyer::pending_destroyers_ = NULL; |
+ |
// static |
void ProfileDestroyer::DestroyProfileWhenAppropriate(Profile* const profile) { |
+ DCHECK(profile); |
std::vector<content::RenderProcessHost*> hosts; |
// Testing profiles can simply be deleted directly. Some tests don't setup |
// RenderProcessHost correctly and don't necessary run on the UI thread |
@@ -23,47 +33,127 @@ |
if (!profile->IsOffTheRecord() && profile->HasOffTheRecordProfile()) |
GetHostsForProfile(profile->GetOffTheRecordProfile(), &hosts); |
} |
- if (hosts.empty()) { |
+ // This should never happen for non Off the record profile, this means that |
+ // there is a leak in a render process host that MUST BE FIXED!!! |
+ DCHECK(hosts.empty() || profile->IsOffTheRecord()); |
+ // Note that we still test for !profile->IsOffTheRecord here even though we |
+ // DCHECK'd above because we want to protect Release builds against this even |
+ // we need to identify if there are leaks when we run Debug builds. |
+ if (hosts.empty() || !profile->IsOffTheRecord()) { |
if (profile->IsOffTheRecord()) |
profile->GetOriginalProfile()->DestroyOffTheRecordProfile(); |
else |
delete profile; |
} else { |
// The instance will destroy itself once all render process hosts referring |
- // to it and/or it's off the record profile are properly terminated. |
+ // to it are properly terminated. |
scoped_refptr<ProfileDestroyer> profile_destroyer( |
new ProfileDestroyer(profile, hosts)); |
} |
} |
+// This can be called to cancel any pending destruction and destroy the profile |
+// now, e.g., if the parent profile is being destroyed while the incognito one |
+// still pending... |
+void ProfileDestroyer::DestroyOffTheRecordProfileNow(Profile* const profile) { |
+ DCHECK(profile); |
+ DCHECK(profile->IsOffTheRecord()); |
+ if (pending_destroyers_) { |
+ for (size_t i = 0; i < pending_destroyers_->size(); ++i) { |
+ if ((*pending_destroyers_)[i]->profile_ == profile) { |
+ // We want to signal this in debug builds so that we don't lose sight of |
+ // these potential leaks, but we handle it in release so that we don't |
+ // crash or corrupt profile data on disk. |
+ NOTREACHED() << "A render process host wasn't destroyed early enough."; |
+ (*pending_destroyers_)[i]->profile_ = NULL; |
+ break; |
+ } |
+ } |
+ } |
+ DCHECK(profile->GetOriginalProfile()); |
+ profile->GetOriginalProfile()->DestroyOffTheRecordProfile(); |
+} |
+ |
ProfileDestroyer::ProfileDestroyer( |
Profile* const profile, |
- const std::vector<content::RenderProcessHost*>& hosts) : profile_(profile) { |
+ const std::vector<content::RenderProcessHost*>& hosts) |
+ : timer_(false, false), |
+ num_hosts_(0), |
+ profile_(profile) { |
+ if (pending_destroyers_ == NULL) |
+ pending_destroyers_ = new std::vector<ProfileDestroyer*>; |
+ pending_destroyers_->push_back(this); |
for (size_t i = 0; i < hosts.size(); ++i) { |
registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, |
content::Source<content::RenderProcessHost>(hosts[i])); |
// For each of the notifications, we bump up our reference count. |
// It will go back to 0 and free us when all hosts are terminated. |
- AddRef(); |
+ ++num_hosts_; |
} |
+ // If we are going to wait for render process hosts, we don't want to do it |
+ // for longer than kTimerDelaySeconds. |
+ if (num_hosts_) { |
+ timer_.Start(FROM_HERE, |
+ base::TimeDelta::FromSeconds(kTimerDelaySeconds), |
+ base::Bind(&ProfileDestroyer::DestroyProfile, this)); |
+ } |
} |
ProfileDestroyer::~ProfileDestroyer() { |
// Check again, in case other render hosts were added while we were |
// waiting for the previous ones to go away... |
- DestroyProfileWhenAppropriate(profile_); |
+ if (profile_) |
+ DestroyProfileWhenAppropriate(profile_); |
+ |
+ // We shouldn't be deleted with pending notifications. |
+ DCHECK(registrar_.IsEmpty()); |
+ |
+ DCHECK(pending_destroyers_ != NULL); |
+ std::vector<ProfileDestroyer*>::iterator iter = std::find( |
+ pending_destroyers_->begin(), pending_destroyers_->end(), this); |
+ DCHECK(iter != pending_destroyers_->end()); |
+ pending_destroyers_->erase(iter); |
+ DCHECK(pending_destroyers_->end() == std::find(pending_destroyers_->begin(), |
+ pending_destroyers_->end(), |
+ this)); |
+ if (pending_destroyers_->empty()) { |
+ delete pending_destroyers_; |
+ pending_destroyers_ = NULL; |
+ } |
} |
void ProfileDestroyer::Observe(int type, |
const content::NotificationSource& source, |
const content::NotificationDetails& details) { |
DCHECK(type == content::NOTIFICATION_RENDERER_PROCESS_TERMINATED); |
- // Delay the destruction one step further in case other observers of this |
- // notification need to look at the profile attached to the host. |
- MessageLoop::current()->PostTask( |
- FROM_HERE, base::Bind(&ProfileDestroyer::Release, this)); |
+ DCHECK(num_hosts_ > 0); |
+ --num_hosts_; |
+ if (num_hosts_ == 0) { |
+ // Delay the destruction one step further in case other observers of this |
+ // notification need to look at the profile attached to the host. |
+ MessageLoop::current()->PostTask( |
+ FROM_HERE, base::Bind(&ProfileDestroyer::DestroyProfile, this)); |
+ } |
} |
+void ProfileDestroyer::DestroyProfile() { |
+ // We might have been cancelled externally before the timer expired. |
+ if (profile_ == NULL) |
+ return; |
+ DCHECK(profile_->IsOffTheRecord()); |
+ DCHECK(profile_->GetOriginalProfile()); |
+ profile_->GetOriginalProfile()->DestroyOffTheRecordProfile(); |
+ profile_ = NULL; |
+ |
+ // Don't wait for pending registrations, if any, these hosts are buggy. |
+ DCHECK(registrar_.IsEmpty()) << "Some render process hosts where not " |
+ << "destroyed early enough!"; |
+ registrar_.RemoveAll(); |
+ |
+ // And stop the timer so we can be released early too. |
+ timer_.Stop(); |
+} |
+ |
// static |
bool ProfileDestroyer::GetHostsForProfile( |
Profile* const profile, std::vector<content::RenderProcessHost*>* hosts) { |