simple_backend_impl.cc 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913
  1. // Copyright (c) 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 "net/disk_cache/simple/simple_backend_impl.h"
  5. #include <algorithm>
  6. #include <cstdlib>
  7. #include <functional>
  8. #include <limits>
  9. #include "base/callback_helpers.h"
  10. #include "base/task/thread_pool.h"
  11. #include "build/build_config.h"
  12. #if BUILDFLAG(IS_POSIX)
  13. #include <sys/resource.h>
  14. #endif
  15. #include "base/bind.h"
  16. #include "base/callback.h"
  17. #include "base/files/file_util.h"
  18. #include "base/lazy_instance.h"
  19. #include "base/location.h"
  20. #include "base/memory/ptr_util.h"
  21. #include "base/metrics/field_trial.h"
  22. #include "base/metrics/field_trial_params.h"
  23. #include "base/metrics/histogram_functions.h"
  24. #include "base/metrics/histogram_macros.h"
  25. #include "base/system/sys_info.h"
  26. #include "base/task/task_runner_util.h"
  27. #include "base/task/thread_pool/thread_pool_instance.h"
  28. #include "base/threading/sequenced_task_runner_handle.h"
  29. #include "base/time/time.h"
  30. #include "build/build_config.h"
  31. #include "net/base/net_errors.h"
  32. #include "net/base/prioritized_task_runner.h"
  33. #include "net/disk_cache/backend_cleanup_tracker.h"
  34. #include "net/disk_cache/cache_util.h"
  35. #include "net/disk_cache/simple/simple_entry_format.h"
  36. #include "net/disk_cache/simple/simple_entry_impl.h"
  37. #include "net/disk_cache/simple/simple_file_tracker.h"
  38. #include "net/disk_cache/simple/simple_histogram_macros.h"
  39. #include "net/disk_cache/simple/simple_index.h"
  40. #include "net/disk_cache/simple/simple_index_file.h"
  41. #include "net/disk_cache/simple/simple_synchronous_entry.h"
  42. #include "net/disk_cache/simple/simple_util.h"
  43. #include "net/disk_cache/simple/simple_version_upgrade.h"
  44. using base::FilePath;
  45. using base::Time;
  46. namespace disk_cache {
  47. namespace {
  48. // Maximum fraction of the cache that one entry can consume.
  49. const int kMaxFileRatio = 8;
  50. // Native code entries can be large. Rather than increasing the overall cache
  51. // size, allow an individual entry to occupy up to half of the cache.
  52. const int kMaxNativeCodeFileRatio = 2;
  53. // Overrides the above.
  54. const int64_t kMinFileSizeLimit = 5 * 1024 * 1024;
  55. // Global context of all the files we have open --- this permits some to be
  56. // closed on demand if too many FDs are being used, to avoid running out.
  57. base::LazyInstance<SimpleFileTracker>::Leaky g_simple_file_tracker =
  58. LAZY_INSTANCE_INITIALIZER;
  59. // Detects if the files in the cache directory match the current disk cache
  60. // backend type and version. If the directory contains no cache, occupies it
  61. // with the fresh structure.
  62. SimpleCacheConsistencyResult FileStructureConsistent(
  63. BackendFileOperations* file_operations,
  64. const base::FilePath& path) {
  65. if (!file_operations->PathExists(path) &&
  66. !file_operations->CreateDirectory(path)) {
  67. LOG(ERROR) << "Failed to create directory: " << path.LossyDisplayName();
  68. return SimpleCacheConsistencyResult::kCreateDirectoryFailed;
  69. }
  70. return disk_cache::UpgradeSimpleCacheOnDisk(file_operations, path);
  71. }
  72. // A context used by a BarrierCompletionCallback to track state.
  73. struct BarrierContext {
  74. explicit BarrierContext(net::CompletionOnceCallback final_callback,
  75. int expected)
  76. : final_callback_(std::move(final_callback)), expected(expected) {}
  77. net::CompletionOnceCallback final_callback_;
  78. const int expected;
  79. int count = 0;
  80. bool had_error = false;
  81. };
  82. void BarrierCompletionCallbackImpl(
  83. BarrierContext* context,
  84. int result) {
  85. DCHECK_GT(context->expected, context->count);
  86. if (context->had_error)
  87. return;
  88. if (result != net::OK) {
  89. context->had_error = true;
  90. std::move(context->final_callback_).Run(result);
  91. return;
  92. }
  93. ++context->count;
  94. if (context->count == context->expected)
  95. std::move(context->final_callback_).Run(net::OK);
  96. }
  97. // A barrier completion callback is a repeatable callback that waits for
  98. // |count| successful results before invoking |final_callback|. In the case of
  99. // an error, the first error is passed to |final_callback| and all others
  100. // are ignored.
  101. base::RepeatingCallback<void(int)> MakeBarrierCompletionCallback(
  102. int count,
  103. net::CompletionOnceCallback final_callback) {
  104. BarrierContext* context =
  105. new BarrierContext(std::move(final_callback), count);
  106. return base::BindRepeating(&BarrierCompletionCallbackImpl,
  107. base::Owned(context));
  108. }
  109. // A short bindable thunk that ensures a completion callback is always called
  110. // after running an operation asynchronously. Checks for backend liveness first.
  111. void RunOperationAndCallback(
  112. base::WeakPtr<SimpleBackendImpl> backend,
  113. base::OnceCallback<net::Error(net::CompletionOnceCallback)> operation,
  114. net::CompletionOnceCallback operation_callback) {
  115. if (!backend)
  116. return;
  117. auto split_callback = base::SplitOnceCallback(std::move(operation_callback));
  118. const int operation_result =
  119. std::move(operation).Run(std::move(split_callback.first));
  120. if (operation_result != net::ERR_IO_PENDING && split_callback.second)
  121. std::move(split_callback.second).Run(operation_result);
  122. }
  123. // Same but for things that work with EntryResult.
  124. void RunEntryResultOperationAndCallback(
  125. base::WeakPtr<SimpleBackendImpl> backend,
  126. base::OnceCallback<EntryResult(EntryResultCallback)> operation,
  127. EntryResultCallback operation_callback) {
  128. if (!backend)
  129. return;
  130. auto split_callback = base::SplitOnceCallback(std::move(operation_callback));
  131. EntryResult operation_result =
  132. std::move(operation).Run(std::move(split_callback.first));
  133. if (operation_result.net_error() != net::ERR_IO_PENDING &&
  134. split_callback.second) {
  135. std::move(split_callback.second).Run(std::move(operation_result));
  136. }
  137. }
  138. void RecordIndexLoad(net::CacheType cache_type,
  139. base::TimeTicks constructed_since,
  140. int result) {
  141. const base::TimeDelta creation_to_index = base::TimeTicks::Now() -
  142. constructed_since;
  143. if (result == net::OK) {
  144. SIMPLE_CACHE_UMA(TIMES, "CreationToIndex", cache_type, creation_to_index);
  145. } else {
  146. SIMPLE_CACHE_UMA(TIMES,
  147. "CreationToIndexFail", cache_type, creation_to_index);
  148. }
  149. }
  150. SimpleEntryImpl::OperationsMode CacheTypeToOperationsMode(net::CacheType type) {
  151. return (type == net::DISK_CACHE || type == net::GENERATED_BYTE_CODE_CACHE ||
  152. type == net::GENERATED_NATIVE_CODE_CACHE ||
  153. type == net::GENERATED_WEBUI_BYTE_CODE_CACHE)
  154. ? SimpleEntryImpl::OPTIMISTIC_OPERATIONS
  155. : SimpleEntryImpl::NON_OPTIMISTIC_OPERATIONS;
  156. }
  157. } // namespace
  158. class SimpleBackendImpl::ActiveEntryProxy
  159. : public SimpleEntryImpl::ActiveEntryProxy {
  160. public:
  161. ~ActiveEntryProxy() override {
  162. if (backend_) {
  163. DCHECK_EQ(1U, backend_->active_entries_.count(entry_hash_));
  164. backend_->active_entries_.erase(entry_hash_);
  165. }
  166. }
  167. static std::unique_ptr<SimpleEntryImpl::ActiveEntryProxy> Create(
  168. int64_t entry_hash,
  169. SimpleBackendImpl* backend) {
  170. return base::WrapUnique(new ActiveEntryProxy(entry_hash, backend));
  171. }
  172. private:
  173. ActiveEntryProxy(uint64_t entry_hash, SimpleBackendImpl* backend)
  174. : entry_hash_(entry_hash), backend_(backend->AsWeakPtr()) {}
  175. uint64_t entry_hash_;
  176. base::WeakPtr<SimpleBackendImpl> backend_;
  177. };
  178. SimpleBackendImpl::SimpleBackendImpl(
  179. scoped_refptr<BackendFileOperationsFactory> file_operations_factory,
  180. const FilePath& path,
  181. scoped_refptr<BackendCleanupTracker> cleanup_tracker,
  182. SimpleFileTracker* file_tracker,
  183. int64_t max_bytes,
  184. net::CacheType cache_type,
  185. net::NetLog* net_log)
  186. : Backend(cache_type),
  187. file_operations_factory_(
  188. file_operations_factory
  189. ? std::move(file_operations_factory)
  190. : base::MakeRefCounted<TrivialFileOperationsFactory>()),
  191. cleanup_tracker_(std::move(cleanup_tracker)),
  192. file_tracker_(file_tracker ? file_tracker
  193. : g_simple_file_tracker.Pointer()),
  194. path_(path),
  195. orig_max_size_(max_bytes),
  196. entry_operations_mode_(CacheTypeToOperationsMode(cache_type)),
  197. post_doom_waiting_(
  198. base::MakeRefCounted<SimplePostDoomWaiterTable>(cache_type)),
  199. net_log_(net_log) {
  200. // Treat negative passed-in sizes same as SetMaxSize would here and in other
  201. // backends, as default (if first call).
  202. if (orig_max_size_ < 0)
  203. orig_max_size_ = 0;
  204. }
  205. SimpleBackendImpl::~SimpleBackendImpl() {
  206. // Write the index out if there is a pending write from a
  207. // previous operation.
  208. if (index_->HasPendingWrite())
  209. index_->WriteToDisk(SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN);
  210. }
  211. void SimpleBackendImpl::SetTaskRunnerForTesting(
  212. scoped_refptr<base::SequencedTaskRunner> task_runner) {
  213. prioritized_task_runner_ =
  214. base::MakeRefCounted<net::PrioritizedTaskRunner>(kWorkerPoolTaskTraits);
  215. prioritized_task_runner_->SetTaskRunnerForTesting( // IN-TEST
  216. std::move(task_runner));
  217. }
  218. void SimpleBackendImpl::Init(CompletionOnceCallback completion_callback) {
  219. auto index_task_runner = base::ThreadPool::CreateSequencedTaskRunner(
  220. {base::MayBlock(), base::WithBaseSyncPrimitives(),
  221. base::TaskPriority::USER_BLOCKING,
  222. base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
  223. prioritized_task_runner_ =
  224. base::MakeRefCounted<net::PrioritizedTaskRunner>(kWorkerPoolTaskTraits);
  225. index_ = std::make_unique<SimpleIndex>(
  226. base::SequencedTaskRunnerHandle::Get(), cleanup_tracker_.get(), this,
  227. GetCacheType(),
  228. std::make_unique<SimpleIndexFile>(
  229. index_task_runner, file_operations_factory_, GetCacheType(), path_));
  230. index_->ExecuteWhenReady(
  231. base::BindOnce(&RecordIndexLoad, GetCacheType(), base::TimeTicks::Now()));
  232. auto file_operations = file_operations_factory_->Create(index_task_runner);
  233. index_task_runner->PostTaskAndReplyWithResult(
  234. FROM_HERE,
  235. base::BindOnce(&SimpleBackendImpl::InitCacheStructureOnDisk,
  236. std::move(file_operations), path_, orig_max_size_,
  237. GetCacheType()),
  238. base::BindOnce(&SimpleBackendImpl::InitializeIndex, AsWeakPtr(),
  239. std::move(completion_callback)));
  240. }
  241. bool SimpleBackendImpl::SetMaxSize(int64_t max_bytes) {
  242. if (max_bytes < 0)
  243. return false;
  244. orig_max_size_ = max_bytes;
  245. index_->SetMaxSize(max_bytes);
  246. return true;
  247. }
  248. int64_t SimpleBackendImpl::MaxFileSize() const {
  249. uint64_t file_size_ratio = GetCacheType() == net::GENERATED_NATIVE_CODE_CACHE
  250. ? kMaxNativeCodeFileRatio
  251. : kMaxFileRatio;
  252. return std::max(
  253. base::saturated_cast<int64_t>(index_->max_size() / file_size_ratio),
  254. kMinFileSizeLimit);
  255. }
  256. scoped_refptr<SimplePostDoomWaiterTable> SimpleBackendImpl::OnDoomStart(
  257. uint64_t entry_hash) {
  258. post_doom_waiting_->OnDoomStart(entry_hash);
  259. return post_doom_waiting_;
  260. }
  261. void SimpleBackendImpl::DoomEntries(std::vector<uint64_t>* entry_hashes,
  262. net::CompletionOnceCallback callback) {
  263. auto mass_doom_entry_hashes = std::make_unique<std::vector<uint64_t>>();
  264. mass_doom_entry_hashes->swap(*entry_hashes);
  265. std::vector<uint64_t> to_doom_individually_hashes;
  266. // For each of the entry hashes, there are two cases:
  267. // 1. There are corresponding entries in active set, pending doom, or both
  268. // sets, and so the hash should be doomed individually to avoid flakes.
  269. // 2. The hash is not in active use at all, so we can call
  270. // SimpleSynchronousEntry::DeleteEntrySetFiles and delete the files en
  271. // masse.
  272. for (int i = mass_doom_entry_hashes->size() - 1; i >= 0; --i) {
  273. const uint64_t entry_hash = (*mass_doom_entry_hashes)[i];
  274. if (!active_entries_.count(entry_hash) &&
  275. !post_doom_waiting_->Has(entry_hash)) {
  276. continue;
  277. }
  278. to_doom_individually_hashes.push_back(entry_hash);
  279. (*mass_doom_entry_hashes)[i] = mass_doom_entry_hashes->back();
  280. mass_doom_entry_hashes->resize(mass_doom_entry_hashes->size() - 1);
  281. }
  282. base::RepeatingCallback<void(int)> barrier_callback =
  283. MakeBarrierCompletionCallback(to_doom_individually_hashes.size() + 1,
  284. std::move(callback));
  285. for (std::vector<uint64_t>::const_iterator
  286. it = to_doom_individually_hashes.begin(),
  287. end = to_doom_individually_hashes.end();
  288. it != end; ++it) {
  289. const int doom_result = DoomEntryFromHash(*it, barrier_callback);
  290. DCHECK_EQ(net::ERR_IO_PENDING, doom_result);
  291. index_->Remove(*it);
  292. }
  293. for (std::vector<uint64_t>::const_iterator
  294. it = mass_doom_entry_hashes->begin(),
  295. end = mass_doom_entry_hashes->end();
  296. it != end; ++it) {
  297. index_->Remove(*it);
  298. OnDoomStart(*it);
  299. }
  300. // Taking this pointer here avoids undefined behaviour from calling
  301. // std::move() before mass_doom_entry_hashes.get().
  302. std::vector<uint64_t>* mass_doom_entry_hashes_ptr =
  303. mass_doom_entry_hashes.get();
  304. // We don't use priorities (i.e., `prioritized_task_runner_`) here because
  305. // we don't actually have them here (since this is for eviction based on
  306. // index).
  307. auto task_runner =
  308. base::ThreadPool::CreateSequencedTaskRunner(kWorkerPoolTaskTraits);
  309. task_runner->PostTaskAndReplyWithResult(
  310. FROM_HERE,
  311. base::BindOnce(&SimpleSynchronousEntry::DeleteEntrySetFiles,
  312. mass_doom_entry_hashes_ptr, path_,
  313. file_operations_factory_->CreateUnbound()),
  314. base::BindOnce(&SimpleBackendImpl::DoomEntriesComplete, AsWeakPtr(),
  315. std::move(mass_doom_entry_hashes), barrier_callback));
  316. }
  317. int32_t SimpleBackendImpl::GetEntryCount() const {
  318. // TODO(pasko): Use directory file count when index is not ready.
  319. return index_->GetEntryCount();
  320. }
  321. EntryResult SimpleBackendImpl::OpenEntry(const std::string& key,
  322. net::RequestPriority request_priority,
  323. EntryResultCallback callback) {
  324. const uint64_t entry_hash = simple_util::GetEntryHashKey(key);
  325. std::vector<SimplePostDoomWaiter>* post_doom = nullptr;
  326. scoped_refptr<SimpleEntryImpl> simple_entry = CreateOrFindActiveOrDoomedEntry(
  327. entry_hash, key, request_priority, &post_doom);
  328. if (!simple_entry) {
  329. if (post_doom->empty() &&
  330. entry_operations_mode_ == SimpleEntryImpl::OPTIMISTIC_OPERATIONS) {
  331. // The entry is doomed, and no other backend operations are queued for the
  332. // entry, thus the open must fail and it's safe to return synchronously.
  333. net::NetLogWithSource log_for_entry(net::NetLogWithSource::Make(
  334. net_log_, net::NetLogSourceType::DISK_CACHE_ENTRY));
  335. log_for_entry.AddEvent(
  336. net::NetLogEventType::SIMPLE_CACHE_ENTRY_OPEN_CALL);
  337. log_for_entry.AddEventWithNetErrorCode(
  338. net::NetLogEventType::SIMPLE_CACHE_ENTRY_OPEN_END, net::ERR_FAILED);
  339. return EntryResult::MakeError(net::ERR_FAILED);
  340. }
  341. base::OnceCallback<EntryResult(EntryResultCallback)> operation =
  342. base::BindOnce(&SimpleBackendImpl::OpenEntry, base::Unretained(this),
  343. key, request_priority);
  344. post_doom->emplace_back(base::BindOnce(&RunEntryResultOperationAndCallback,
  345. AsWeakPtr(), std::move(operation),
  346. std::move(callback)));
  347. return EntryResult::MakeError(net::ERR_IO_PENDING);
  348. }
  349. return simple_entry->OpenEntry(std::move(callback));
  350. }
  351. EntryResult SimpleBackendImpl::CreateEntry(
  352. const std::string& key,
  353. net::RequestPriority request_priority,
  354. EntryResultCallback callback) {
  355. DCHECK_LT(0u, key.size());
  356. const uint64_t entry_hash = simple_util::GetEntryHashKey(key);
  357. std::vector<SimplePostDoomWaiter>* post_doom = nullptr;
  358. scoped_refptr<SimpleEntryImpl> simple_entry = CreateOrFindActiveOrDoomedEntry(
  359. entry_hash, key, request_priority, &post_doom);
  360. // If couldn't grab an entry object due to pending doom, see if circumstances
  361. // are right for an optimistic create.
  362. if (!simple_entry) {
  363. simple_entry = MaybeOptimisticCreateForPostDoom(
  364. entry_hash, key, request_priority, post_doom);
  365. }
  366. // If that doesn't work either, retry this once doom is done.
  367. if (!simple_entry) {
  368. base::OnceCallback<EntryResult(EntryResultCallback)> operation =
  369. base::BindOnce(&SimpleBackendImpl::CreateEntry, base::Unretained(this),
  370. key, request_priority);
  371. post_doom->emplace_back(base::BindOnce(&RunEntryResultOperationAndCallback,
  372. AsWeakPtr(), std::move(operation),
  373. std::move(callback)));
  374. return EntryResult::MakeError(net::ERR_IO_PENDING);
  375. }
  376. return simple_entry->CreateEntry(std::move(callback));
  377. }
  378. EntryResult SimpleBackendImpl::OpenOrCreateEntry(
  379. const std::string& key,
  380. net::RequestPriority request_priority,
  381. EntryResultCallback callback) {
  382. DCHECK_LT(0u, key.size());
  383. const uint64_t entry_hash = simple_util::GetEntryHashKey(key);
  384. std::vector<SimplePostDoomWaiter>* post_doom = nullptr;
  385. scoped_refptr<SimpleEntryImpl> simple_entry = CreateOrFindActiveOrDoomedEntry(
  386. entry_hash, key, request_priority, &post_doom);
  387. // If couldn't grab an entry object due to pending doom, see if circumstances
  388. // are right for an optimistic create.
  389. if (!simple_entry) {
  390. simple_entry = MaybeOptimisticCreateForPostDoom(
  391. entry_hash, key, request_priority, post_doom);
  392. if (simple_entry) {
  393. return simple_entry->CreateEntry(std::move(callback));
  394. } else {
  395. // If that doesn't work either, retry this once doom is done.
  396. base::OnceCallback<EntryResult(EntryResultCallback)> operation =
  397. base::BindOnce(&SimpleBackendImpl::OpenOrCreateEntry,
  398. base::Unretained(this), key, request_priority);
  399. post_doom->emplace_back(
  400. base::BindOnce(&RunEntryResultOperationAndCallback, AsWeakPtr(),
  401. std::move(operation), std::move(callback)));
  402. return EntryResult::MakeError(net::ERR_IO_PENDING);
  403. }
  404. }
  405. return simple_entry->OpenOrCreateEntry(std::move(callback));
  406. }
  407. scoped_refptr<SimpleEntryImpl>
  408. SimpleBackendImpl::MaybeOptimisticCreateForPostDoom(
  409. uint64_t entry_hash,
  410. const std::string& key,
  411. net::RequestPriority request_priority,
  412. std::vector<SimplePostDoomWaiter>* post_doom) {
  413. scoped_refptr<SimpleEntryImpl> simple_entry;
  414. // We would like to optimistically have create go ahead, for benefit of
  415. // HTTP cache use. This can only be sanely done if we are the only op
  416. // serialized after doom's completion.
  417. if (post_doom->empty() &&
  418. entry_operations_mode_ == SimpleEntryImpl::OPTIMISTIC_OPERATIONS) {
  419. simple_entry = base::MakeRefCounted<SimpleEntryImpl>(
  420. GetCacheType(), path_, cleanup_tracker_.get(), entry_hash,
  421. entry_operations_mode_, this, file_tracker_, file_operations_factory_,
  422. net_log_, GetNewEntryPriority(request_priority));
  423. simple_entry->SetKey(key);
  424. simple_entry->SetActiveEntryProxy(
  425. ActiveEntryProxy::Create(entry_hash, this));
  426. simple_entry->SetCreatePendingDoom();
  427. std::pair<EntryMap::iterator, bool> insert_result = active_entries_.insert(
  428. EntryMap::value_type(entry_hash, simple_entry.get()));
  429. post_doom->emplace_back(base::BindOnce(
  430. &SimpleEntryImpl::NotifyDoomBeforeCreateComplete, simple_entry));
  431. DCHECK(insert_result.second);
  432. }
  433. return simple_entry;
  434. }
  435. net::Error SimpleBackendImpl::DoomEntry(const std::string& key,
  436. net::RequestPriority priority,
  437. CompletionOnceCallback callback) {
  438. const uint64_t entry_hash = simple_util::GetEntryHashKey(key);
  439. std::vector<SimplePostDoomWaiter>* post_doom = nullptr;
  440. scoped_refptr<SimpleEntryImpl> simple_entry =
  441. CreateOrFindActiveOrDoomedEntry(entry_hash, key, priority, &post_doom);
  442. if (!simple_entry) {
  443. // At first glance, it appears exceedingly silly to queue up a doom
  444. // when we get here because the files corresponding to our key are being
  445. // deleted... but it's possible that one of the things in post_doom is a
  446. // create for our key, in which case we still have work to do.
  447. base::OnceCallback<net::Error(CompletionOnceCallback)> operation =
  448. base::BindOnce(&SimpleBackendImpl::DoomEntry, base::Unretained(this),
  449. key, priority);
  450. post_doom->emplace_back(base::BindOnce(&RunOperationAndCallback,
  451. AsWeakPtr(), std::move(operation),
  452. std::move(callback)));
  453. return net::ERR_IO_PENDING;
  454. }
  455. return simple_entry->DoomEntry(std::move(callback));
  456. }
  457. net::Error SimpleBackendImpl::DoomAllEntries(CompletionOnceCallback callback) {
  458. return DoomEntriesBetween(Time(), Time(), std::move(callback));
  459. }
  460. net::Error SimpleBackendImpl::DoomEntriesBetween(
  461. const Time initial_time,
  462. const Time end_time,
  463. CompletionOnceCallback callback) {
  464. index_->ExecuteWhenReady(base::BindOnce(&SimpleBackendImpl::IndexReadyForDoom,
  465. AsWeakPtr(), initial_time, end_time,
  466. std::move(callback)));
  467. return net::ERR_IO_PENDING;
  468. }
  469. net::Error SimpleBackendImpl::DoomEntriesSince(
  470. const Time initial_time,
  471. CompletionOnceCallback callback) {
  472. return DoomEntriesBetween(initial_time, Time(), std::move(callback));
  473. }
  474. int64_t SimpleBackendImpl::CalculateSizeOfAllEntries(
  475. Int64CompletionOnceCallback callback) {
  476. index_->ExecuteWhenReady(
  477. base::BindOnce(&SimpleBackendImpl::IndexReadyForSizeCalculation,
  478. AsWeakPtr(), std::move(callback)));
  479. return net::ERR_IO_PENDING;
  480. }
  481. int64_t SimpleBackendImpl::CalculateSizeOfEntriesBetween(
  482. base::Time initial_time,
  483. base::Time end_time,
  484. Int64CompletionOnceCallback callback) {
  485. index_->ExecuteWhenReady(
  486. base::BindOnce(&SimpleBackendImpl::IndexReadyForSizeBetweenCalculation,
  487. AsWeakPtr(), initial_time, end_time, std::move(callback)));
  488. return net::ERR_IO_PENDING;
  489. }
  490. class SimpleBackendImpl::SimpleIterator final : public Iterator {
  491. public:
  492. explicit SimpleIterator(base::WeakPtr<SimpleBackendImpl> backend)
  493. : backend_(backend) {}
  494. // From Backend::Iterator:
  495. EntryResult OpenNextEntry(EntryResultCallback callback) override {
  496. if (!backend_)
  497. return EntryResult::MakeError(net::ERR_FAILED);
  498. CompletionOnceCallback open_next_entry_impl =
  499. base::BindOnce(&SimpleIterator::OpenNextEntryImpl,
  500. weak_factory_.GetWeakPtr(), std::move(callback));
  501. backend_->index_->ExecuteWhenReady(std::move(open_next_entry_impl));
  502. return EntryResult::MakeError(net::ERR_IO_PENDING);
  503. }
  504. void OpenNextEntryImpl(EntryResultCallback callback,
  505. int index_initialization_error_code) {
  506. if (!backend_) {
  507. std::move(callback).Run(EntryResult::MakeError(net::ERR_FAILED));
  508. return;
  509. }
  510. if (index_initialization_error_code != net::OK) {
  511. std::move(callback).Run(EntryResult::MakeError(
  512. static_cast<net::Error>(index_initialization_error_code)));
  513. return;
  514. }
  515. if (!hashes_to_enumerate_)
  516. hashes_to_enumerate_ = backend_->index()->GetAllHashes();
  517. while (!hashes_to_enumerate_->empty()) {
  518. uint64_t entry_hash = hashes_to_enumerate_->back();
  519. hashes_to_enumerate_->pop_back();
  520. if (backend_->index()->Has(entry_hash)) {
  521. auto split_callback = base::SplitOnceCallback(std::move(callback));
  522. callback = std::move(split_callback.first);
  523. EntryResultCallback continue_iteration = base::BindOnce(
  524. &SimpleIterator::CheckIterationReturnValue,
  525. weak_factory_.GetWeakPtr(), std::move(split_callback.second));
  526. EntryResult open_result = backend_->OpenEntryFromHash(
  527. entry_hash, std::move(continue_iteration));
  528. if (open_result.net_error() == net::ERR_IO_PENDING)
  529. return;
  530. if (open_result.net_error() != net::ERR_FAILED) {
  531. std::move(callback).Run(std::move(open_result));
  532. return;
  533. }
  534. }
  535. }
  536. std::move(callback).Run(EntryResult::MakeError(net::ERR_FAILED));
  537. }
  538. void CheckIterationReturnValue(EntryResultCallback callback,
  539. EntryResult result) {
  540. if (result.net_error() == net::ERR_FAILED) {
  541. OpenNextEntry(std::move(callback));
  542. return;
  543. }
  544. std::move(callback).Run(std::move(result));
  545. }
  546. private:
  547. base::WeakPtr<SimpleBackendImpl> backend_;
  548. std::unique_ptr<std::vector<uint64_t>> hashes_to_enumerate_;
  549. base::WeakPtrFactory<SimpleIterator> weak_factory_{this};
  550. };
  551. std::unique_ptr<Backend::Iterator> SimpleBackendImpl::CreateIterator() {
  552. return std::make_unique<SimpleIterator>(AsWeakPtr());
  553. }
  554. void SimpleBackendImpl::GetStats(base::StringPairs* stats) {
  555. std::pair<std::string, std::string> item;
  556. item.first = "Cache type";
  557. item.second = "Simple Cache";
  558. stats->push_back(item);
  559. }
  560. void SimpleBackendImpl::OnExternalCacheHit(const std::string& key) {
  561. index_->UseIfExists(simple_util::GetEntryHashKey(key));
  562. }
  563. uint8_t SimpleBackendImpl::GetEntryInMemoryData(const std::string& key) {
  564. const uint64_t entry_hash = simple_util::GetEntryHashKey(key);
  565. return index_->GetEntryInMemoryData(entry_hash);
  566. }
  567. void SimpleBackendImpl::SetEntryInMemoryData(const std::string& key,
  568. uint8_t data) {
  569. const uint64_t entry_hash = simple_util::GetEntryHashKey(key);
  570. index_->SetEntryInMemoryData(entry_hash, data);
  571. }
  572. void SimpleBackendImpl::InitializeIndex(CompletionOnceCallback callback,
  573. const DiskStatResult& result) {
  574. if (result.net_error == net::OK) {
  575. index_->SetMaxSize(result.max_size);
  576. #if BUILDFLAG(IS_ANDROID)
  577. if (app_status_listener_)
  578. index_->set_app_status_listener(app_status_listener_);
  579. #endif
  580. index_->Initialize(result.cache_dir_mtime);
  581. }
  582. std::move(callback).Run(result.net_error);
  583. }
  584. void SimpleBackendImpl::IndexReadyForDoom(Time initial_time,
  585. Time end_time,
  586. CompletionOnceCallback callback,
  587. int result) {
  588. if (result != net::OK) {
  589. std::move(callback).Run(result);
  590. return;
  591. }
  592. std::unique_ptr<std::vector<uint64_t>> removed_key_hashes(
  593. index_->GetEntriesBetween(initial_time, end_time).release());
  594. DoomEntries(removed_key_hashes.get(), std::move(callback));
  595. }
  596. void SimpleBackendImpl::IndexReadyForSizeCalculation(
  597. Int64CompletionOnceCallback callback,
  598. int result) {
  599. int64_t rv = result == net::OK ? index_->GetCacheSize() : result;
  600. std::move(callback).Run(rv);
  601. }
  602. void SimpleBackendImpl::IndexReadyForSizeBetweenCalculation(
  603. base::Time initial_time,
  604. base::Time end_time,
  605. Int64CompletionOnceCallback callback,
  606. int result) {
  607. int64_t rv = result == net::OK
  608. ? index_->GetCacheSizeBetween(initial_time, end_time)
  609. : result;
  610. std::move(callback).Run(rv);
  611. }
  612. // static
  613. SimpleBackendImpl::DiskStatResult SimpleBackendImpl::InitCacheStructureOnDisk(
  614. std::unique_ptr<BackendFileOperations> file_operations,
  615. const base::FilePath& path,
  616. uint64_t suggested_max_size,
  617. net::CacheType cache_type) {
  618. DiskStatResult result;
  619. result.max_size = suggested_max_size;
  620. result.net_error = net::OK;
  621. SimpleCacheConsistencyResult consistency =
  622. FileStructureConsistent(file_operations.get(), path);
  623. SIMPLE_CACHE_UMA(ENUMERATION, "ConsistencyResult", cache_type, consistency);
  624. // If the cache structure is inconsistent make a single attempt at
  625. // recovering it. Previously there were bugs that could cause a partially
  626. // written fake index file to be left in an otherwise empty cache. In
  627. // that case we can delete the index files and start over. Also, some
  628. // consistency failures may leave an empty directory directly and we can
  629. // retry those cases as well.
  630. if (consistency != SimpleCacheConsistencyResult::kOK) {
  631. bool deleted_files = disk_cache::DeleteIndexFilesIfCacheIsEmpty(path);
  632. SIMPLE_CACHE_UMA(BOOLEAN, "DidDeleteIndexFilesAfterFailedConsistency",
  633. cache_type, deleted_files);
  634. if (base::IsDirectoryEmpty(path)) {
  635. SimpleCacheConsistencyResult orig_consistency = consistency;
  636. consistency = FileStructureConsistent(file_operations.get(), path);
  637. SIMPLE_CACHE_UMA(ENUMERATION, "RetryConsistencyResult", cache_type,
  638. consistency);
  639. if (consistency == SimpleCacheConsistencyResult::kOK) {
  640. SIMPLE_CACHE_UMA(ENUMERATION,
  641. "OriginalConsistencyResultBeforeSuccessfulRetry",
  642. cache_type, orig_consistency);
  643. }
  644. }
  645. if (deleted_files) {
  646. SIMPLE_CACHE_UMA(ENUMERATION, "ConsistencyResultAfterIndexFilesDeleted",
  647. cache_type, consistency);
  648. }
  649. }
  650. if (consistency != SimpleCacheConsistencyResult::kOK) {
  651. LOG(ERROR) << "Simple Cache Backend: wrong file structure on disk: "
  652. << static_cast<int>(consistency)
  653. << " path: " << path.LossyDisplayName();
  654. result.net_error = net::ERR_FAILED;
  655. } else {
  656. absl::optional<base::File::Info> file_info =
  657. file_operations->GetFileInfo(path);
  658. if (!file_info.has_value()) {
  659. // Something deleted the directory between when we set it up and the
  660. // mstat; this is not uncommon on some test fixtures which erase their
  661. // tempdir while some worker threads may still be running.
  662. LOG(ERROR) << "Simple Cache Backend: cache directory inaccessible right "
  663. "after creation; path: "
  664. << path.LossyDisplayName();
  665. result.net_error = net::ERR_FAILED;
  666. } else {
  667. result.cache_dir_mtime = file_info->last_modified;
  668. if (!result.max_size) {
  669. int64_t available = base::SysInfo::AmountOfFreeDiskSpace(path);
  670. result.max_size = disk_cache::PreferredCacheSize(available, cache_type);
  671. DCHECK(result.max_size);
  672. }
  673. }
  674. }
  675. return result;
  676. }
  677. scoped_refptr<SimpleEntryImpl>
  678. SimpleBackendImpl::CreateOrFindActiveOrDoomedEntry(
  679. const uint64_t entry_hash,
  680. const std::string& key,
  681. net::RequestPriority request_priority,
  682. std::vector<SimplePostDoomWaiter>** post_doom) {
  683. DCHECK_EQ(entry_hash, simple_util::GetEntryHashKey(key));
  684. // If there is a doom pending, we would want to serialize after it.
  685. *post_doom = post_doom_waiting_->Find(entry_hash);
  686. if (*post_doom)
  687. return nullptr;
  688. std::pair<EntryMap::iterator, bool> insert_result =
  689. active_entries_.insert(EntryMap::value_type(entry_hash, nullptr));
  690. EntryMap::iterator& it = insert_result.first;
  691. const bool did_insert = insert_result.second;
  692. if (did_insert) {
  693. SimpleEntryImpl* entry = it->second = new SimpleEntryImpl(
  694. GetCacheType(), path_, cleanup_tracker_.get(), entry_hash,
  695. entry_operations_mode_, this, file_tracker_, file_operations_factory_,
  696. net_log_, GetNewEntryPriority(request_priority));
  697. entry->SetKey(key);
  698. entry->SetActiveEntryProxy(ActiveEntryProxy::Create(entry_hash, this));
  699. }
  700. // TODO(jkarlin): In case of recycling a half-closed entry, we might want to
  701. // update its priority.
  702. DCHECK(it->second);
  703. // It's possible, but unlikely, that we have an entry hash collision with a
  704. // currently active entry.
  705. if (key != it->second->key()) {
  706. it->second->Doom();
  707. DCHECK_EQ(0U, active_entries_.count(entry_hash));
  708. DCHECK(post_doom_waiting_->Has(entry_hash));
  709. // Re-run ourselves to handle the now-pending doom.
  710. return CreateOrFindActiveOrDoomedEntry(entry_hash, key, request_priority,
  711. post_doom);
  712. }
  713. return base::WrapRefCounted(it->second);
  714. }
  715. EntryResult SimpleBackendImpl::OpenEntryFromHash(uint64_t entry_hash,
  716. EntryResultCallback callback) {
  717. std::vector<SimplePostDoomWaiter>* post_doom =
  718. post_doom_waiting_->Find(entry_hash);
  719. if (post_doom) {
  720. base::OnceCallback<EntryResult(EntryResultCallback)> operation =
  721. base::BindOnce(&SimpleBackendImpl::OpenEntryFromHash,
  722. base::Unretained(this), entry_hash);
  723. // TODO(https://crbug.com/1019682) The cancellation behavior looks wrong.
  724. post_doom->emplace_back(base::BindOnce(&RunEntryResultOperationAndCallback,
  725. AsWeakPtr(), std::move(operation),
  726. std::move(callback)));
  727. return EntryResult::MakeError(net::ERR_IO_PENDING);
  728. }
  729. auto has_active = active_entries_.find(entry_hash);
  730. if (has_active != active_entries_.end()) {
  731. return OpenEntry(has_active->second->key(), net::HIGHEST,
  732. std::move(callback));
  733. }
  734. auto simple_entry = base::MakeRefCounted<SimpleEntryImpl>(
  735. GetCacheType(), path_, cleanup_tracker_.get(), entry_hash,
  736. entry_operations_mode_, this, file_tracker_, file_operations_factory_,
  737. net_log_, GetNewEntryPriority(net::HIGHEST));
  738. EntryResultCallback backend_callback =
  739. base::BindOnce(&SimpleBackendImpl::OnEntryOpenedFromHash, AsWeakPtr(),
  740. entry_hash, simple_entry, std::move(callback));
  741. return simple_entry->OpenEntry(std::move(backend_callback));
  742. }
  743. net::Error SimpleBackendImpl::DoomEntryFromHash(
  744. uint64_t entry_hash,
  745. CompletionOnceCallback callback) {
  746. std::vector<SimplePostDoomWaiter>* post_doom =
  747. post_doom_waiting_->Find(entry_hash);
  748. if (post_doom) {
  749. base::OnceCallback<net::Error(CompletionOnceCallback)> operation =
  750. base::BindOnce(&SimpleBackendImpl::DoomEntryFromHash,
  751. base::Unretained(this), entry_hash);
  752. post_doom->emplace_back(base::BindOnce(&RunOperationAndCallback,
  753. AsWeakPtr(), std::move(operation),
  754. std::move(callback)));
  755. return net::ERR_IO_PENDING;
  756. }
  757. auto active_it = active_entries_.find(entry_hash);
  758. if (active_it != active_entries_.end())
  759. return active_it->second->DoomEntry(std::move(callback));
  760. // There's no pending dooms, nor any open entry. We can make a trivial
  761. // call to DoomEntries() to delete this entry.
  762. std::vector<uint64_t> entry_hash_vector;
  763. entry_hash_vector.push_back(entry_hash);
  764. DoomEntries(&entry_hash_vector, std::move(callback));
  765. return net::ERR_IO_PENDING;
  766. }
  767. void SimpleBackendImpl::OnEntryOpenedFromHash(
  768. uint64_t hash,
  769. const scoped_refptr<SimpleEntryImpl>& simple_entry,
  770. EntryResultCallback callback,
  771. EntryResult result) {
  772. if (result.net_error() != net::OK) {
  773. std::move(callback).Run(std::move(result));
  774. return;
  775. }
  776. std::pair<EntryMap::iterator, bool> insert_result =
  777. active_entries_.insert(EntryMap::value_type(hash, simple_entry.get()));
  778. EntryMap::iterator& it = insert_result.first;
  779. const bool did_insert = insert_result.second;
  780. if (did_insert) {
  781. // There was no active entry corresponding to this hash. We've already put
  782. // the entry opened from hash in the |active_entries_|. We now provide the
  783. // proxy object to the entry.
  784. it->second->SetActiveEntryProxy(ActiveEntryProxy::Create(hash, this));
  785. std::move(callback).Run(std::move(result));
  786. } else {
  787. // The entry was made active while we waiting for the open from hash to
  788. // finish. The entry created from hash needs to be closed, and the one
  789. // in |active_entries_| can be returned to the caller.
  790. Entry* entry_from_result = result.ReleaseEntry();
  791. DCHECK_EQ(entry_from_result, simple_entry.get());
  792. simple_entry->Close();
  793. EntryResult reopen_result = it->second->OpenEntry(std::move(callback));
  794. DCHECK_EQ(reopen_result.net_error(), net::ERR_IO_PENDING);
  795. }
  796. }
  797. void SimpleBackendImpl::DoomEntriesComplete(
  798. std::unique_ptr<std::vector<uint64_t>> entry_hashes,
  799. CompletionOnceCallback callback,
  800. int result) {
  801. for (const uint64_t& entry_hash : *entry_hashes)
  802. post_doom_waiting_->OnDoomComplete(entry_hash);
  803. std::move(callback).Run(result);
  804. }
  805. uint32_t SimpleBackendImpl::GetNewEntryPriority(
  806. net::RequestPriority request_priority) {
  807. // Lower priority is better, so give high network priority the least bump.
  808. return ((net::RequestPriority::MAXIMUM_PRIORITY - request_priority) * 10000) +
  809. entry_count_++;
  810. }
  811. } // namespace disk_cache