| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef NET_DISK_CACHE_SIMPLE_SIMPLE_FILE_TRACKER_H_ |
| #define NET_DISK_CACHE_SIMPLE_SIMPLE_FILE_TRACKER_H_ |
| |
| #include <stdint.h> |
| #include <algorithm> |
| #include <memory> |
| #include <unordered_map> |
| #include <vector> |
| |
| #include "base/files/file.h" |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/synchronization/lock.h" |
| #include "net/base/net_export.h" |
| #include "net/disk_cache/simple/simple_entry_format.h" |
| |
| namespace disk_cache { |
| |
| class SimpleSynchronousEntry; |
| |
| // This keeps track of all the files SimpleCache has open, across all the |
| // backend instancess, in order to prevent us from running out of file |
| // descriptors. |
| // TODO(morlovich): Actually implement closing and re-opening of things if we |
| // run out. |
| // |
| // This class is thread-safe. |
| class NET_EXPORT_PRIVATE SimpleFileTracker { |
| public: |
| enum class SubFile { FILE_0, FILE_1, FILE_SPARSE }; |
| |
| // A RAII helper that guards access to a file grabbed for use from |
| // SimpleFileTracker::Acquire(). While it's still alive, if IsOK() is true, |
| // then using the underlying base::File via get() or the -> operator will be |
| // safe. |
| // |
| // This class is movable but not copyable. It should only be used from a |
| // single logical sequence of execution, and should not outlive the |
| // corresponding SimpleSynchronousEntry. |
| class NET_EXPORT_PRIVATE FileHandle { |
| public: |
| FileHandle(); |
| FileHandle(FileHandle&& other); |
| ~FileHandle(); |
| FileHandle& operator=(FileHandle&& other); |
| base::File* operator->() const; |
| base::File* get() const; |
| // Returns true if this handle points to a valid file. This should normally |
| // be the first thing called on the object, after getting it from |
| // SimpleFileTracker::Acquire. |
| bool IsOK() const; |
| |
| private: |
| friend class SimpleFileTracker; |
| FileHandle(SimpleFileTracker* file_tracker, |
| const SimpleSynchronousEntry* entry, |
| SimpleFileTracker::SubFile subfile, |
| base::File* file); |
| |
| // All the pointer fields are nullptr in the default/moved away from form. |
| SimpleFileTracker* file_tracker_; |
| const SimpleSynchronousEntry* entry_; |
| SimpleFileTracker::SubFile subfile_; |
| base::File* file_; |
| DISALLOW_COPY_AND_ASSIGN(FileHandle); |
| }; |
| |
| struct EntryFileKey { |
| EntryFileKey() : entry_hash(0), doom_generation(0) {} |
| explicit EntryFileKey(uint64_t hash) |
| : entry_hash(hash), doom_generation(0) {} |
| |
| uint64_t entry_hash; |
| |
| // In case of a hash collision, there may be multiple SimpleEntryImpl's |
| // around which have the same entry_hash but different key. In that case, |
| // we doom all but the most recent one and this number will eventually be |
| // used to name the files for the doomed ones. |
| // 0 here means the entry is the active one, and is the only value that's |
| // presently in use here. |
| uint32_t doom_generation; |
| }; |
| |
| SimpleFileTracker(); |
| ~SimpleFileTracker(); |
| |
| // Established |file| as what's backing |subfile| for |owner|. This is |
| // intended to be called when SimpleSynchronousEntry first sets up the file to |
| // transfer its ownership to SimpleFileTracker. Any Register() call must be |
| // eventually followed by a corresponding Close() call before the |owner| is |
| // destroyed. |
| void Register(const SimpleSynchronousEntry* owner, |
| SubFile subfile, |
| std::unique_ptr<base::File> file); |
| |
| // Lends out a file to SimpleSynchronousEntry for use. SimpleFileTracker |
| // will ensure that it doesn't close the file until the handle is destroyed. |
| // The caller should check .IsOK() on the returned value before using it, as |
| // it's possible that the file had to be closed and re-opened due to FD |
| // pressure, and that open may have failed. This should not be called twice |
| // with the exact same arguments until the handle returned from the previous |
| // such call is destroyed. |
| FileHandle Acquire(const SimpleSynchronousEntry* owner, SubFile subfile); |
| |
| // Tells SimpleFileTracker that SimpleSynchronousEntry will not be interested |
| // in the file further, so it can be closed and forgotten about. It's OK to |
| // call this while a handle to the file is alive, in which case the effect |
| // takes place after the handle is destroyed. |
| // If Close() has been called and the handle to the file is no longer alive, |
| // a new backing file can be established by calling Register() again. |
| void Close(const SimpleSynchronousEntry* owner, SubFile file); |
| |
| // Returns true if there is no in-memory state around, e.g. everything got |
| // cleaned up. This is a test-only method since this object is expected to be |
| // shared between multiple threads, in which case its return value may be |
| // outdated the moment it's returned. |
| bool IsEmptyForTesting(); |
| |
| private: |
| struct TrackedFiles { |
| // We can potentially run through this state machine multiple times for |
| // FILE_1, as that's often missing, so SimpleSynchronousEntry can sometimes |
| // close and remove the file for an empty stream, then re-open it on actual |
| // data. |
| enum State { |
| TF_NO_REGISTRATION = 0, |
| TF_REGISTERED = 1, |
| TF_ACQUIRED = 2, |
| TF_ACQUIRED_PENDING_CLOSE = 3, |
| }; |
| |
| TrackedFiles() { |
| std::fill(state, state + kSimpleEntryTotalFileCount, TF_NO_REGISTRATION); |
| } |
| |
| bool Empty() const; |
| |
| // We use pointers to SimpleSynchronousEntry two ways: |
| // 1) As opaque keys. This is handy as it avoids having to compare paths in |
| // case multiple backends use the same key. Since we access the |
| // bookkeeping under |lock_| |
| // |
| // 2) To get info on the caller of our operation. |
| // Accessing |owner| from any other TrackedFiles would be unsafe (as it |
| // may be doing its own thing in a different thread). |
| const SimpleSynchronousEntry* owner; |
| EntryFileKey key; |
| |
| // Some of these may be !IsValid(), if they are not open. |
| // Note that these are stored indirect since we hand out pointers to these, |
| // and we don't want those to become invalid if some other thread appends |
| // things here. |
| std::unique_ptr<base::File> files[kSimpleEntryTotalFileCount]; |
| |
| State state[kSimpleEntryTotalFileCount]; |
| }; |
| |
| // Marks the file that was previously returned by Acquire as eligible for |
| // closing again. Called by ~FileHandle. |
| void Release(const SimpleSynchronousEntry* owner, SubFile subfile); |
| |
| // |*found| will be set to whether the entry was found or not. |
| std::vector<TrackedFiles>::iterator Find(const SimpleSynchronousEntry* owner); |
| |
| // Handles state transition of closing file (when we are not deferring it), |
| // and moves the file out. Note that this may invalidate |owners_files|. |
| std::unique_ptr<base::File> PrepareClose( |
| std::vector<TrackedFiles>::iterator owners_files, |
| int file_index); |
| |
| base::Lock lock_; |
| std::unordered_map<uint64_t, std::vector<TrackedFiles>> tracked_files_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SimpleFileTracker); |
| }; |
| |
| } // namespace disk_cache |
| |
| #endif // NET_DISK_CACHE_SIMPLE_SIMPLE_FILE_TRACKER_H_ |