1 // Copyright 2017 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef NET_DISK_CACHE_SIMPLE_SIMPLE_FILE_TRACKER_H_ 6 #define NET_DISK_CACHE_SIMPLE_SIMPLE_FILE_TRACKER_H_ 7 8 #include <stdint.h> 9 #include <algorithm> 10 #include <list> 11 #include <memory> 12 #include <unordered_map> 13 #include <vector> 14 15 #include "base/files/file.h" 16 #include "base/memory/raw_ptr.h" 17 #include "base/synchronization/lock.h" 18 #include "net/base/net_export.h" 19 #include "net/disk_cache/simple/simple_entry_format.h" 20 21 namespace disk_cache { 22 23 class BackendFileOperations; 24 class SimpleSynchronousEntry; 25 26 // This keeps track of all the files SimpleCache has open, across all the 27 // backend instancess, in order to prevent us from running out of file 28 // descriptors. 29 // TODO(morlovich): Actually implement closing and re-opening of things if we 30 // run out. 31 // 32 // This class is thread-safe. 33 class NET_EXPORT_PRIVATE SimpleFileTracker { 34 public: 35 enum class SubFile { FILE_0, FILE_1, FILE_SPARSE }; 36 37 // A RAII helper that guards access to a file grabbed for use from 38 // SimpleFileTracker::Acquire(). While it's still alive, if IsOK() is true, 39 // then using the underlying base::File via get() or the -> operator will be 40 // safe. 41 // 42 // This class is movable but not copyable. It should only be used from a 43 // single logical sequence of execution, and should not outlive the 44 // corresponding SimpleSynchronousEntry. 45 class NET_EXPORT_PRIVATE FileHandle { 46 public: 47 FileHandle(); 48 FileHandle(FileHandle&& other); 49 50 FileHandle(const FileHandle&) = delete; 51 FileHandle& operator=(const FileHandle&) = delete; 52 53 ~FileHandle(); 54 FileHandle& operator=(FileHandle&& other); 55 base::File* operator->() const; 56 base::File* get() const; 57 // Returns true if this handle points to a valid file. This should normally 58 // be the first thing called on the object, after getting it from 59 // SimpleFileTracker::Acquire. 60 bool IsOK() const; 61 62 private: 63 friend class SimpleFileTracker; 64 FileHandle(SimpleFileTracker* file_tracker, 65 const SimpleSynchronousEntry* entry, 66 SimpleFileTracker::SubFile subfile, 67 base::File* file); 68 69 // All the pointer fields are nullptr in the default/moved away from form. 70 raw_ptr<SimpleFileTracker> file_tracker_ = nullptr; 71 raw_ptr<const SimpleSynchronousEntry> entry_ = nullptr; 72 SimpleFileTracker::SubFile subfile_; 73 raw_ptr<base::File> file_ = nullptr; 74 }; 75 76 struct EntryFileKey { 77 EntryFileKey() = default; EntryFileKeyEntryFileKey78 explicit EntryFileKey(uint64_t hash) : entry_hash(hash) {} 79 80 uint64_t entry_hash = 0; 81 82 // 0 means this a non-doomed, active entry, for its backend that will be 83 // checked on OpenEntry(key) where hash(key) = entry_hash. Other values of 84 // |doom_generation| are used to generate distinct file names for entries 85 // that have been Doom()ed, either by explicit API call by the client or 86 // internal operation (eviction, collisions, etc.) 87 uint64_t doom_generation = 0; 88 }; 89 90 // The default limit here is half of what's available on our target OS where 91 // Chrome has the lowest limit. 92 explicit SimpleFileTracker(int file_limit = 512); 93 94 SimpleFileTracker(const SimpleFileTracker&) = delete; 95 SimpleFileTracker& operator=(const SimpleFileTracker&) = delete; 96 97 ~SimpleFileTracker(); 98 99 // Established |file| as what's backing |subfile| for |owner|. This is 100 // intended to be called when SimpleSynchronousEntry first sets up the file to 101 // transfer its ownership to SimpleFileTracker. Any Register() call must be 102 // eventually followed by a corresponding Close() call before the |owner| is 103 // destroyed. |file->IsValid()| must be true. 104 void Register(const SimpleSynchronousEntry* owner, 105 SubFile subfile, 106 std::unique_ptr<base::File> file); 107 108 // Lends out a file to SimpleSynchronousEntry for use. SimpleFileTracker 109 // will ensure that it doesn't close the file until the handle is destroyed. 110 // The caller should check .IsOK() on the returned value before using it, as 111 // it's possible that the file had to be closed and re-opened due to FD 112 // pressure, and that open may have failed. This should not be called twice 113 // with the exact same arguments until the handle returned from the previous 114 // such call is destroyed. 115 FileHandle Acquire(BackendFileOperations* file_operations, 116 const SimpleSynchronousEntry* owner, 117 SubFile subfile); 118 119 // Tells SimpleFileTracker that SimpleSynchronousEntry will not be interested 120 // in the file further, so it can be closed and forgotten about. It's OK to 121 // call this while a handle to the file is alive, in which case the effect 122 // takes place after the handle is destroyed. 123 // If Close() has been called and the handle to the file is no longer alive, 124 // a new backing file can be established by calling Register() again. 125 void Close(const SimpleSynchronousEntry* owner, SubFile file); 126 127 // Updates key->doom_generation to one not in use for the hash; it's the 128 // caller's responsibility to update file names accordingly, and to prevent 129 // others from stomping on things while this is going on. SimpleBackendImpl's 130 // entries_pending_doom_ is the mechanism for protecting this action from 131 // races. 132 void Doom(const SimpleSynchronousEntry* owner, EntryFileKey* key); 133 134 // Returns true if there is no in-memory state around, e.g. everything got 135 // cleaned up. This is a test-only method since this object is expected to be 136 // shared between multiple threads, in which case its return value may be 137 // outdated the moment it's returned. 138 bool IsEmptyForTesting(); 139 140 private: 141 struct TrackedFiles { 142 // We can potentially run through this state machine multiple times for 143 // FILE_1, as that's often missing, so SimpleSynchronousEntry can sometimes 144 // close and remove the file for an empty stream, then re-open it on actual 145 // data. 146 enum State { 147 TF_NO_REGISTRATION = 0, 148 TF_REGISTERED = 1, 149 TF_ACQUIRED = 2, 150 TF_ACQUIRED_PENDING_CLOSE = 3, 151 }; 152 153 NET_EXPORT_PRIVATE TrackedFiles(); 154 NET_EXPORT_PRIVATE ~TrackedFiles(); 155 156 // True if this isn't keeping track of anything any more. 157 bool Empty() const; 158 159 // True if this has open files. Note that this is not the same as !Empty() 160 // as this may be false when an entry had its files temporarily closed, but 161 // is still relevant. 162 bool HasOpenFiles() const; 163 164 // We use pointers to SimpleSynchronousEntry two ways: 165 // 1) As opaque keys. This is handy as it avoids having to compare paths in 166 // case multiple backends use the same key. Since we access the 167 // bookkeeping under |lock_| 168 // 169 // 2) To get info on the caller of our operation. 170 // Accessing |owner| from any other TrackedFiles would be unsafe (as it 171 // may be doing its own thing in a different thread). 172 raw_ptr<const SimpleSynchronousEntry> owner; 173 EntryFileKey key; 174 175 // Some of these may be nullptr, if they are not open. Non-null pointers 176 // to files that are not valid will not be stored here. 177 // Note that these are stored indirect since we hand out pointers to these, 178 // and we don't want those to become invalid if some other thread appends 179 // things here. 180 std::unique_ptr<base::File> files[kSimpleEntryTotalFileCount]; 181 182 State state[kSimpleEntryTotalFileCount]; 183 std::list<TrackedFiles*>::iterator position_in_lru; 184 185 // true if position_in_lru is valid. For entries where we closed everything, 186 // we try not to keep them in the LRU so that we don't have to constantly 187 // rescan them. 188 bool in_lru = false; 189 }; 190 191 // Marks the file that was previously returned by Acquire as eligible for 192 // closing again. Called by ~FileHandle. 193 void Release(const SimpleSynchronousEntry* owner, SubFile subfile); 194 195 // Precondition: entry for given |owner| must already be in tracked_files_ 196 TrackedFiles* Find(const SimpleSynchronousEntry* owner); 197 198 // Handles state transition of closing file (when we are not deferring it), 199 // and moves the file out. Note that this may delete |*owners_files|. 200 std::unique_ptr<base::File> PrepareClose(TrackedFiles* owners_files, 201 int file_index); 202 203 // If too many files are open, picks some to close, and moves them to 204 // |*files_to_close|, updating other state as appropriate. 205 void CloseFilesIfTooManyOpen( 206 std::vector<std::unique_ptr<base::File>>* files_to_close); 207 208 // Tries to reopen given file, updating |*owners_files| if successful. 209 void ReopenFile(BackendFileOperations* file_operations, 210 TrackedFiles* owners_files, 211 SubFile subfile); 212 213 // Makes sure the entry is marked as most recently used, adding it to LRU 214 // if needed. 215 void EnsureInFrontOfLRU(TrackedFiles* owners_files); 216 217 base::Lock lock_; 218 std::unordered_map<uint64_t, std::vector<std::unique_ptr<TrackedFiles>>> 219 tracked_files_; 220 std::list<TrackedFiles*> lru_; 221 222 int file_limit_; 223 224 // How many actually open files we are using. 225 // Note that when a thread commits to closing a file, but hasn't actually 226 // executed the close yet, the file is no longer counted as open here, so this 227 // might be a little off. This should be OK as long as file_limit_ is set 228 // conservatively, considering SimpleCache's parallelism is bounded by a low 229 // number of threads, and getting it exact would require re-acquiring the 230 // lock after closing the file. 231 int open_files_ = 0; 232 }; 233 234 } // namespace disk_cache 235 236 #endif // NET_DISK_CACHE_SIMPLE_SIMPLE_FILE_TRACKER_H_ 237