1 /* 2 ** Copyright 2022, The Android Open Source Project 3 ** 4 ** Licensed under the Apache License, Version 2.0 (the "License"); 5 ** you may not use this file except in compliance with the License. 6 ** You may obtain a copy of the License at 7 ** 8 ** http://www.apache.org/licenses/LICENSE-2.0 9 ** 10 ** Unless required by applicable law or agreed to in writing, software 11 ** distributed under the License is distributed on an "AS IS" BASIS, 12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 ** See the License for the specific language governing permissions and 14 ** limitations under the License. 15 */ 16 17 #ifndef ANDROID_MULTIFILE_BLOB_CACHE_H 18 #define ANDROID_MULTIFILE_BLOB_CACHE_H 19 20 #include <EGL/egl.h> 21 #include <EGL/eglext.h> 22 23 #include <android-base/thread_annotations.h> 24 #include <cutils/properties.h> 25 #include <future> 26 #include <map> 27 #include <queue> 28 #include <string> 29 #include <thread> 30 #include <unordered_map> 31 #include <unordered_set> 32 33 #include "FileBlobCache.h" 34 35 #include <com_android_graphics_egl_flags.h> 36 37 using namespace com::android::graphics::egl; 38 39 namespace android { 40 41 constexpr uint32_t kMultifileBlobCacheVersion = 2; 42 constexpr char kMultifileBlobCacheStatusFile[] = "cache.status"; 43 44 struct MultifileHeader { 45 uint32_t magic; 46 uint32_t crc; 47 EGLsizeiANDROID keySize; 48 EGLsizeiANDROID valueSize; 49 }; 50 51 struct MultifileEntryStats { 52 uint32_t entryHash; 53 EGLsizeiANDROID valueSize; 54 size_t fileSize; 55 }; 56 57 struct MultifileStatus { 58 uint32_t magic; 59 uint32_t crc; 60 uint32_t cacheVersion; 61 char buildId[PROP_VALUE_MAX]; 62 }; 63 64 struct MultifileHotCache { 65 int entryFd; 66 uint8_t* entryBuffer; 67 size_t entrySize; 68 }; 69 70 enum class TaskCommand { 71 Invalid = 0, 72 WriteToDisk, 73 Exit, 74 }; 75 76 class DeferredTask { 77 public: DeferredTask(TaskCommand command)78 DeferredTask(TaskCommand command) 79 : mCommand(command), mEntryHash(0), mBuffer(nullptr), mBufferSize(0) {} 80 getTaskCommand()81 TaskCommand getTaskCommand() { return mCommand; } 82 initWriteToDisk(uint32_t entryHash,std::string fullPath,uint8_t * buffer,size_t bufferSize)83 void initWriteToDisk(uint32_t entryHash, std::string fullPath, uint8_t* buffer, 84 size_t bufferSize) { 85 mCommand = TaskCommand::WriteToDisk; 86 mEntryHash = entryHash; 87 mFullPath = std::move(fullPath); 88 mBuffer = buffer; 89 mBufferSize = bufferSize; 90 } 91 getEntryHash()92 uint32_t getEntryHash() { return mEntryHash; } getFullPath()93 std::string& getFullPath() { return mFullPath; } getBuffer()94 uint8_t* getBuffer() { return mBuffer; } getBufferSize()95 size_t getBufferSize() { return mBufferSize; }; 96 97 private: 98 TaskCommand mCommand; 99 100 // Parameters for WriteToDisk 101 uint32_t mEntryHash; 102 std::string mFullPath; 103 uint8_t* mBuffer; 104 size_t mBufferSize; 105 }; 106 107 #if COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE) 108 struct MultifileTimeLess { operatorMultifileTimeLess109 bool operator()(const struct timespec& t1, const struct timespec& t2) const { 110 if (t1.tv_sec == t2.tv_sec) { 111 // If seconds are equal, check nanoseconds 112 return t1.tv_nsec < t2.tv_nsec; 113 } else { 114 // Otherwise, compare seconds 115 return t1.tv_sec < t2.tv_sec; 116 } 117 } 118 }; 119 120 // The third parameter here causes all entries to be sorted by access time, 121 // so oldest will be accessed first in applyLRU 122 using MultifileEntryStatsMap = 123 std::multimap<struct timespec, MultifileEntryStats, MultifileTimeLess>; 124 using MultifileEntryStatsMapIter = MultifileEntryStatsMap::iterator; 125 #endif // COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE) 126 127 class MultifileBlobCache { 128 public: 129 MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize, 130 size_t maxTotalEntries, const std::string& baseDir); 131 ~MultifileBlobCache(); 132 133 void set(const void* key, EGLsizeiANDROID keySize, const void* value, 134 EGLsizeiANDROID valueSize); 135 EGLsizeiANDROID get(const void* key, EGLsizeiANDROID keySize, void* value, 136 EGLsizeiANDROID valueSize); 137 138 void finish(); 139 getTotalSize()140 size_t getTotalSize() const { return mTotalCacheSize; } getTotalEntries()141 size_t getTotalEntries() const { return mTotalCacheEntries; } getTotalCacheSizeDivisor()142 size_t getTotalCacheSizeDivisor() const { return mTotalCacheSizeDivisor; } 143 getCurrentBuildId()144 const std::string& getCurrentBuildId() const { return mBuildId; } setCurrentBuildId(const std::string & buildId)145 void setCurrentBuildId(const std::string& buildId) { mBuildId = buildId; } 146 getCurrentCacheVersion()147 uint32_t getCurrentCacheVersion() const { return mCacheVersion; } setCurrentCacheVersion(uint32_t cacheVersion)148 void setCurrentCacheVersion(uint32_t cacheVersion) { mCacheVersion = cacheVersion; } 149 150 private: 151 void trackEntry(uint32_t entryHash, EGLsizeiANDROID valueSize, size_t fileSize, 152 const timespec& accessTime); 153 bool contains(uint32_t entryHash) const; 154 bool removeEntry(uint32_t entryHash); 155 MultifileEntryStats getEntryStats(uint32_t entryHash); 156 void updateEntryTime(uint32_t entryHash, const timespec& newTime); 157 158 bool createStatus(const std::string& baseDir); 159 bool checkStatus(const std::string& baseDir); 160 161 size_t getFileSize(uint32_t entryHash); 162 size_t getValueSize(uint32_t entryHash); 163 164 void increaseTotalCacheSize(size_t fileSize); 165 void decreaseTotalCacheSize(size_t fileSize); 166 167 bool addToHotCache(uint32_t entryHash, int fd, uint8_t* entryBufer, size_t entrySize); 168 bool removeFromHotCache(uint32_t entryHash); 169 170 bool clearCache(); 171 void trimCache(); 172 bool applyLRU(size_t cacheSizeLimit, size_t cacheEntryLimit); 173 174 bool mInitialized; 175 std::string mMultifileDirName; 176 177 std::string mBuildId; 178 uint32_t mCacheVersion; 179 180 #if COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE) 181 std::unordered_map<uint32_t, MultifileEntryStatsMapIter> mEntries; 182 MultifileEntryStatsMap mEntryStats; 183 #else 184 std::unordered_set<uint32_t> mEntries; 185 std::unordered_map<uint32_t, MultifileEntryStats> mEntryStats; 186 #endif // COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE) 187 188 std::unordered_map<uint32_t, MultifileHotCache> mHotCache; 189 190 size_t mMaxKeySize; 191 size_t mMaxValueSize; 192 size_t mMaxTotalSize; 193 size_t mMaxTotalEntries; 194 size_t mTotalCacheSize; 195 size_t mTotalCacheEntries; 196 size_t mTotalCacheSizeDivisor; 197 size_t mHotCacheLimit; 198 size_t mHotCacheEntryLimit; 199 size_t mHotCacheSize; 200 201 // Below are the components used for deferred writes 202 203 // Track whether we have pending writes for an entry 204 std::mutex mDeferredWriteStatusMutex; 205 std::multimap<uint32_t, uint8_t*> mDeferredWrites GUARDED_BY(mDeferredWriteStatusMutex); 206 207 // Functions to work through tasks in the queue 208 void processTasks(); 209 void processTasksImpl(bool* exitThread); 210 void processTask(DeferredTask& task); 211 212 // Used by main thread to create work for the worker thread 213 void queueTask(DeferredTask&& task); 214 215 // Used by main thread to wait for worker thread to complete all outstanding work. 216 void waitForWorkComplete(); 217 218 std::thread mTaskThread; 219 std::queue<DeferredTask> mTasks; 220 std::mutex mWorkerMutex; 221 222 // This condition will block the worker thread until a task is queued 223 std::condition_variable mWorkAvailableCondition; 224 225 // This condition will block the main thread while the worker thread still has tasks 226 std::condition_variable mWorkerIdleCondition; 227 228 // This bool will track whether all tasks have been completed 229 bool mWorkerThreadIdle; 230 }; 231 232 }; // namespace android 233 234 #endif // ANDROID_MULTIFILE_BLOB_CACHE_H 235