175 lines
5.0 KiB
C++
175 lines
5.0 KiB
C++
/*
|
|
** 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 <future>
|
|
#include <map>
|
|
#include <queue>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
|
|
#include "FileBlobCache.h"
|
|
|
|
namespace android {
|
|
|
|
struct MultifileHeader {
|
|
uint32_t magic;
|
|
uint32_t crc;
|
|
EGLsizeiANDROID keySize;
|
|
EGLsizeiANDROID valueSize;
|
|
};
|
|
|
|
struct MultifileEntryStats {
|
|
EGLsizeiANDROID valueSize;
|
|
size_t fileSize;
|
|
time_t accessTime;
|
|
};
|
|
|
|
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,
|
|
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; }
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
void trimCache();
|
|
bool applyLRU(size_t cacheLimit);
|
|
|
|
bool mInitialized;
|
|
std::string mMultifileDirName;
|
|
|
|
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 mTotalCacheSize;
|
|
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
|