/* ** Copyright 2022, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #ifndef ANDROID_MULTIFILE_BLOB_CACHE_H #define ANDROID_MULTIFILE_BLOB_CACHE_H #include #include #include #include #include #include #include #include #include #include #include #include "FileBlobCache.h" namespace android { constexpr uint32_t kMultifileBlobCacheVersion = 1; constexpr char kMultifileBlobCacheStatusFile[] = "cache.status"; struct MultifileHeader { uint32_t magic; uint32_t crc; EGLsizeiANDROID keySize; EGLsizeiANDROID valueSize; }; struct MultifileEntryStats { EGLsizeiANDROID valueSize; size_t fileSize; time_t accessTime; }; struct MultifileStatus { uint32_t magic; uint32_t crc; uint32_t cacheVersion; char buildId[PROP_VALUE_MAX]; }; struct MultifileHotCache { int entryFd; uint8_t* entryBuffer; size_t entrySize; }; enum class TaskCommand { Invalid = 0, WriteToDisk, Exit, }; class DeferredTask { public: DeferredTask(TaskCommand command) : mCommand(command), mEntryHash(0), mBuffer(nullptr), mBufferSize(0) {} TaskCommand getTaskCommand() { return mCommand; } void initWriteToDisk(uint32_t entryHash, std::string fullPath, uint8_t* buffer, size_t bufferSize) { mCommand = TaskCommand::WriteToDisk; mEntryHash = entryHash; mFullPath = std::move(fullPath); mBuffer = buffer; mBufferSize = bufferSize; } uint32_t getEntryHash() { return mEntryHash; } std::string& getFullPath() { return mFullPath; } uint8_t* getBuffer() { return mBuffer; } size_t getBufferSize() { return mBufferSize; }; private: TaskCommand mCommand; // Parameters for WriteToDisk uint32_t mEntryHash; std::string mFullPath; uint8_t* mBuffer; size_t mBufferSize; }; class MultifileBlobCache { public: MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize, size_t maxTotalEntries, const std::string& baseDir); ~MultifileBlobCache(); void set(const void* key, EGLsizeiANDROID keySize, const void* value, EGLsizeiANDROID valueSize); EGLsizeiANDROID get(const void* key, EGLsizeiANDROID keySize, void* value, EGLsizeiANDROID valueSize); void finish(); size_t getTotalSize() const { return mTotalCacheSize; } size_t getTotalEntries() const { return mTotalCacheEntries; } const std::string& getCurrentBuildId() const { return mBuildId; } void setCurrentBuildId(const std::string& buildId) { mBuildId = buildId; } uint32_t getCurrentCacheVersion() const { return mCacheVersion; } void setCurrentCacheVersion(uint32_t cacheVersion) { mCacheVersion = cacheVersion; } private: void trackEntry(uint32_t entryHash, EGLsizeiANDROID valueSize, size_t fileSize, time_t accessTime); bool contains(uint32_t entryHash) const; bool removeEntry(uint32_t entryHash); MultifileEntryStats getEntryStats(uint32_t entryHash); bool createStatus(const std::string& baseDir); bool checkStatus(const std::string& baseDir); size_t getFileSize(uint32_t entryHash); size_t getValueSize(uint32_t entryHash); void increaseTotalCacheSize(size_t fileSize); void decreaseTotalCacheSize(size_t fileSize); bool addToHotCache(uint32_t entryHash, int fd, uint8_t* entryBufer, size_t entrySize); bool removeFromHotCache(uint32_t entryHash); bool clearCache(); void trimCache(); bool applyLRU(size_t cacheSizeLimit, size_t cacheEntryLimit); bool mInitialized; std::string mMultifileDirName; std::string mBuildId; uint32_t mCacheVersion; std::unordered_set mEntries; std::unordered_map mEntryStats; std::unordered_map mHotCache; size_t mMaxKeySize; size_t mMaxValueSize; size_t mMaxTotalSize; size_t mMaxTotalEntries; size_t mTotalCacheSize; size_t mTotalCacheEntries; size_t mHotCacheLimit; size_t mHotCacheEntryLimit; size_t mHotCacheSize; // Below are the components used for deferred writes // Track whether we have pending writes for an entry std::mutex mDeferredWriteStatusMutex; std::multimap mDeferredWrites GUARDED_BY(mDeferredWriteStatusMutex); // Functions to work through tasks in the queue void processTasks(); void processTasksImpl(bool* exitThread); void processTask(DeferredTask& task); // Used by main thread to create work for the worker thread void queueTask(DeferredTask&& task); // Used by main thread to wait for worker thread to complete all outstanding work. void waitForWorkComplete(); std::thread mTaskThread; std::queue mTasks; std::mutex mWorkerMutex; // This condition will block the worker thread until a task is queued std::condition_variable mWorkAvailableCondition; // This condition will block the main thread while the worker thread still has tasks std::condition_variable mWorkerIdleCondition; // This bool will track whether all tasks have been completed bool mWorkerThreadIdle; }; }; // namespace android #endif // ANDROID_MULTIFILE_BLOB_CACHE_H