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