Home | History | Annotate | Download | only in browser
      1 // Copyright 2013 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 "components/nacl/browser/pnacl_host.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/file_util.h"
     10 #include "base/files/file_path.h"
     11 #include "base/logging.h"
     12 #include "base/task_runner_util.h"
     13 #include "base/threading/sequenced_worker_pool.h"
     14 #include "components/nacl/browser/nacl_browser.h"
     15 #include "components/nacl/browser/pnacl_translation_cache.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "net/base/io_buffer.h"
     18 #include "net/base/net_errors.h"
     19 
     20 using content::BrowserThread;
     21 
     22 namespace {
     23 
     24 static const base::FilePath::CharType kTranslationCacheDirectoryName[] =
     25     FILE_PATH_LITERAL("PnaclTranslationCache");
     26 // Delay to wait for initialization of the cache backend
     27 static const int kTranslationCacheInitializationDelayMs = 20;
     28 
     29 void CloseBaseFile(base::File auto_file_closer) {
     30 }
     31 
     32 void CloseScopedFile(scoped_ptr<base::File> auto_file_closer) {
     33 }
     34 
     35 }  // namespace
     36 
     37 namespace pnacl {
     38 
     39 class FileProxy {
     40  public:
     41   FileProxy(scoped_ptr<base::File> file, base::WeakPtr<pnacl::PnaclHost> host);
     42   int Write(scoped_refptr<net::DrainableIOBuffer> buffer);
     43   void WriteDone(const PnaclHost::TranslationID& id, int result);
     44 
     45  private:
     46   scoped_ptr<base::File> file_;
     47   base::WeakPtr<pnacl::PnaclHost> host_;
     48 };
     49 
     50 FileProxy::FileProxy(scoped_ptr<base::File> file,
     51                      base::WeakPtr<pnacl::PnaclHost> host)
     52     : file_(file.Pass()),
     53       host_(host) {
     54 }
     55 
     56 int FileProxy::Write(scoped_refptr<net::DrainableIOBuffer> buffer) {
     57   int rv = file_->Write(0, buffer->data(), buffer->size());
     58   if (rv == -1)
     59     PLOG(ERROR) << "FileProxy::Write error";
     60   return rv;
     61 }
     62 
     63 void FileProxy::WriteDone(const PnaclHost::TranslationID& id, int result) {
     64   if (host_) {
     65     host_->OnBufferCopiedToTempFile(id, file_.Pass(), result);
     66   } else {
     67     BrowserThread::PostBlockingPoolTask(
     68         FROM_HERE,
     69         base::Bind(CloseScopedFile, Passed(&file_)));
     70   }
     71 }
     72 
     73 PnaclHost::PnaclHost()
     74     : pending_backend_operations_(0),
     75       cache_state_(CacheUninitialized),
     76       weak_factory_(this) {}
     77 
     78 PnaclHost::~PnaclHost() {
     79   // When PnaclHost is destroyed, it's too late to post anything to the cache
     80   // thread (it will hang shutdown). So just leak the cache backend.
     81   pnacl::PnaclTranslationCache* cache = disk_cache_.release();
     82   (void)cache;
     83 }
     84 
     85 PnaclHost* PnaclHost::GetInstance() {
     86   return Singleton<PnaclHost>::get();
     87 }
     88 
     89 PnaclHost::PendingTranslation::PendingTranslation()
     90     : process_handle(base::kNullProcessHandle),
     91       render_view_id(0),
     92       nexe_fd(NULL),
     93       got_nexe_fd(false),
     94       got_cache_reply(false),
     95       got_cache_hit(false),
     96       is_incognito(false),
     97       callback(NexeFdCallback()),
     98       cache_info(nacl::PnaclCacheInfo()) {
     99 }
    100 
    101 PnaclHost::PendingTranslation::~PendingTranslation() {
    102   if (nexe_fd)
    103     delete nexe_fd;
    104 }
    105 
    106 bool PnaclHost::TranslationMayBeCached(
    107     const PendingTranslationMap::iterator& entry) {
    108   return !entry->second.is_incognito &&
    109          !entry->second.cache_info.has_no_store_header;
    110 }
    111 
    112 /////////////////////////////////////// Initialization
    113 
    114 static base::FilePath GetCachePath() {
    115   NaClBrowserDelegate* browser_delegate = nacl::NaClBrowser::GetDelegate();
    116   // Determine where the translation cache resides in the file system.  It
    117   // exists in Chrome's cache directory and is not tied to any specific
    118   // profile. If we fail, return an empty path.
    119   // Start by finding the user data directory.
    120   base::FilePath user_data_dir;
    121   if (!browser_delegate ||
    122       !browser_delegate->GetUserDirectory(&user_data_dir)) {
    123     return base::FilePath();
    124   }
    125   // The cache directory may or may not be the user data directory.
    126   base::FilePath cache_file_path;
    127   browser_delegate->GetCacheDirectory(&cache_file_path);
    128 
    129   // Append the base file name to the cache directory.
    130   return cache_file_path.Append(kTranslationCacheDirectoryName);
    131 }
    132 
    133 void PnaclHost::OnCacheInitialized(int net_error) {
    134   DCHECK(thread_checker_.CalledOnValidThread());
    135   // If the cache was cleared before the load completed, ignore.
    136   if (cache_state_ == CacheReady)
    137     return;
    138   if (net_error != net::OK) {
    139     // This will cause the cache to attempt to re-init on the next call to
    140     // GetNexeFd.
    141     cache_state_ = CacheUninitialized;
    142   } else {
    143     cache_state_ = CacheReady;
    144   }
    145 }
    146 
    147 void PnaclHost::Init() {
    148   // Extra check that we're on the real IO thread since this version of
    149   // Init isn't used in unit tests.
    150   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    151   DCHECK(thread_checker_.CalledOnValidThread());
    152   base::FilePath cache_path(GetCachePath());
    153   if (cache_path.empty() || cache_state_ != CacheUninitialized)
    154     return;
    155   disk_cache_.reset(new pnacl::PnaclTranslationCache());
    156   cache_state_ = CacheInitializing;
    157   int rv = disk_cache_->InitOnDisk(
    158       cache_path,
    159       base::Bind(&PnaclHost::OnCacheInitialized, weak_factory_.GetWeakPtr()));
    160   if (rv != net::ERR_IO_PENDING)
    161     OnCacheInitialized(rv);
    162 }
    163 
    164 // Initialize for testing, optionally using the in-memory backend, and manually
    165 // setting the temporary file directory instead of using the system directory.
    166 void PnaclHost::InitForTest(base::FilePath temp_dir, bool in_memory) {
    167   DCHECK(thread_checker_.CalledOnValidThread());
    168   disk_cache_.reset(new pnacl::PnaclTranslationCache());
    169   cache_state_ = CacheInitializing;
    170   temp_dir_ = temp_dir;
    171   int rv;
    172   if (in_memory) {
    173     rv = disk_cache_->InitInMemory(
    174       base::Bind(&PnaclHost::OnCacheInitialized, weak_factory_.GetWeakPtr()));
    175   } else {
    176     rv = disk_cache_->InitOnDisk(
    177       temp_dir,
    178       base::Bind(&PnaclHost::OnCacheInitialized, weak_factory_.GetWeakPtr()));
    179   }
    180   if (rv != net::ERR_IO_PENDING)
    181     OnCacheInitialized(rv);
    182 }
    183 
    184 ///////////////////////////////////////// Temp files
    185 
    186 // Create a temporary file on the blocking pool
    187 // static
    188 void PnaclHost::DoCreateTemporaryFile(base::FilePath temp_dir,
    189                                       TempFileCallback cb) {
    190   DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
    191 
    192   base::FilePath file_path;
    193   base::File file;
    194   bool rv = temp_dir.empty()
    195                 ? base::CreateTemporaryFile(&file_path)
    196                 : base::CreateTemporaryFileInDir(temp_dir, &file_path);
    197   if (!rv) {
    198     PLOG(ERROR) << "Temp file creation failed.";
    199   } else {
    200     file.Initialize(
    201         file_path,
    202         base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_READ |
    203             base::File::FLAG_WRITE | base::File::FLAG_TEMPORARY |
    204             base::File::FLAG_DELETE_ON_CLOSE);
    205 
    206     if (!file.IsValid())
    207       PLOG(ERROR) << "Temp file open failed: " << file.error_details();
    208   }
    209   BrowserThread::PostTask(
    210       BrowserThread::IO, FROM_HERE, base::Bind(cb, Passed(file.Pass())));
    211 }
    212 
    213 void PnaclHost::CreateTemporaryFile(TempFileCallback cb) {
    214   if (!BrowserThread::PostBlockingPoolSequencedTask(
    215            "PnaclHostCreateTempFile",
    216            FROM_HERE,
    217            base::Bind(&PnaclHost::DoCreateTemporaryFile, temp_dir_, cb))) {
    218     DCHECK(thread_checker_.CalledOnValidThread());
    219     cb.Run(base::File());
    220   }
    221 }
    222 
    223 ///////////////////////////////////////// GetNexeFd implementation
    224 ////////////////////// Common steps
    225 
    226 void PnaclHost::GetNexeFd(int render_process_id,
    227                           int render_view_id,
    228                           int pp_instance,
    229                           bool is_incognito,
    230                           const nacl::PnaclCacheInfo& cache_info,
    231                           const NexeFdCallback& cb) {
    232   DCHECK(thread_checker_.CalledOnValidThread());
    233   if (cache_state_ == CacheUninitialized) {
    234     Init();
    235   }
    236   if (cache_state_ != CacheReady) {
    237     // If the backend hasn't yet initialized, try the request again later.
    238     BrowserThread::PostDelayedTask(BrowserThread::IO,
    239                                    FROM_HERE,
    240                                    base::Bind(&PnaclHost::GetNexeFd,
    241                                               weak_factory_.GetWeakPtr(),
    242                                               render_process_id,
    243                                               render_view_id,
    244                                               pp_instance,
    245                                               is_incognito,
    246                                               cache_info,
    247                                               cb),
    248                                    base::TimeDelta::FromMilliseconds(
    249                                        kTranslationCacheInitializationDelayMs));
    250     return;
    251   }
    252 
    253   TranslationID id(render_process_id, pp_instance);
    254   PendingTranslationMap::iterator entry = pending_translations_.find(id);
    255   if (entry != pending_translations_.end()) {
    256     // Existing translation must have been abandonded. Clean it up.
    257     LOG(ERROR) << "GetNexeFd for already-pending translation";
    258     pending_translations_.erase(entry);
    259   }
    260 
    261   std::string cache_key(disk_cache_->GetKey(cache_info));
    262   if (cache_key.empty()) {
    263     LOG(ERROR) << "GetNexeFd: Invalid cache info";
    264     cb.Run(base::File(), false);
    265     return;
    266   }
    267 
    268   PendingTranslation pt;
    269   pt.render_view_id = render_view_id;
    270   pt.callback = cb;
    271   pt.cache_info = cache_info;
    272   pt.cache_key = cache_key;
    273   pt.is_incognito = is_incognito;
    274   pending_translations_[id] = pt;
    275   SendCacheQueryAndTempFileRequest(cache_key, id);
    276 }
    277 
    278 // Dispatch the cache read request and the temp file creation request
    279 // simultaneously; currently we need a temp file regardless of whether the
    280 // request hits.
    281 void PnaclHost::SendCacheQueryAndTempFileRequest(const std::string& cache_key,
    282                                                  const TranslationID& id) {
    283   pending_backend_operations_++;
    284   disk_cache_->GetNexe(
    285       cache_key,
    286       base::Bind(
    287           &PnaclHost::OnCacheQueryReturn, weak_factory_.GetWeakPtr(), id));
    288 
    289   CreateTemporaryFile(
    290       base::Bind(&PnaclHost::OnTempFileReturn, weak_factory_.GetWeakPtr(), id));
    291 }
    292 
    293 // Callback from the translation cache query. |id| is bound from
    294 // SendCacheQueryAndTempFileRequest, |net_error| is a net::Error code (which for
    295 // our purposes means a hit if it's net::OK (i.e. 0). |buffer| is allocated
    296 // by PnaclTranslationCache and now belongs to PnaclHost.
    297 // (Bound callbacks must re-lookup the TranslationID because the translation
    298 // could be cancelled before they get called).
    299 void PnaclHost::OnCacheQueryReturn(
    300     const TranslationID& id,
    301     int net_error,
    302     scoped_refptr<net::DrainableIOBuffer> buffer) {
    303   DCHECK(thread_checker_.CalledOnValidThread());
    304   pending_backend_operations_--;
    305   PendingTranslationMap::iterator entry(pending_translations_.find(id));
    306   if (entry == pending_translations_.end()) {
    307     LOG(ERROR) << "OnCacheQueryReturn: id not found";
    308     DeInitIfSafe();
    309     return;
    310   }
    311   PendingTranslation* pt = &entry->second;
    312   pt->got_cache_reply = true;
    313   pt->got_cache_hit = (net_error == net::OK);
    314   if (pt->got_cache_hit)
    315     pt->nexe_read_buffer = buffer;
    316   CheckCacheQueryReady(entry);
    317 }
    318 
    319 // Callback from temp file creation. |id| is bound from
    320 // SendCacheQueryAndTempFileRequest, and |file| is the created file.
    321 // If there was an error, file is invalid.
    322 // (Bound callbacks must re-lookup the TranslationID because the translation
    323 // could be cancelled before they get called).
    324 void PnaclHost::OnTempFileReturn(const TranslationID& id,
    325                                  base::File file) {
    326   DCHECK(thread_checker_.CalledOnValidThread());
    327   PendingTranslationMap::iterator entry(pending_translations_.find(id));
    328   if (entry == pending_translations_.end()) {
    329     // The renderer may have signaled an error or closed while the temp
    330     // file was being created.
    331     LOG(ERROR) << "OnTempFileReturn: id not found";
    332     BrowserThread::PostBlockingPoolTask(
    333         FROM_HERE, base::Bind(CloseBaseFile, Passed(file.Pass())));
    334     return;
    335   }
    336   if (!file.IsValid()) {
    337     // This translation will fail, but we need to retry any translation
    338     // waiting for its result.
    339     LOG(ERROR) << "OnTempFileReturn: temp file creation failed";
    340     std::string key(entry->second.cache_key);
    341     entry->second.callback.Run(base::File(), false);
    342     bool may_be_cached = TranslationMayBeCached(entry);
    343     pending_translations_.erase(entry);
    344     // No translations will be waiting for entries that will not be stored.
    345     if (may_be_cached)
    346       RequeryMatchingTranslations(key);
    347     return;
    348   }
    349   PendingTranslation* pt = &entry->second;
    350   pt->got_nexe_fd = true;
    351   pt->nexe_fd = new base::File(file.Pass());
    352   CheckCacheQueryReady(entry);
    353 }
    354 
    355 // Check whether both the cache query and the temp file have returned, and check
    356 // whether we actually got a hit or not.
    357 void PnaclHost::CheckCacheQueryReady(
    358     const PendingTranslationMap::iterator& entry) {
    359   PendingTranslation* pt = &entry->second;
    360   if (!(pt->got_cache_reply && pt->got_nexe_fd))
    361     return;
    362   if (!pt->got_cache_hit) {
    363     // Check if there is already a pending translation for this file. If there
    364     // is, we will wait for it to come back, to avoid redundant translations.
    365     for (PendingTranslationMap::iterator it = pending_translations_.begin();
    366          it != pending_translations_.end();
    367          ++it) {
    368       // Another translation matches if it's a request for the same file,
    369       if (it->second.cache_key == entry->second.cache_key &&
    370           // and it's not this translation,
    371           it->first != entry->first &&
    372           // and it can be stored in the cache,
    373           TranslationMayBeCached(it) &&
    374           // and it's already gotten past this check and returned the miss.
    375           it->second.got_cache_reply &&
    376           it->second.got_nexe_fd) {
    377         return;
    378       }
    379     }
    380     ReturnMiss(entry);
    381     return;
    382   }
    383 
    384   scoped_ptr<base::File> file(pt->nexe_fd);
    385   pt->nexe_fd = NULL;
    386   pt->got_nexe_fd = false;
    387   FileProxy* proxy(new FileProxy(file.Pass(), weak_factory_.GetWeakPtr()));
    388 
    389   if (!base::PostTaskAndReplyWithResult(
    390            BrowserThread::GetBlockingPool(),
    391            FROM_HERE,
    392            base::Bind(&FileProxy::Write, base::Unretained(proxy),
    393                       pt->nexe_read_buffer),
    394            base::Bind(&FileProxy::WriteDone, base::Owned(proxy),
    395                       entry->first))) {
    396     pt->callback.Run(base::File(), false);
    397   }
    398 }
    399 
    400 //////////////////// GetNexeFd miss path
    401 // Return the temp fd to the renderer, reporting a miss.
    402 void PnaclHost::ReturnMiss(const PendingTranslationMap::iterator& entry) {
    403   // Return the fd
    404   PendingTranslation* pt = &entry->second;
    405   NexeFdCallback cb(pt->callback);
    406   cb.Run(*pt->nexe_fd, false);
    407   if (!pt->nexe_fd->IsValid()) {
    408     // Bad FD is unrecoverable, so clear out the entry.
    409     pending_translations_.erase(entry);
    410   }
    411 }
    412 
    413 // On error, just return a null refptr.
    414 // static
    415 scoped_refptr<net::DrainableIOBuffer> PnaclHost::CopyFileToBuffer(
    416     scoped_ptr<base::File> file) {
    417   base::File::Info info;
    418   scoped_refptr<net::DrainableIOBuffer> buffer;
    419   bool error = false;
    420   if (!file->GetInfo(&info) ||
    421       info.size >= std::numeric_limits<int>::max()) {
    422     PLOG(ERROR) << "File::GetInfo failed";
    423     error = true;
    424   } else {
    425     buffer = new net::DrainableIOBuffer(
    426         new net::IOBuffer(static_cast<int>(info.size)), info.size);
    427     if (file->Read(0, buffer->data(), buffer->size()) != info.size) {
    428       PLOG(ERROR) << "CopyFileToBuffer file read failed";
    429       error = true;
    430     }
    431   }
    432   if (error) {
    433     buffer = NULL;
    434   }
    435   return buffer;
    436 }
    437 
    438 // Called by the renderer in the miss path to report a finished translation
    439 void PnaclHost::TranslationFinished(int render_process_id,
    440                                     int pp_instance,
    441                                     bool success) {
    442   DCHECK(thread_checker_.CalledOnValidThread());
    443   if (cache_state_ != CacheReady)
    444     return;
    445   TranslationID id(render_process_id, pp_instance);
    446   PendingTranslationMap::iterator entry(pending_translations_.find(id));
    447   if (entry == pending_translations_.end()) {
    448     LOG(ERROR) << "TranslationFinished: TranslationID " << render_process_id
    449                << "," << pp_instance << " not found.";
    450     return;
    451   }
    452   bool store_nexe = true;
    453   // If this is a premature response (i.e. we haven't returned a temp file
    454   // yet) or if it's an unsuccessful translation, or if we are incognito,
    455   // don't store in the cache.
    456   // TODO(dschuff): use a separate in-memory cache for incognito
    457   // translations.
    458   if (!entry->second.got_nexe_fd || !entry->second.got_cache_reply ||
    459       !success || !TranslationMayBeCached(entry)) {
    460     store_nexe = false;
    461   } else {
    462     scoped_ptr<base::File> file(entry->second.nexe_fd);
    463     entry->second.nexe_fd = NULL;
    464     entry->second.got_nexe_fd = false;
    465 
    466     if (!base::PostTaskAndReplyWithResult(
    467              BrowserThread::GetBlockingPool(),
    468              FROM_HERE,
    469              base::Bind(&PnaclHost::CopyFileToBuffer, Passed(&file)),
    470              base::Bind(&PnaclHost::StoreTranslatedNexe,
    471                         weak_factory_.GetWeakPtr(),
    472                         id))) {
    473       store_nexe = false;
    474     }
    475   }
    476 
    477   if (!store_nexe) {
    478     // If store_nexe is true, the fd will be closed by CopyFileToBuffer.
    479     if (entry->second.got_nexe_fd) {
    480       scoped_ptr<base::File> file(entry->second.nexe_fd);
    481       entry->second.nexe_fd = NULL;
    482       BrowserThread::PostBlockingPoolTask(
    483           FROM_HERE,
    484           base::Bind(CloseScopedFile, Passed(&file)));
    485     }
    486     pending_translations_.erase(entry);
    487   }
    488 }
    489 
    490 // Store the translated nexe in the translation cache. Called back with the
    491 // TranslationID from the host and the result of CopyFileToBuffer.
    492 // (Bound callbacks must re-lookup the TranslationID because the translation
    493 // could be cancelled before they get called).
    494 void PnaclHost::StoreTranslatedNexe(
    495     TranslationID id,
    496     scoped_refptr<net::DrainableIOBuffer> buffer) {
    497   DCHECK(thread_checker_.CalledOnValidThread());
    498   if (cache_state_ != CacheReady)
    499     return;
    500   PendingTranslationMap::iterator it(pending_translations_.find(id));
    501   if (it == pending_translations_.end()) {
    502     LOG(ERROR) << "StoreTranslatedNexe: TranslationID " << id.first << ","
    503                << id.second << " not found.";
    504     return;
    505   }
    506 
    507   if (buffer.get() == NULL) {
    508     LOG(ERROR) << "Error reading translated nexe";
    509     return;
    510   }
    511   pending_backend_operations_++;
    512   disk_cache_->StoreNexe(it->second.cache_key,
    513                          buffer,
    514                          base::Bind(&PnaclHost::OnTranslatedNexeStored,
    515                                     weak_factory_.GetWeakPtr(),
    516                                     it->first));
    517 }
    518 
    519 // After we know the nexe has been stored, we can clean up, and unblock any
    520 // outstanding requests for the same file.
    521 // (Bound callbacks must re-lookup the TranslationID because the translation
    522 // could be cancelled before they get called).
    523 void PnaclHost::OnTranslatedNexeStored(const TranslationID& id, int net_error) {
    524   PendingTranslationMap::iterator entry(pending_translations_.find(id));
    525   pending_backend_operations_--;
    526   if (entry == pending_translations_.end()) {
    527     // If the renderer closed while we were storing the nexe, we land here.
    528     // Make sure we try to de-init.
    529     DeInitIfSafe();
    530     return;
    531   }
    532   std::string key(entry->second.cache_key);
    533   pending_translations_.erase(entry);
    534   RequeryMatchingTranslations(key);
    535 }
    536 
    537 // Check if any pending translations match |key|. If so, re-issue the cache
    538 // query. In the overlapped miss case, we expect a hit this time, but a miss
    539 // is also possible in case of an error.
    540 void PnaclHost::RequeryMatchingTranslations(const std::string& key) {
    541   // Check for outstanding misses to this same file
    542   for (PendingTranslationMap::iterator it = pending_translations_.begin();
    543        it != pending_translations_.end();
    544        ++it) {
    545     if (it->second.cache_key == key) {
    546       // Re-send the cache read request. This time we expect a hit, but if
    547       // something goes wrong, it will just handle it like a miss.
    548       it->second.got_cache_reply = false;
    549       pending_backend_operations_++;
    550       disk_cache_->GetNexe(key,
    551                            base::Bind(&PnaclHost::OnCacheQueryReturn,
    552                                       weak_factory_.GetWeakPtr(),
    553                                       it->first));
    554     }
    555   }
    556 }
    557 
    558 //////////////////// GetNexeFd hit path
    559 
    560 void PnaclHost::OnBufferCopiedToTempFile(const TranslationID& id,
    561                                          scoped_ptr<base::File> file,
    562                                          int file_error) {
    563   DCHECK(thread_checker_.CalledOnValidThread());
    564   PendingTranslationMap::iterator entry(pending_translations_.find(id));
    565   if (entry == pending_translations_.end()) {
    566     BrowserThread::PostBlockingPoolTask(
    567         FROM_HERE,
    568         base::Bind(CloseScopedFile, Passed(&file)));
    569     return;
    570   }
    571   if (file_error == -1) {
    572     // Write error on the temp file. Request a new file and start over.
    573     BrowserThread::PostBlockingPoolTask(
    574         FROM_HERE,
    575         base::Bind(CloseScopedFile, Passed(&file)));
    576     CreateTemporaryFile(base::Bind(&PnaclHost::OnTempFileReturn,
    577                                    weak_factory_.GetWeakPtr(),
    578                                    entry->first));
    579     return;
    580   }
    581   entry->second.callback.Run(*file.get(), true);
    582   BrowserThread::PostBlockingPoolTask(
    583       FROM_HERE,
    584       base::Bind(CloseScopedFile, Passed(&file)));
    585   pending_translations_.erase(entry);
    586 }
    587 
    588 ///////////////////
    589 
    590 void PnaclHost::RendererClosing(int render_process_id) {
    591   DCHECK(thread_checker_.CalledOnValidThread());
    592   if (cache_state_ != CacheReady)
    593     return;
    594   for (PendingTranslationMap::iterator it = pending_translations_.begin();
    595        it != pending_translations_.end();) {
    596     PendingTranslationMap::iterator to_erase(it++);
    597     if (to_erase->first.first == render_process_id) {
    598       // Clean up the open files.
    599       scoped_ptr<base::File> file(to_erase->second.nexe_fd);
    600       to_erase->second.nexe_fd = NULL;
    601       BrowserThread::PostBlockingPoolTask(
    602           FROM_HERE,
    603           base::Bind(CloseScopedFile, Passed(&file)));
    604       std::string key(to_erase->second.cache_key);
    605       bool may_be_cached = TranslationMayBeCached(to_erase);
    606       pending_translations_.erase(to_erase);
    607       // No translations will be waiting for entries that will not be stored.
    608       if (may_be_cached)
    609         RequeryMatchingTranslations(key);
    610     }
    611   }
    612   BrowserThread::PostTask(
    613       BrowserThread::IO,
    614       FROM_HERE,
    615       base::Bind(&PnaclHost::DeInitIfSafe, weak_factory_.GetWeakPtr()));
    616 }
    617 
    618 ////////////////// Cache data removal
    619 void PnaclHost::ClearTranslationCacheEntriesBetween(
    620     base::Time initial_time,
    621     base::Time end_time,
    622     const base::Closure& callback) {
    623   DCHECK(thread_checker_.CalledOnValidThread());
    624   if (cache_state_ == CacheUninitialized) {
    625     Init();
    626   }
    627   if (cache_state_ == CacheInitializing) {
    628     // If the backend hasn't yet initialized, try the request again later.
    629     BrowserThread::PostDelayedTask(
    630         BrowserThread::IO,
    631         FROM_HERE,
    632         base::Bind(&PnaclHost::ClearTranslationCacheEntriesBetween,
    633                    weak_factory_.GetWeakPtr(),
    634                    initial_time,
    635                    end_time,
    636                    callback),
    637         base::TimeDelta::FromMilliseconds(
    638             kTranslationCacheInitializationDelayMs));
    639     return;
    640   }
    641   pending_backend_operations_++;
    642   int rv = disk_cache_->DoomEntriesBetween(
    643       initial_time,
    644       end_time,
    645       base::Bind(
    646           &PnaclHost::OnEntriesDoomed, weak_factory_.GetWeakPtr(), callback));
    647   if (rv != net::ERR_IO_PENDING)
    648     OnEntriesDoomed(callback, rv);
    649 }
    650 
    651 void PnaclHost::OnEntriesDoomed(const base::Closure& callback, int net_error) {
    652   DCHECK(thread_checker_.CalledOnValidThread());
    653   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, callback);
    654   pending_backend_operations_--;
    655   // When clearing the cache, the UI is blocked on all the cache-clearing
    656   // operations, and freeing the backend actually blocks the IO thread. So
    657   // instead of calling DeInitIfSafe directly, post it for later.
    658   BrowserThread::PostTask(
    659       BrowserThread::IO,
    660       FROM_HERE,
    661       base::Bind(&PnaclHost::DeInitIfSafe, weak_factory_.GetWeakPtr()));
    662 }
    663 
    664 // Destroying the cache backend causes it to post tasks to the cache thread to
    665 // flush to disk. Because PnaclHost is a singleton, it does not get destroyed
    666 // until all the browser threads have gone away and it's too late to post
    667 // anything (attempting to do so hangs shutdown).  So we make sure to destroy it
    668 // when we no longer have any outstanding operations that need it. These include
    669 // pending translations, cache clear requests, and requests to read or write
    670 // translated nexes.  We check when renderers close, when cache clear requests
    671 // finish, and when backend operations complete.
    672 
    673 // It is not safe to delete the backend while it is initializing, nor if it has
    674 // outstanding entry open requests; it is in theory safe to delete it with
    675 // outstanding read/write requests, but because that distinction is hidden
    676 // inside PnaclTranslationCache, we do not delete the backend if there are any
    677 // backend requests in flight.  As a last resort in the destructor, we just leak
    678 // the backend to avoid hanging shutdown.
    679 void PnaclHost::DeInitIfSafe() {
    680   DCHECK(pending_backend_operations_ >= 0);
    681   if (pending_translations_.empty() &&
    682       pending_backend_operations_ <= 0 &&
    683       cache_state_ == CacheReady) {
    684     cache_state_ = CacheUninitialized;
    685     disk_cache_.reset();
    686   }
    687 }
    688 
    689 }  // namespace pnacl
    690