simple_file_tracker.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. // Copyright 2017 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_file_tracker.h"
  5. #include <algorithm>
  6. #include <limits>
  7. #include <memory>
  8. #include <utility>
  9. #include "base/files/file.h"
  10. #include "base/logging.h"
  11. #include "base/metrics/histogram_macros.h"
  12. #include "base/synchronization/lock.h"
  13. #include "net/disk_cache/disk_cache.h"
  14. #include "net/disk_cache/simple/simple_histogram_enums.h"
  15. #include "net/disk_cache/simple/simple_synchronous_entry.h"
  16. namespace disk_cache {
  17. namespace {
  18. void RecordFileDescripterLimiterOp(FileDescriptorLimiterOp op) {
  19. UMA_HISTOGRAM_ENUMERATION("SimpleCache.FileDescriptorLimiterAction", op,
  20. FD_LIMIT_OP_MAX);
  21. }
  22. } // namespace
  23. SimpleFileTracker::SimpleFileTracker(int file_limit)
  24. : file_limit_(file_limit) {}
  25. SimpleFileTracker::~SimpleFileTracker() {
  26. DCHECK(lru_.empty());
  27. DCHECK(tracked_files_.empty());
  28. }
  29. void SimpleFileTracker::Register(const SimpleSynchronousEntry* owner,
  30. SubFile subfile,
  31. std::unique_ptr<base::File> file) {
  32. DCHECK(file->IsValid());
  33. std::vector<std::unique_ptr<base::File>> files_to_close;
  34. {
  35. base::AutoLock hold_lock(lock_);
  36. // Make sure the list of everything with given hash exists.
  37. auto insert_status = tracked_files_.insert(
  38. std::make_pair(owner->entry_file_key().entry_hash,
  39. std::vector<std::unique_ptr<TrackedFiles>>()));
  40. std::vector<std::unique_ptr<TrackedFiles>>& candidates =
  41. insert_status.first->second;
  42. // See if entry for |owner| already exists, if not append.
  43. TrackedFiles* owners_files = nullptr;
  44. for (const std::unique_ptr<TrackedFiles>& candidate : candidates) {
  45. if (candidate->owner == owner) {
  46. owners_files = candidate.get();
  47. break;
  48. }
  49. }
  50. if (!owners_files) {
  51. candidates.emplace_back(std::make_unique<TrackedFiles>());
  52. owners_files = candidates.back().get();
  53. owners_files->owner = owner;
  54. owners_files->key = owner->entry_file_key();
  55. }
  56. EnsureInFrontOfLRU(owners_files);
  57. int file_index = static_cast<int>(subfile);
  58. DCHECK_EQ(TrackedFiles::TF_NO_REGISTRATION,
  59. owners_files->state[file_index]);
  60. owners_files->files[file_index] = std::move(file);
  61. owners_files->state[file_index] = TrackedFiles::TF_REGISTERED;
  62. ++open_files_;
  63. CloseFilesIfTooManyOpen(&files_to_close);
  64. }
  65. }
  66. SimpleFileTracker::FileHandle SimpleFileTracker::Acquire(
  67. BackendFileOperations* file_operations,
  68. const SimpleSynchronousEntry* owner,
  69. SubFile subfile) {
  70. std::vector<std::unique_ptr<base::File>> files_to_close;
  71. {
  72. base::AutoLock hold_lock(lock_);
  73. TrackedFiles* owners_files = Find(owner);
  74. int file_index = static_cast<int>(subfile);
  75. DCHECK_EQ(TrackedFiles::TF_REGISTERED, owners_files->state[file_index]);
  76. owners_files->state[file_index] = TrackedFiles::TF_ACQUIRED;
  77. EnsureInFrontOfLRU(owners_files);
  78. // Check to see if we have to reopen the file. That might push us over the
  79. // fd limit. CloseFilesIfTooManyOpen will not close anything in
  80. // |*owners_files| since it's already in the the TF_ACQUIRED state.
  81. if (owners_files->files[file_index] == nullptr) {
  82. ReopenFile(file_operations, owners_files, subfile);
  83. CloseFilesIfTooManyOpen(&files_to_close);
  84. }
  85. return FileHandle(this, owner, subfile,
  86. owners_files->files[file_index].get());
  87. }
  88. }
  89. SimpleFileTracker::TrackedFiles::TrackedFiles() {
  90. std::fill(state, state + kSimpleEntryTotalFileCount, TF_NO_REGISTRATION);
  91. }
  92. SimpleFileTracker::TrackedFiles::~TrackedFiles() = default;
  93. bool SimpleFileTracker::TrackedFiles::Empty() const {
  94. for (State s : state)
  95. if (s != TF_NO_REGISTRATION)
  96. return false;
  97. return true;
  98. }
  99. bool SimpleFileTracker::TrackedFiles::HasOpenFiles() const {
  100. for (const std::unique_ptr<base::File>& file : files)
  101. if (file != nullptr)
  102. return true;
  103. return false;
  104. }
  105. void SimpleFileTracker::Release(const SimpleSynchronousEntry* owner,
  106. SubFile subfile) {
  107. std::vector<std::unique_ptr<base::File>> files_to_close;
  108. {
  109. base::AutoLock hold_lock(lock_);
  110. TrackedFiles* owners_files = Find(owner);
  111. int file_index = static_cast<int>(subfile);
  112. DCHECK(owners_files->state[file_index] == TrackedFiles::TF_ACQUIRED ||
  113. owners_files->state[file_index] ==
  114. TrackedFiles::TF_ACQUIRED_PENDING_CLOSE);
  115. // Prepare to executed deferred close, if any.
  116. if (owners_files->state[file_index] ==
  117. TrackedFiles::TF_ACQUIRED_PENDING_CLOSE) {
  118. files_to_close.push_back(PrepareClose(owners_files, file_index));
  119. } else {
  120. owners_files->state[file_index] = TrackedFiles::TF_REGISTERED;
  121. }
  122. // It's possible that we were over limit and couldn't do much about it
  123. // since everything was lent out, so now may be the time to close extra
  124. // stuff.
  125. CloseFilesIfTooManyOpen(&files_to_close);
  126. }
  127. }
  128. void SimpleFileTracker::Close(const SimpleSynchronousEntry* owner,
  129. SubFile subfile) {
  130. std::unique_ptr<base::File> file_to_close;
  131. {
  132. base::AutoLock hold_lock(lock_);
  133. TrackedFiles* owners_files = Find(owner);
  134. int file_index = static_cast<int>(subfile);
  135. DCHECK(owners_files->state[file_index] == TrackedFiles::TF_ACQUIRED ||
  136. owners_files->state[file_index] == TrackedFiles::TF_REGISTERED);
  137. if (owners_files->state[file_index] == TrackedFiles::TF_ACQUIRED) {
  138. // The FD is currently acquired, so we can't clean up the TrackedFiles,
  139. // just yet; even if this is the last close, so delay the close until it
  140. // gets released.
  141. owners_files->state[file_index] = TrackedFiles::TF_ACQUIRED_PENDING_CLOSE;
  142. } else {
  143. file_to_close = PrepareClose(owners_files, file_index);
  144. }
  145. }
  146. }
  147. void SimpleFileTracker::Doom(const SimpleSynchronousEntry* owner,
  148. EntryFileKey* key) {
  149. base::AutoLock hold_lock(lock_);
  150. auto iter = tracked_files_.find(key->entry_hash);
  151. DCHECK(iter != tracked_files_.end());
  152. uint64_t max_doom_gen = 0;
  153. for (const std::unique_ptr<TrackedFiles>& file_with_same_hash :
  154. iter->second) {
  155. max_doom_gen =
  156. std::max(max_doom_gen, file_with_same_hash->key.doom_generation);
  157. }
  158. // It would take >502 years to doom the same hash enough times (at 10^9 dooms
  159. // per second) to wrap the 64 bit counter. Still, if it does wrap around,
  160. // there is a security risk since we could confuse different keys.
  161. CHECK_NE(max_doom_gen, std::numeric_limits<uint64_t>::max());
  162. uint64_t new_doom_gen = max_doom_gen + 1;
  163. // Update external key.
  164. key->doom_generation = new_doom_gen;
  165. // Update our own.
  166. for (const std::unique_ptr<TrackedFiles>& file_with_same_hash :
  167. iter->second) {
  168. if (file_with_same_hash->owner == owner)
  169. file_with_same_hash->key.doom_generation = new_doom_gen;
  170. }
  171. }
  172. bool SimpleFileTracker::IsEmptyForTesting() {
  173. base::AutoLock hold_lock(lock_);
  174. return tracked_files_.empty() && lru_.empty();
  175. }
  176. SimpleFileTracker::TrackedFiles* SimpleFileTracker::Find(
  177. const SimpleSynchronousEntry* owner) {
  178. auto candidates = tracked_files_.find(owner->entry_file_key().entry_hash);
  179. DCHECK(candidates != tracked_files_.end());
  180. for (const auto& candidate : candidates->second) {
  181. if (candidate->owner == owner) {
  182. return candidate.get();
  183. }
  184. }
  185. LOG(DFATAL) << "SimpleFileTracker operation on non-found entry";
  186. return nullptr;
  187. }
  188. std::unique_ptr<base::File> SimpleFileTracker::PrepareClose(
  189. TrackedFiles* owners_files,
  190. int file_index) {
  191. std::unique_ptr<base::File> file_out =
  192. std::move(owners_files->files[file_index]);
  193. owners_files->state[file_index] = TrackedFiles::TF_NO_REGISTRATION;
  194. if (owners_files->Empty()) {
  195. auto iter = tracked_files_.find(owners_files->key.entry_hash);
  196. for (auto i = iter->second.begin(); i != iter->second.end(); ++i) {
  197. if ((*i).get() == owners_files) {
  198. if (owners_files->in_lru)
  199. lru_.erase(owners_files->position_in_lru);
  200. iter->second.erase(i);
  201. break;
  202. }
  203. }
  204. if (iter->second.empty())
  205. tracked_files_.erase(iter);
  206. }
  207. if (file_out != nullptr)
  208. --open_files_;
  209. return file_out;
  210. }
  211. void SimpleFileTracker::CloseFilesIfTooManyOpen(
  212. std::vector<std::unique_ptr<base::File>>* files_to_close) {
  213. auto i = lru_.end();
  214. while (open_files_ > file_limit_ && i != lru_.begin()) {
  215. --i; // Point to the actual entry.
  216. TrackedFiles* tracked_files = *i;
  217. DCHECK(tracked_files->in_lru);
  218. for (int j = 0; j < kSimpleEntryTotalFileCount; ++j) {
  219. if (tracked_files->state[j] == TrackedFiles::TF_REGISTERED &&
  220. tracked_files->files[j] != nullptr) {
  221. files_to_close->push_back(std::move(tracked_files->files[j]));
  222. --open_files_;
  223. RecordFileDescripterLimiterOp(FD_LIMIT_CLOSE_FILE);
  224. }
  225. }
  226. if (!tracked_files->HasOpenFiles()) {
  227. // If there is nothing here that can possibly be closed, remove this from
  228. // LRU for now so we don't have to rescan it next time we are here. If the
  229. // files get re-opened (in Acquire), it will get added back in.
  230. DCHECK_EQ(*tracked_files->position_in_lru, tracked_files);
  231. DCHECK(i == tracked_files->position_in_lru);
  232. // Note that we're erasing at i, which would make it invalid, so go back
  233. // one element ahead to we can decrement from that on next iteration.
  234. ++i;
  235. lru_.erase(tracked_files->position_in_lru);
  236. tracked_files->in_lru = false;
  237. }
  238. }
  239. }
  240. void SimpleFileTracker::ReopenFile(BackendFileOperations* file_operations,
  241. TrackedFiles* owners_files,
  242. SubFile subfile) {
  243. int file_index = static_cast<int>(subfile);
  244. DCHECK(owners_files->files[file_index] == nullptr);
  245. int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
  246. base::File::FLAG_WRITE | base::File::FLAG_WIN_SHARE_DELETE;
  247. base::FilePath file_path =
  248. owners_files->owner->GetFilenameForSubfile(subfile);
  249. owners_files->files[file_index] =
  250. std::make_unique<base::File>(file_operations->OpenFile(file_path, flags));
  251. if (owners_files->files[file_index]->IsValid()) {
  252. RecordFileDescripterLimiterOp(FD_LIMIT_REOPEN_FILE);
  253. ++open_files_;
  254. } else {
  255. owners_files->files[file_index] = nullptr;
  256. RecordFileDescripterLimiterOp(FD_LIMIT_FAIL_REOPEN_FILE);
  257. }
  258. }
  259. void SimpleFileTracker::EnsureInFrontOfLRU(TrackedFiles* owners_files) {
  260. if (!owners_files->in_lru) {
  261. lru_.push_front(owners_files);
  262. owners_files->position_in_lru = lru_.begin();
  263. owners_files->in_lru = true;
  264. } else if (owners_files->position_in_lru != lru_.begin()) {
  265. lru_.splice(lru_.begin(), lru_, owners_files->position_in_lru);
  266. }
  267. DCHECK_EQ(*owners_files->position_in_lru, owners_files);
  268. }
  269. SimpleFileTracker::FileHandle::FileHandle() = default;
  270. SimpleFileTracker::FileHandle::FileHandle(SimpleFileTracker* file_tracker,
  271. const SimpleSynchronousEntry* entry,
  272. SimpleFileTracker::SubFile subfile,
  273. base::File* file)
  274. : file_tracker_(file_tracker),
  275. entry_(entry),
  276. subfile_(subfile),
  277. file_(file) {}
  278. SimpleFileTracker::FileHandle::FileHandle(FileHandle&& other) {
  279. *this = std::move(other);
  280. }
  281. SimpleFileTracker::FileHandle::~FileHandle() {
  282. if (entry_)
  283. file_tracker_->Release(entry_, subfile_);
  284. }
  285. SimpleFileTracker::FileHandle& SimpleFileTracker::FileHandle::operator=(
  286. FileHandle&& other) {
  287. file_tracker_ = other.file_tracker_;
  288. entry_ = other.entry_;
  289. subfile_ = other.subfile_;
  290. file_ = other.file_;
  291. other.file_tracker_ = nullptr;
  292. other.entry_ = nullptr;
  293. other.file_ = nullptr;
  294. return *this;
  295. }
  296. base::File* SimpleFileTracker::FileHandle::operator->() const {
  297. return file_;
  298. }
  299. base::File* SimpleFileTracker::FileHandle::get() const {
  300. return file_;
  301. }
  302. bool SimpleFileTracker::FileHandle::IsOK() const {
  303. return file_ && file_->IsValid();
  304. }
  305. } // namespace disk_cache