• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/profiles/profile_destroyer.h"
6 
7 #include "base/bind.h"
8 #include "base/debug/trace_event.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "content/public/browser/render_process_host.h"
13 
14 namespace {
15 
16 #if defined(OS_ANDROID)
17 // Set the render host waiting time to 5s on Android, that's the same
18 // as an "Application Not Responding" timeout.
19 const int64 kTimerDelaySeconds = 5;
20 #else
21 const int64 kTimerDelaySeconds = 1;
22 #endif
23 
24 }  // namespace
25 
26 ProfileDestroyer::DestroyerSet* ProfileDestroyer::pending_destroyers_ = NULL;
27 
28 // static
DestroyProfileWhenAppropriate(Profile * const profile)29 void ProfileDestroyer::DestroyProfileWhenAppropriate(Profile* const profile) {
30   TRACE_EVENT0("shutdown", "ProfileDestroyer::DestroyProfileWhenAppropriate");
31 
32   DCHECK(profile);
33   profile->MaybeSendDestroyedNotification();
34 
35   HostSet hosts;
36   // Testing profiles can simply be deleted directly. Some tests don't setup
37   // RenderProcessHost correctly and don't necessary run on the UI thread
38   // anyway, so we can't use the AllHostIterator.
39   if (profile->AsTestingProfile() == NULL) {
40     GetHostsForProfile(profile, &hosts);
41     if (!profile->IsOffTheRecord() && profile->HasOffTheRecordProfile())
42       GetHostsForProfile(profile->GetOffTheRecordProfile(), &hosts);
43   }
44   // Generally, !hosts.empty() means that there is a leak in a render process
45   // host that MUST BE FIXED!!!
46   //
47   // However, off-the-record profiles are destroyed before their
48   // RenderProcessHosts in order to erase private data quickly, and
49   // RenderProcessHostImpl::Release() avoids destroying RenderProcessHosts in
50   // --single-process mode to avoid race conditions.
51   DCHECK(hosts.empty() || profile->IsOffTheRecord() ||
52       content::RenderProcessHost::run_renderer_in_process()) << \
53       "Profile still has " << hosts.size() << " hosts";
54   // Note that we still test for !profile->IsOffTheRecord here even though we
55   // DCHECK'd above because we want to protect Release builds against this even
56   // we need to identify if there are leaks when we run Debug builds.
57   if (hosts.empty() || !profile->IsOffTheRecord()) {
58     if (profile->IsOffTheRecord())
59       profile->GetOriginalProfile()->DestroyOffTheRecordProfile();
60     else
61       delete profile;
62   } else {
63     // The instance will destroy itself once all render process hosts referring
64     // to it are properly terminated.
65     new ProfileDestroyer(profile, &hosts);
66   }
67 }
68 
69 // This can be called to cancel any pending destruction and destroy the profile
70 // now, e.g., if the parent profile is being destroyed while the incognito one
71 // still pending...
DestroyOffTheRecordProfileNow(Profile * const profile)72 void ProfileDestroyer::DestroyOffTheRecordProfileNow(Profile* const profile) {
73   DCHECK(profile);
74   DCHECK(profile->IsOffTheRecord());
75   if (pending_destroyers_) {
76     for (DestroyerSet::iterator i = pending_destroyers_->begin();
77          i != pending_destroyers_->end(); ++i) {
78       if ((*i)->profile_ == profile) {
79         // We want to signal this in debug builds so that we don't lose sight of
80         // these potential leaks, but we handle it in release so that we don't
81         // crash or corrupt profile data on disk.
82         NOTREACHED() << "A render process host wasn't destroyed early enough.";
83         (*i)->profile_ = NULL;
84         break;
85       }
86     }
87   }
88   DCHECK(profile->GetOriginalProfile());
89   profile->GetOriginalProfile()->DestroyOffTheRecordProfile();
90 }
91 
ProfileDestroyer(Profile * const profile,HostSet * hosts)92 ProfileDestroyer::ProfileDestroyer(Profile* const profile, HostSet* hosts)
93     : timer_(false, false),
94       num_hosts_(0),
95       profile_(profile),
96       weak_ptr_factory_(this) {
97   if (pending_destroyers_ == NULL)
98     pending_destroyers_ = new DestroyerSet;
99   pending_destroyers_->insert(this);
100   for (HostSet::iterator i = hosts->begin(); i != hosts->end(); ++i) {
101     (*i)->AddObserver(this);
102     // For each of the observations, we bump up our reference count.
103     // It will go back to 0 and free us when all hosts are terminated.
104     ++num_hosts_;
105   }
106   // If we are going to wait for render process hosts, we don't want to do it
107   // for longer than kTimerDelaySeconds.
108   if (num_hosts_) {
109     timer_.Start(FROM_HERE,
110                  base::TimeDelta::FromSeconds(kTimerDelaySeconds),
111                  base::Bind(&ProfileDestroyer::DestroyProfile,
112                             weak_ptr_factory_.GetWeakPtr()));
113   }
114 }
115 
~ProfileDestroyer()116 ProfileDestroyer::~ProfileDestroyer() {
117   // Check again, in case other render hosts were added while we were
118   // waiting for the previous ones to go away...
119   if (profile_)
120     DestroyProfileWhenAppropriate(profile_);
121 
122   // Don't wait for pending registrations, if any, these hosts are buggy.
123   // Note: this can happen, but if so, it's better to crash here than wait
124   // for the host to dereference a deleted Profile. http://crbug.com/248625
125   CHECK_EQ(0U, num_hosts_) << "Some render process hosts were not "
126                            << "destroyed early enough!";
127 
128   DCHECK(pending_destroyers_ != NULL);
129   DestroyerSet::iterator iter = pending_destroyers_->find(this);
130   DCHECK(iter != pending_destroyers_->end());
131   pending_destroyers_->erase(iter);
132   if (pending_destroyers_->empty()) {
133     delete pending_destroyers_;
134     pending_destroyers_ = NULL;
135   }
136 }
137 
RenderProcessHostDestroyed(content::RenderProcessHost * host)138 void ProfileDestroyer::RenderProcessHostDestroyed(
139     content::RenderProcessHost* host) {
140   DCHECK(num_hosts_ > 0);
141   --num_hosts_;
142   if (num_hosts_ == 0) {
143     // Delay the destruction one step further in case other observers need to
144     // look at the profile attached to the host.
145     base::MessageLoop::current()->PostTask(
146         FROM_HERE, base::Bind(
147             &ProfileDestroyer::DestroyProfile, weak_ptr_factory_.GetWeakPtr()));
148   }
149 }
150 
DestroyProfile()151 void ProfileDestroyer::DestroyProfile() {
152   // We might have been cancelled externally before the timer expired.
153   if (!profile_) {
154     delete this;
155     return;
156   }
157 
158   DCHECK(profile_->IsOffTheRecord());
159   DCHECK(profile_->GetOriginalProfile());
160   profile_->GetOriginalProfile()->DestroyOffTheRecordProfile();
161   profile_ = NULL;
162 
163   // And stop the timer so we can be released early too.
164   timer_.Stop();
165 
166   delete this;
167 }
168 
169 // static
GetHostsForProfile(Profile * const profile,HostSet * hosts)170 bool ProfileDestroyer::GetHostsForProfile(
171     Profile* const profile, HostSet* hosts) {
172   for (content::RenderProcessHost::iterator iter(
173         content::RenderProcessHost::AllHostsIterator());
174       !iter.IsAtEnd(); iter.Advance()) {
175     content::RenderProcessHost* render_process_host = iter.GetCurrentValue();
176     if (render_process_host &&
177             render_process_host->GetBrowserContext() == profile) {
178       hosts->insert(render_process_host);
179     }
180   }
181   return !hosts->empty();
182 }
183