• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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