blob: 18566c2892cdbe5621a82dea401b788fef7fb17f [file] [log] [blame]
/*
** 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 <EGL/egl.h>
#include <EGL/eglext.h>
#include <android-base/thread_annotations.h>
#include <cutils/properties.h>
#include <future>
#include <map>
#include <queue>
#include <string>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#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<uint32_t> mEntries;
std::unordered_map<uint32_t, MultifileEntryStats> mEntryStats;
std::unordered_map<uint32_t, MultifileHotCache> 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<uint32_t, uint8_t*> 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<DeferredTask> 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