OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/nacl_host/pnacl_host.h" | 5 #include "chrome/browser/nacl_host/pnacl_host.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
9 #include "base/file_util.h" | 9 #include "base/file_util.h" |
10 #include "base/files/file_path.h" | 10 #include "base/files/file_path.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/task_runner_util.h" | 12 #include "base/task_runner_util.h" |
13 #include "base/threading/sequenced_worker_pool.h" | 13 #include "base/threading/sequenced_worker_pool.h" |
14 #include "chrome/browser/nacl_host/nacl_browser.h" | 14 #include "chrome/browser/nacl_host/nacl_browser.h" |
15 #include "chrome/browser/nacl_host/pnacl_translation_cache.h" | 15 #include "chrome/browser/nacl_host/pnacl_translation_cache.h" |
16 #include "content/public/browser/browser_thread.h" | 16 #include "content/public/browser/browser_thread.h" |
17 #include "net/base/io_buffer.h" | 17 #include "net/base/io_buffer.h" |
18 #include "net/base/net_errors.h" | 18 #include "net/base/net_errors.h" |
19 | 19 |
20 using content::BrowserThread; | 20 using content::BrowserThread; |
21 | 21 |
22 namespace { | 22 namespace { |
23 static const base::FilePath::CharType kTranslationCacheDirectoryName[] = | 23 static const base::FilePath::CharType kTranslationCacheDirectoryName[] = |
24 FILE_PATH_LITERAL("PnaclTranslationCache"); | 24 FILE_PATH_LITERAL("PnaclTranslationCache"); |
25 // Delay to wait for initialization of the cache backend | 25 // Delay to wait for initialization of the cache backend |
26 static const int kTranslationCacheInitializationDelayMs = 20; | 26 static const int kTranslationCacheInitializationDelayMs = 20; |
27 } | 27 } |
28 | 28 |
29 PnaclHost::PnaclHost() | 29 PnaclHost::PnaclHost() |
30 : cache_state_(CacheUninitialized), weak_factory_(this) {} | 30 : pending_backend_operations_(0), |
31 cache_state_(CacheUninitialized), | |
32 weak_factory_(this) {} | |
31 | 33 |
32 PnaclHost::~PnaclHost() {} | 34 PnaclHost::~PnaclHost() { |
35 // When PnaclHost is destroyed, it's too late to post anything to the cache | |
36 // thread (it will hang shutdown). So just leak the cache backend. | |
37 pnacl::PnaclTranslationCache* cache = disk_cache_.release(); | |
jvoung (off chromium)
2013/10/24 21:14:58
Can this just be a one-liner (void)...release()?
Derek Schuff
2013/10/25 19:38:06
Nope, gcc still warns about unused result if you d
| |
38 (void)cache; | |
39 } | |
33 | 40 |
34 PnaclHost* PnaclHost::GetInstance() { return Singleton<PnaclHost>::get(); } | 41 PnaclHost* PnaclHost::GetInstance() { return Singleton<PnaclHost>::get(); } |
35 | 42 |
36 PnaclHost::PendingTranslation::PendingTranslation() | 43 PnaclHost::PendingTranslation::PendingTranslation() |
37 : process_handle(base::kNullProcessHandle), | 44 : process_handle(base::kNullProcessHandle), |
38 render_view_id(0), | 45 render_view_id(0), |
39 nexe_fd(base::kInvalidPlatformFileValue), | 46 nexe_fd(base::kInvalidPlatformFileValue), |
40 got_nexe_fd(false), | 47 got_nexe_fd(false), |
41 got_cache_reply(false), | 48 got_cache_reply(false), |
42 got_cache_hit(false), | 49 got_cache_hit(false), |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
89 void PnaclHost::Init() { | 96 void PnaclHost::Init() { |
90 // Extra check that we're on the real IO thread since this version of | 97 // Extra check that we're on the real IO thread since this version of |
91 // Init isn't used in unit tests. | 98 // Init isn't used in unit tests. |
92 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 99 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
93 DCHECK(thread_checker_.CalledOnValidThread()); | 100 DCHECK(thread_checker_.CalledOnValidThread()); |
94 base::FilePath cache_path(GetCachePath()); | 101 base::FilePath cache_path(GetCachePath()); |
95 if (cache_path.empty() || cache_state_ != CacheUninitialized) | 102 if (cache_path.empty() || cache_state_ != CacheUninitialized) |
96 return; | 103 return; |
97 disk_cache_.reset(new pnacl::PnaclTranslationCache()); | 104 disk_cache_.reset(new pnacl::PnaclTranslationCache()); |
98 cache_state_ = CacheInitializing; | 105 cache_state_ = CacheInitializing; |
99 disk_cache_->InitOnDisk( | 106 int rv = disk_cache_->InitOnDisk( |
100 cache_path, | 107 cache_path, |
101 base::Bind(&PnaclHost::OnCacheInitialized, weak_factory_.GetWeakPtr())); | 108 base::Bind(&PnaclHost::OnCacheInitialized, weak_factory_.GetWeakPtr())); |
109 if (rv != net::ERR_IO_PENDING) | |
110 OnCacheInitialized(rv); | |
102 } | 111 } |
103 | 112 |
104 // Initialize using the in-memory backend, and manually set the temporary file | 113 // Initialize using the in-memory backend, and manually set the temporary file |
105 // directory instead of using the system directory. | 114 // directory instead of using the system directory. |
106 void PnaclHost::InitForTest(base::FilePath temp_dir) { | 115 void PnaclHost::InitForTest(base::FilePath temp_dir) { |
107 DCHECK(thread_checker_.CalledOnValidThread()); | 116 DCHECK(thread_checker_.CalledOnValidThread()); |
108 disk_cache_.reset(new pnacl::PnaclTranslationCache()); | 117 disk_cache_.reset(new pnacl::PnaclTranslationCache()); |
109 cache_state_ = CacheInitializing; | 118 cache_state_ = CacheInitializing; |
110 temp_dir_ = temp_dir; | 119 temp_dir_ = temp_dir; |
111 disk_cache_->InitInMemory( | 120 int rv = disk_cache_->InitInMemory( |
112 base::Bind(&PnaclHost::OnCacheInitialized, weak_factory_.GetWeakPtr())); | 121 base::Bind(&PnaclHost::OnCacheInitialized, weak_factory_.GetWeakPtr())); |
122 if (rv != net::ERR_IO_PENDING) | |
123 OnCacheInitialized(rv); | |
113 } | 124 } |
114 | 125 |
115 ///////////////////////////////////////// Temp files | 126 ///////////////////////////////////////// Temp files |
116 | 127 |
117 // Create a temporary file on the blocking pool | 128 // Create a temporary file on the blocking pool |
118 // static | 129 // static |
119 void PnaclHost::DoCreateTemporaryFile(base::FilePath temp_dir, | 130 void PnaclHost::DoCreateTemporaryFile(base::FilePath temp_dir, |
120 TempFileCallback cb) { | 131 TempFileCallback cb) { |
121 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | 132 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
122 | 133 |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
209 pt.is_incognito = is_incognito; | 220 pt.is_incognito = is_incognito; |
210 pending_translations_[id] = pt; | 221 pending_translations_[id] = pt; |
211 SendCacheQueryAndTempFileRequest(cache_key, id); | 222 SendCacheQueryAndTempFileRequest(cache_key, id); |
212 } | 223 } |
213 | 224 |
214 // Dispatch the cache read request and the temp file creation request | 225 // Dispatch the cache read request and the temp file creation request |
215 // simultaneously; currently we need a temp file regardless of whether the | 226 // simultaneously; currently we need a temp file regardless of whether the |
216 // request hits. | 227 // request hits. |
217 void PnaclHost::SendCacheQueryAndTempFileRequest(const std::string& cache_key, | 228 void PnaclHost::SendCacheQueryAndTempFileRequest(const std::string& cache_key, |
218 const TranslationID& id) { | 229 const TranslationID& id) { |
230 pending_backend_operations_++; | |
219 disk_cache_->GetNexe( | 231 disk_cache_->GetNexe( |
220 cache_key, | 232 cache_key, |
221 base::Bind( | 233 base::Bind( |
222 &PnaclHost::OnCacheQueryReturn, weak_factory_.GetWeakPtr(), id)); | 234 &PnaclHost::OnCacheQueryReturn, weak_factory_.GetWeakPtr(), id)); |
223 | 235 |
224 CreateTemporaryFile( | 236 CreateTemporaryFile( |
225 base::Bind(&PnaclHost::OnTempFileReturn, weak_factory_.GetWeakPtr(), id)); | 237 base::Bind(&PnaclHost::OnTempFileReturn, weak_factory_.GetWeakPtr(), id)); |
226 } | 238 } |
227 | 239 |
228 // Callback from the translation cache query. |id| is bound from | 240 // Callback from the translation cache query. |id| is bound from |
229 // SendCacheQueryAndTempFileRequest, |net_error| is a net::Error code (which for | 241 // SendCacheQueryAndTempFileRequest, |net_error| is a net::Error code (which for |
230 // our purposes means a hit if it's net::OK (i.e. 0). |buffer| is allocated | 242 // our purposes means a hit if it's net::OK (i.e. 0). |buffer| is allocated |
231 // by PnaclTranslationCache and now belongs to PnaclHost. | 243 // by PnaclTranslationCache and now belongs to PnaclHost. |
232 // (Bound callbacks must re-lookup the TranslationID because the translation | 244 // (Bound callbacks must re-lookup the TranslationID because the translation |
233 // could be cancelled before they get called). | 245 // could be cancelled before they get called). |
234 void PnaclHost::OnCacheQueryReturn( | 246 void PnaclHost::OnCacheQueryReturn( |
235 const TranslationID& id, | 247 const TranslationID& id, |
236 int net_error, | 248 int net_error, |
237 scoped_refptr<net::DrainableIOBuffer> buffer) { | 249 scoped_refptr<net::DrainableIOBuffer> buffer) { |
238 DCHECK(thread_checker_.CalledOnValidThread()); | 250 DCHECK(thread_checker_.CalledOnValidThread()); |
251 pending_backend_operations_--; | |
239 PendingTranslationMap::iterator entry(pending_translations_.find(id)); | 252 PendingTranslationMap::iterator entry(pending_translations_.find(id)); |
240 if (entry == pending_translations_.end()) { | 253 if (entry == pending_translations_.end()) { |
241 LOG(ERROR) << "OnCacheQueryReturn: id not found"; | 254 LOG(ERROR) << "OnCacheQueryReturn: id not found"; |
255 DeInitIfSafe(); | |
242 return; | 256 return; |
243 } | 257 } |
244 PendingTranslation* pt = &entry->second; | 258 PendingTranslation* pt = &entry->second; |
245 pt->got_cache_reply = true; | 259 pt->got_cache_reply = true; |
246 pt->got_cache_hit = (net_error == net::OK); | 260 pt->got_cache_hit = (net_error == net::OK); |
247 if (pt->got_cache_hit) | 261 if (pt->got_cache_hit) |
248 pt->nexe_read_buffer = buffer; | 262 pt->nexe_read_buffer = buffer; |
249 CheckCacheQueryReady(entry); | 263 CheckCacheQueryReady(entry); |
250 } | 264 } |
251 | 265 |
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
426 if (it == pending_translations_.end()) { | 440 if (it == pending_translations_.end()) { |
427 LOG(ERROR) << "StoreTranslatedNexe: TranslationID " << id.first << "," | 441 LOG(ERROR) << "StoreTranslatedNexe: TranslationID " << id.first << "," |
428 << id.second << " not found."; | 442 << id.second << " not found."; |
429 return; | 443 return; |
430 } | 444 } |
431 | 445 |
432 if (buffer.get() == NULL) { | 446 if (buffer.get() == NULL) { |
433 LOG(ERROR) << "Error reading translated nexe"; | 447 LOG(ERROR) << "Error reading translated nexe"; |
434 return; | 448 return; |
435 } | 449 } |
436 | 450 pending_backend_operations_++; |
437 disk_cache_->StoreNexe(it->second.cache_key, | 451 disk_cache_->StoreNexe(it->second.cache_key, |
438 buffer, | 452 buffer, |
439 base::Bind(&PnaclHost::OnTranslatedNexeStored, | 453 base::Bind(&PnaclHost::OnTranslatedNexeStored, |
440 weak_factory_.GetWeakPtr(), | 454 weak_factory_.GetWeakPtr(), |
441 it->first)); | 455 it->first)); |
442 } | 456 } |
443 | 457 |
444 // After we know the nexe has been stored, we can clean up, and unblock any | 458 // After we know the nexe has been stored, we can clean up, and unblock any |
445 // outstanding requests for the same file. | 459 // outstanding requests for the same file. |
446 // (Bound callbacks must re-lookup the TranslationID because the translation | 460 // (Bound callbacks must re-lookup the TranslationID because the translation |
447 // could be cancelled before they get called). | 461 // could be cancelled before they get called). |
448 void PnaclHost::OnTranslatedNexeStored(const TranslationID& id, int net_error) { | 462 void PnaclHost::OnTranslatedNexeStored(const TranslationID& id, int net_error) { |
449 PendingTranslationMap::iterator entry(pending_translations_.find(id)); | 463 PendingTranslationMap::iterator entry(pending_translations_.find(id)); |
464 pending_backend_operations_--; | |
450 if (entry == pending_translations_.end()) { | 465 if (entry == pending_translations_.end()) { |
466 // If the renderer closed while we were storing the nexe, we land here. | |
467 // Make sure we try to de-init. | |
468 DeInitIfSafe(); | |
451 return; | 469 return; |
452 } | 470 } |
453 std::string key(entry->second.cache_key); | 471 std::string key(entry->second.cache_key); |
454 pending_translations_.erase(entry); | 472 pending_translations_.erase(entry); |
455 RequeryMatchingTranslations(key); | 473 RequeryMatchingTranslations(key); |
456 } | 474 } |
457 | 475 |
458 // Check if any pending translations match |key|. If so, re-issue the cache | 476 // Check if any pending translations match |key|. If so, re-issue the cache |
459 // query. In the overlapped miss case, we expect a hit this time, but a miss | 477 // query. In the overlapped miss case, we expect a hit this time, but a miss |
460 // is also possible in case of an error. | 478 // is also possible in case of an error. |
461 void PnaclHost::RequeryMatchingTranslations(const std::string& key) { | 479 void PnaclHost::RequeryMatchingTranslations(const std::string& key) { |
462 // Check for outstanding misses to this same file | 480 // Check for outstanding misses to this same file |
463 for (PendingTranslationMap::iterator it = pending_translations_.begin(); | 481 for (PendingTranslationMap::iterator it = pending_translations_.begin(); |
464 it != pending_translations_.end(); | 482 it != pending_translations_.end(); |
465 ++it) { | 483 ++it) { |
466 if (it->second.cache_key == key) { | 484 if (it->second.cache_key == key) { |
467 // Re-send the cache read request. This time we expect a hit, but if | 485 // Re-send the cache read request. This time we expect a hit, but if |
468 // something goes wrong, it will just handle it like a miss. | 486 // something goes wrong, it will just handle it like a miss. |
469 it->second.got_cache_reply = false; | 487 it->second.got_cache_reply = false; |
488 pending_backend_operations_++; | |
470 disk_cache_->GetNexe(key, | 489 disk_cache_->GetNexe(key, |
471 base::Bind(&PnaclHost::OnCacheQueryReturn, | 490 base::Bind(&PnaclHost::OnCacheQueryReturn, |
472 weak_factory_.GetWeakPtr(), | 491 weak_factory_.GetWeakPtr(), |
473 it->first)); | 492 it->first)); |
474 } | 493 } |
475 } | 494 } |
476 } | 495 } |
477 | 496 |
478 //////////////////// GetNexeFd hit path | 497 //////////////////// GetNexeFd hit path |
479 | 498 |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
528 base::Bind(base::IgnoreResult(base::ClosePlatformFile), | 547 base::Bind(base::IgnoreResult(base::ClosePlatformFile), |
529 to_erase->second.nexe_fd)); | 548 to_erase->second.nexe_fd)); |
530 std::string key(to_erase->second.cache_key); | 549 std::string key(to_erase->second.cache_key); |
531 bool may_be_cached = TranslationMayBeCached(to_erase); | 550 bool may_be_cached = TranslationMayBeCached(to_erase); |
532 pending_translations_.erase(to_erase); | 551 pending_translations_.erase(to_erase); |
533 // No translations will be waiting for entries that will not be stored. | 552 // No translations will be waiting for entries that will not be stored. |
534 if (may_be_cached) | 553 if (may_be_cached) |
535 RequeryMatchingTranslations(key); | 554 RequeryMatchingTranslations(key); |
536 } | 555 } |
537 } | 556 } |
538 if (pending_translations_.empty()) { | 557 BrowserThread::PostTask( |
539 cache_state_ = CacheUninitialized; | 558 BrowserThread::IO, |
540 // Freeing the backend causes it to flush to disk, so do it when the | 559 FROM_HERE, |
541 // last renderer closes rather than on destruction. | 560 base::Bind(&PnaclHost::DeInitIfSafe, weak_factory_.GetWeakPtr())); |
542 disk_cache_.reset(); | |
543 } | |
544 } | 561 } |
545 | 562 |
546 ////////////////// Cache data removal | 563 ////////////////// Cache data removal |
547 void PnaclHost::ClearTranslationCacheEntriesBetween( | 564 void PnaclHost::ClearTranslationCacheEntriesBetween( |
548 base::Time initial_time, | 565 base::Time initial_time, |
549 base::Time end_time, | 566 base::Time end_time, |
550 const base::Closure& callback) { | 567 const base::Closure& callback) { |
551 DCHECK(thread_checker_.CalledOnValidThread()); | 568 DCHECK(thread_checker_.CalledOnValidThread()); |
552 if (cache_state_ == CacheUninitialized) { | 569 if (cache_state_ == CacheUninitialized) { |
553 Init(); | 570 Init(); |
554 } | 571 } |
555 if (cache_state_ == CacheInitializing) { | 572 if (cache_state_ == CacheInitializing) { |
556 // If the backend hasn't yet initialized, try the request again later. | 573 // If the backend hasn't yet initialized, try the request again later. |
557 BrowserThread::PostDelayedTask( | 574 BrowserThread::PostDelayedTask( |
558 BrowserThread::IO, | 575 BrowserThread::IO, |
559 FROM_HERE, | 576 FROM_HERE, |
560 base::Bind(&PnaclHost::ClearTranslationCacheEntriesBetween, | 577 base::Bind(&PnaclHost::ClearTranslationCacheEntriesBetween, |
561 weak_factory_.GetWeakPtr(), | 578 weak_factory_.GetWeakPtr(), |
562 initial_time, | 579 initial_time, |
563 end_time, | 580 end_time, |
564 callback), | 581 callback), |
565 base::TimeDelta::FromMilliseconds( | 582 base::TimeDelta::FromMilliseconds( |
566 kTranslationCacheInitializationDelayMs)); | 583 kTranslationCacheInitializationDelayMs)); |
567 return; | 584 return; |
568 } | 585 } |
586 pending_backend_operations_++; | |
569 int rv = disk_cache_->DoomEntriesBetween( | 587 int rv = disk_cache_->DoomEntriesBetween( |
570 initial_time, | 588 initial_time, |
571 end_time, | 589 end_time, |
572 base::Bind(&PnaclHost::OnEntriesDoomed, callback)); | 590 base::Bind( |
591 &PnaclHost::OnEntriesDoomed, weak_factory_.GetWeakPtr(), callback)); | |
573 if (rv != net::ERR_IO_PENDING) | 592 if (rv != net::ERR_IO_PENDING) |
574 OnEntriesDoomed(callback, rv); | 593 OnEntriesDoomed(callback, rv); |
575 } | 594 } |
576 | 595 |
577 // static | |
578 void PnaclHost::OnEntriesDoomed(const base::Closure& callback, int net_error) { | 596 void PnaclHost::OnEntriesDoomed(const base::Closure& callback, int net_error) { |
597 DCHECK(thread_checker_.CalledOnValidThread()); | |
579 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, callback); | 598 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, callback); |
599 pending_backend_operations_--; | |
600 // When clearing the cache, the UI is blocked on all the cache-clearing | |
601 // operations, and freeing the backend actually blocks the IO thread. So | |
602 // instead of calling DeInitIfSafe directly, post it for later. | |
603 BrowserThread::PostTask( | |
604 BrowserThread::IO, | |
605 FROM_HERE, | |
606 base::Bind(&PnaclHost::DeInitIfSafe, weak_factory_.GetWeakPtr())); | |
580 } | 607 } |
608 | |
609 // Destroying the cache backend causes it to post tasks to the cache thread to | |
610 // flush to disk. Because PnaclHost is a singleton, it does not get destroyed | |
611 // until all the browser threads have gone away and it's too late to post | |
612 // anything (attempting to do so hangs shutdown). So we make sure to destroy it | |
613 // when we no longer have any outstanding operations that need it. These include | |
614 // pending translations, cache clear requests, and requests to read or write | |
615 // translated nexes. We check when renderers close, when cache clear requests | |
616 // finish, and when backend operations complete. | |
617 | |
618 // It is not safe to delete the backend while it is initializing, nor if it has | |
619 // outstanding entry open requests; it is in theory safe to delete it with | |
620 // outstanding read/write requests, but because that distinction is hidden | |
621 // inside PnaclTranslationCache, we do not delete the backend if there are any | |
622 // backend requests in flight. As a last resort in the destructor, we just leak | |
623 // the backend to avoid hanging shutdown. | |
624 void PnaclHost::DeInitIfSafe() { | |
625 if (pending_translations_.empty() && pending_backend_operations_ <= 0) { | |
jvoung (off chromium)
2013/10/24 21:14:58
Should just be pending_backend_operations_ == 0? S
Derek Schuff
2013/10/25 19:38:06
Yeah, it's bad and definitely not expected, the qu
jvoung (off chromium)
2013/10/25 19:51:43
Maybe you could DCHECK() that it's never < 0 for t
Derek Schuff
2013/10/25 19:57:09
good idea, done.
| |
626 cache_state_ = CacheUninitialized; | |
627 disk_cache_.reset(); | |
628 } | |
629 } | |
OLD | NEW |