pnacl_host.cc 24 KB

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