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