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