Overte C++ Documentation
FileCache.h
1 //
2 // FileCache.h
3 // libraries/networking/src
4 //
5 // Created by Zach Pomerantz on 2/21/2017.
6 // Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0.
7 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
8 //
9 
10 #ifndef hifi_FileCache_h
11 #define hifi_FileCache_h
12 
13 #include <atomic>
14 #include <memory>
15 #include <cstddef>
16 #include <map>
17 #include <unordered_set>
18 #include <mutex>
19 #include <string>
20 #include <unordered_map>
21 
22 #include <QObject>
23 #include <QLoggingCategory>
24 
25 Q_DECLARE_LOGGING_CATEGORY(file_cache)
26 
27 class FileCacheTests;
28 
29 namespace cache {
30 
31 class File;
32 using FilePointer = std::shared_ptr<File>;
33 class FileCache;
34 using FileCachePointer = std::shared_ptr<FileCache>;
35 using FileCacheWeakPointer = std::weak_ptr<FileCache>;
36 
37 class FileCache : public QObject, public std::enable_shared_from_this<FileCache> {
38  Q_OBJECT
39  Q_PROPERTY(size_t numTotal READ getNumTotalFiles NOTIFY dirty)
40  Q_PROPERTY(size_t numCached READ getNumCachedFiles NOTIFY dirty)
41  Q_PROPERTY(size_t sizeTotal READ getSizeTotalFiles NOTIFY dirty)
42  Q_PROPERTY(size_t sizeCached READ getSizeCachedFiles NOTIFY dirty)
43 
44  static const size_t DEFAULT_MAX_SIZE;
45  static const size_t MAX_MAX_SIZE;
46  static const size_t DEFAULT_MIN_FREE_STORAGE_SPACE;
47 
48  friend class ::FileCacheTests;
49 
50 public:
51  // You can initialize the FileCache with a directory name (ex.: "temp_jpgs") that will be relative to the application local data, OR with a full path
52  // The file cache will ignore any file that doesn't match the extension provided
53  FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr);
54  virtual ~FileCache();
55 
56  // Remove all unlocked items from the cache
57  void wipe();
58 
59  size_t getNumTotalFiles() const { return _numTotalFiles; }
60  size_t getNumCachedFiles() const { return _numUnusedFiles; }
61  size_t getSizeTotalFiles() const { return _totalFilesSize; }
62  size_t getSizeCachedFiles() const { return _unusedFilesSize; }
63 
64  // Set the maximum amount of disk space to use on disk
65  void setMaxSize(size_t maxCacheSize);
66 
67  // Set the minumum amount of free disk space to retain. This supercedes the max size,
68  // so if the cache is consuming all but 500 MB of the drive, unused entries will be ejected
69  // to free up more space, regardless of the cache max size
70  void setMinFreeSize(size_t size);
71 
72  using Key = std::string;
73  struct Metadata {
74  Metadata(const Key& key, size_t length) :
75  key(key), length(length) {}
76  Key key;
77  size_t length;
78  };
79 
80  // derived classes should implement a setter/getter, for example, for a FileCache backing a network cache:
81  //
82  // DerivedFilePointer writeFile(const char* data, DerivedMetadata&& metadata) {
83  // return writeFile(data, std::forward(metadata));
84  // }
85  //
86  // DerivedFilePointer getFile(const QUrl& url) {
87  // auto key = lookup_hash_for(url); // assuming hashing url in create/evictedFile overrides
88  // return getFile(key);
89  // }
90 
91 signals:
92  void dirty();
93 
94 public:
96  virtual void initialize();
97 
98  // Add file to the cache and return the cache entry.
99  FilePointer writeFile(const char* data, Metadata&& metadata, bool overwrite = false);
100  FilePointer getFile(const Key& key);
101 
103  virtual std::unique_ptr<File> createFile(Metadata&& metadata, const std::string& filepath);
104 
105 private:
106  using Mutex = std::recursive_mutex;
107  using Lock = std::unique_lock<Mutex>;
108  using Map = std::unordered_map<Key, std::weak_ptr<File>>;
109  using Set = std::unordered_set<FilePointer>;
110  using KeySet = std::unordered_set<Key>;
111 
112  friend class File;
113 
114  std::string getFilepath(const Key& key);
115 
116  FilePointer addFile(Metadata&& metadata, const std::string& filepath);
117  void addUnusedFile(const FilePointer& file);
118  void releaseFile(File* file);
119  void clean();
120  void clear();
121  // Remove a file from the cache
122  void eject(FilePointer file);
123 
124  size_t getOverbudgetAmount() const;
125 
126  // FIXME it might be desirable to have the min free space variable be static so it can be
127  // shared among multiple instances of FileCache
128  std::atomic<size_t> _minFreeSpaceSize { DEFAULT_MIN_FREE_STORAGE_SPACE };
129  std::atomic<size_t> _maxSize { DEFAULT_MAX_SIZE };
130  std::atomic<size_t> _numTotalFiles { 0 };
131  std::atomic<size_t> _numUnusedFiles { 0 };
132  std::atomic<size_t> _totalFilesSize { 0 };
133  std::atomic<size_t> _unusedFilesSize { 0 };
134 
135  const std::string _ext;
136  const std::string _dirname;
137  const std::string _dirpath;
138  bool _initialized { false };
139 
140  Mutex _mutex;
141  Map _files;
142  Set _unusedFiles;
143 };
144 
145 class File {
146 public:
147  using Key = FileCache::Key;
148  using Metadata = FileCache::Metadata;
149 
150  const Key& getKey() const { return _key; }
151  const size_t& getLength() const { return _length; }
152  const std::string& getFilepath() const { return _filepath; }
153 
154  virtual ~File();
156  static void deleter(File* file);
157 
158 protected:
160  File(Metadata&& metadata, const std::string& filepath);
161 
162 private:
163  friend class FileCache;
164  friend struct FilePointerComparator;
165  friend class ::FileCacheTests;
166 
167  const Key _key;
168  const size_t _length;
169  const std::string _filepath;
170 
171  void touch();
172  FileCacheWeakPointer _parent;
173  int64_t _modified { 0 };
174  bool _locked { false };
175 
176  bool _shouldPersist { false };
177 };
178 
179 }
180 
181 #endif // hifi_FileCache_h