Add an exponential backoff to rechecking the app list doodle.
[chromium-blink-merge.git] / components / nacl / browser / pnacl_host.cc
blobbae7e97d86cbe722a7d90fde8d5c405f3aab5803
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.
5 #include "components/nacl/browser/pnacl_host.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.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"
20 using content::BrowserThread;
22 namespace {
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;
29 void CloseBaseFile(base::File auto_file_closer) {
32 void CloseScopedFile(scoped_ptr<base::File> auto_file_closer) {
35 } // namespace
37 namespace pnacl {
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);
45 private:
46 scoped_ptr<base::File> file_;
47 base::WeakPtr<pnacl::PnaclHost> host_;
50 FileProxy::FileProxy(scoped_ptr<base::File> file,
51 base::WeakPtr<pnacl::PnaclHost> host)
52 : file_(file.Pass()),
53 host_(host) {
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;
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_)));
73 PnaclHost::PnaclHost()
74 : pending_backend_operations_(0),
75 cache_state_(CacheUninitialized),
76 weak_factory_(this) {}
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;
85 PnaclHost* PnaclHost::GetInstance() {
86 return Singleton<PnaclHost>::get();
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()) {
101 PnaclHost::PendingTranslation::~PendingTranslation() {
102 if (nexe_fd)
103 delete nexe_fd;
106 bool PnaclHost::TranslationMayBeCached(
107 const PendingTranslationMap::iterator& entry) {
108 return !entry->second.is_incognito &&
109 !entry->second.cache_info.has_no_store_header;
112 /////////////////////////////////////// Initialization
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();
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);
129 // Append the base file name to the cache directory.
130 return cache_file_path.Append(kTranslationCacheDirectoryName);
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;
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_CURRENTLY_ON(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);
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()));
180 if (rv != net::ERR_IO_PENDING)
181 OnCacheInitialized(rv);
184 ///////////////////////////////////////// Temp files
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());
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);
206 if (!file.IsValid())
207 PLOG(ERROR) << "Temp file open failed: " << file.error_details();
209 BrowserThread::PostTask(
210 BrowserThread::IO, FROM_HERE, base::Bind(cb, Passed(file.Pass())));
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());
223 ///////////////////////////////////////// GetNexeFd implementation
224 ////////////////////// Common steps
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();
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;
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);
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;
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);
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));
289 CreateTemporaryFile(
290 base::Bind(&PnaclHost::OnTempFileReturn, weak_factory_.GetWeakPtr(), id));
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;
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);
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;
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;
349 PendingTranslation* pt = &entry->second;
350 pt->got_nexe_fd = true;
351 pt->nexe_fd = new base::File(file.Pass());
352 CheckCacheQueryReady(entry);
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;
380 ReturnMiss(entry);
381 return;
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()));
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);
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);
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;
432 if (error) {
433 buffer = NULL;
435 return buffer;
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;
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;
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;
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)));
486 pending_translations_.erase(entry);
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;
507 if (buffer.get() == NULL) {
508 LOG(ERROR) << "Error reading translated nexe";
509 return;
511 pending_backend_operations_++;
512 disk_cache_->StoreNexe(it->second.cache_key,
513 buffer.get(),
514 base::Bind(&PnaclHost::OnTranslatedNexeStored,
515 weak_factory_.GetWeakPtr(),
516 it->first));
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;
532 std::string key(entry->second.cache_key);
533 pending_translations_.erase(entry);
534 RequeryMatchingTranslations(key);
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));
558 //////////////////// GetNexeFd hit path
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;
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;
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);
588 ///////////////////
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);
612 BrowserThread::PostTask(
613 BrowserThread::IO,
614 FROM_HERE,
615 base::Bind(&PnaclHost::DeInitIfSafe, weak_factory_.GetWeakPtr()));
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();
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;
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);
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()));
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.
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();
689 } // namespace pnacl