1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker ** Copyright 2022, The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker **
4*38e8c45fSAndroid Build Coastguard Worker ** Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker ** you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker ** You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker **
8*38e8c45fSAndroid Build Coastguard Worker ** http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker **
10*38e8c45fSAndroid Build Coastguard Worker ** Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker ** distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker ** See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker ** limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker */
16*38e8c45fSAndroid Build Coastguard Worker
17*38e8c45fSAndroid Build Coastguard Worker // #define LOG_NDEBUG 0
18*38e8c45fSAndroid Build Coastguard Worker
19*38e8c45fSAndroid Build Coastguard Worker #include "MultifileBlobCache.h"
20*38e8c45fSAndroid Build Coastguard Worker
21*38e8c45fSAndroid Build Coastguard Worker #include <android-base/properties.h>
22*38e8c45fSAndroid Build Coastguard Worker #include <dirent.h>
23*38e8c45fSAndroid Build Coastguard Worker #include <fcntl.h>
24*38e8c45fSAndroid Build Coastguard Worker #include <inttypes.h>
25*38e8c45fSAndroid Build Coastguard Worker #include <log/log.h>
26*38e8c45fSAndroid Build Coastguard Worker #include <stdio.h>
27*38e8c45fSAndroid Build Coastguard Worker #include <stdlib.h>
28*38e8c45fSAndroid Build Coastguard Worker #include <sys/mman.h>
29*38e8c45fSAndroid Build Coastguard Worker #include <sys/stat.h>
30*38e8c45fSAndroid Build Coastguard Worker #include <time.h>
31*38e8c45fSAndroid Build Coastguard Worker #include <unistd.h>
32*38e8c45fSAndroid Build Coastguard Worker #include <utime.h>
33*38e8c45fSAndroid Build Coastguard Worker
34*38e8c45fSAndroid Build Coastguard Worker #include <algorithm>
35*38e8c45fSAndroid Build Coastguard Worker #include <chrono>
36*38e8c45fSAndroid Build Coastguard Worker #include <limits>
37*38e8c45fSAndroid Build Coastguard Worker #include <locale>
38*38e8c45fSAndroid Build Coastguard Worker
39*38e8c45fSAndroid Build Coastguard Worker #include <utils/JenkinsHash.h>
40*38e8c45fSAndroid Build Coastguard Worker
41*38e8c45fSAndroid Build Coastguard Worker #include <com_android_graphics_egl_flags.h>
42*38e8c45fSAndroid Build Coastguard Worker
43*38e8c45fSAndroid Build Coastguard Worker using namespace com::android::graphics::egl;
44*38e8c45fSAndroid Build Coastguard Worker
45*38e8c45fSAndroid Build Coastguard Worker using namespace std::literals;
46*38e8c45fSAndroid Build Coastguard Worker
47*38e8c45fSAndroid Build Coastguard Worker constexpr uint32_t kMultifileMagic = 'MFB$';
48*38e8c45fSAndroid Build Coastguard Worker constexpr uint32_t kCrcPlaceholder = 0;
49*38e8c45fSAndroid Build Coastguard Worker
50*38e8c45fSAndroid Build Coastguard Worker // When removing files, what fraction of the overall limit should be reached when removing files
51*38e8c45fSAndroid Build Coastguard Worker // A divisor of two will decrease the cache to 50%, four to 25% and so on
52*38e8c45fSAndroid Build Coastguard Worker // We use the same limit to manage size and entry count
53*38e8c45fSAndroid Build Coastguard Worker constexpr uint32_t kCacheLimitDivisor = 2;
54*38e8c45fSAndroid Build Coastguard Worker
55*38e8c45fSAndroid Build Coastguard Worker namespace {
56*38e8c45fSAndroid Build Coastguard Worker
57*38e8c45fSAndroid Build Coastguard Worker // Helper function to close entries or free them
freeHotCacheEntry(android::MultifileHotCache & entry)58*38e8c45fSAndroid Build Coastguard Worker void freeHotCacheEntry(android::MultifileHotCache& entry) {
59*38e8c45fSAndroid Build Coastguard Worker if (entry.entryFd != -1) {
60*38e8c45fSAndroid Build Coastguard Worker // If we have an fd, then this entry was added to hot cache via INIT or GET
61*38e8c45fSAndroid Build Coastguard Worker // We need to unmap the entry
62*38e8c45fSAndroid Build Coastguard Worker munmap(entry.entryBuffer, entry.entrySize);
63*38e8c45fSAndroid Build Coastguard Worker } else {
64*38e8c45fSAndroid Build Coastguard Worker // Otherwise, this was added to hot cache during SET, so it was never mapped
65*38e8c45fSAndroid Build Coastguard Worker // and fd was only on the deferred thread.
66*38e8c45fSAndroid Build Coastguard Worker delete[] entry.entryBuffer;
67*38e8c45fSAndroid Build Coastguard Worker }
68*38e8c45fSAndroid Build Coastguard Worker }
69*38e8c45fSAndroid Build Coastguard Worker
70*38e8c45fSAndroid Build Coastguard Worker } // namespace
71*38e8c45fSAndroid Build Coastguard Worker
72*38e8c45fSAndroid Build Coastguard Worker namespace android {
73*38e8c45fSAndroid Build Coastguard Worker
MultifileBlobCache(size_t maxKeySize,size_t maxValueSize,size_t maxTotalSize,size_t maxTotalEntries,const std::string & baseDir)74*38e8c45fSAndroid Build Coastguard Worker MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize,
75*38e8c45fSAndroid Build Coastguard Worker size_t maxTotalEntries, const std::string& baseDir)
76*38e8c45fSAndroid Build Coastguard Worker : mInitialized(false),
77*38e8c45fSAndroid Build Coastguard Worker mCacheVersion(0),
78*38e8c45fSAndroid Build Coastguard Worker mMaxKeySize(maxKeySize),
79*38e8c45fSAndroid Build Coastguard Worker mMaxValueSize(maxValueSize),
80*38e8c45fSAndroid Build Coastguard Worker mMaxTotalSize(maxTotalSize),
81*38e8c45fSAndroid Build Coastguard Worker mMaxTotalEntries(maxTotalEntries),
82*38e8c45fSAndroid Build Coastguard Worker mTotalCacheSize(0),
83*38e8c45fSAndroid Build Coastguard Worker mTotalCacheEntries(0),
84*38e8c45fSAndroid Build Coastguard Worker mTotalCacheSizeDivisor(kCacheLimitDivisor),
85*38e8c45fSAndroid Build Coastguard Worker mHotCacheLimit(0),
86*38e8c45fSAndroid Build Coastguard Worker mHotCacheSize(0),
87*38e8c45fSAndroid Build Coastguard Worker mWorkerThreadIdle(true) {
88*38e8c45fSAndroid Build Coastguard Worker if (baseDir.empty()) {
89*38e8c45fSAndroid Build Coastguard Worker ALOGV("INIT: no baseDir provided in MultifileBlobCache constructor, returning early.");
90*38e8c45fSAndroid Build Coastguard Worker return;
91*38e8c45fSAndroid Build Coastguard Worker }
92*38e8c45fSAndroid Build Coastguard Worker
93*38e8c45fSAndroid Build Coastguard Worker // Set the cache version
94*38e8c45fSAndroid Build Coastguard Worker mCacheVersion = kMultifileBlobCacheVersion;
95*38e8c45fSAndroid Build Coastguard Worker // Bump the version if we're using flagged features
96*38e8c45fSAndroid Build Coastguard Worker if (flags::multifile_blobcache_advanced_usage()) {
97*38e8c45fSAndroid Build Coastguard Worker mCacheVersion++;
98*38e8c45fSAndroid Build Coastguard Worker }
99*38e8c45fSAndroid Build Coastguard Worker // Override if debug value set
100*38e8c45fSAndroid Build Coastguard Worker int debugCacheVersion = base::GetIntProperty("debug.egl.blobcache.cache_version", -1);
101*38e8c45fSAndroid Build Coastguard Worker if (debugCacheVersion >= 0) {
102*38e8c45fSAndroid Build Coastguard Worker ALOGV("INIT: Using %u as cacheVersion instead of %u", debugCacheVersion, mCacheVersion);
103*38e8c45fSAndroid Build Coastguard Worker mCacheVersion = debugCacheVersion;
104*38e8c45fSAndroid Build Coastguard Worker }
105*38e8c45fSAndroid Build Coastguard Worker
106*38e8c45fSAndroid Build Coastguard Worker // Set the platform build ID, override if debug value set
107*38e8c45fSAndroid Build Coastguard Worker mBuildId = base::GetProperty("ro.build.id", "");
108*38e8c45fSAndroid Build Coastguard Worker std::string debugBuildId = base::GetProperty("debug.egl.blobcache.build_id", "");
109*38e8c45fSAndroid Build Coastguard Worker if (!debugBuildId.empty()) {
110*38e8c45fSAndroid Build Coastguard Worker ALOGV("INIT: Using %s as buildId instead of %s", debugBuildId.c_str(), mBuildId.c_str());
111*38e8c45fSAndroid Build Coastguard Worker if (debugBuildId.length() > PROP_VALUE_MAX) {
112*38e8c45fSAndroid Build Coastguard Worker ALOGV("INIT: debugBuildId is too long (%zu), reduce it to %u", debugBuildId.length(),
113*38e8c45fSAndroid Build Coastguard Worker PROP_VALUE_MAX);
114*38e8c45fSAndroid Build Coastguard Worker }
115*38e8c45fSAndroid Build Coastguard Worker mBuildId = debugBuildId;
116*38e8c45fSAndroid Build Coastguard Worker }
117*38e8c45fSAndroid Build Coastguard Worker
118*38e8c45fSAndroid Build Coastguard Worker // Establish the name of our multifile directory
119*38e8c45fSAndroid Build Coastguard Worker mMultifileDirName = baseDir + ".multifile";
120*38e8c45fSAndroid Build Coastguard Worker
121*38e8c45fSAndroid Build Coastguard Worker // Set the hotcache limit to be large enough to contain one max entry
122*38e8c45fSAndroid Build Coastguard Worker // This ensure the hot cache is always large enough for single entry
123*38e8c45fSAndroid Build Coastguard Worker mHotCacheLimit = mMaxKeySize + mMaxValueSize + sizeof(MultifileHeader);
124*38e8c45fSAndroid Build Coastguard Worker
125*38e8c45fSAndroid Build Coastguard Worker ALOGV("INIT: Initializing multifile blobcache with maxKeySize=%zu and maxValueSize=%zu",
126*38e8c45fSAndroid Build Coastguard Worker mMaxKeySize, mMaxValueSize);
127*38e8c45fSAndroid Build Coastguard Worker
128*38e8c45fSAndroid Build Coastguard Worker // Initialize our cache with the contents of the directory
129*38e8c45fSAndroid Build Coastguard Worker mTotalCacheSize = 0;
130*38e8c45fSAndroid Build Coastguard Worker
131*38e8c45fSAndroid Build Coastguard Worker // Create the worker thread
132*38e8c45fSAndroid Build Coastguard Worker mTaskThread = std::thread(&MultifileBlobCache::processTasks, this);
133*38e8c45fSAndroid Build Coastguard Worker
134*38e8c45fSAndroid Build Coastguard Worker // See if the dir exists, and initialize using its contents
135*38e8c45fSAndroid Build Coastguard Worker bool statusGood = false;
136*38e8c45fSAndroid Build Coastguard Worker
137*38e8c45fSAndroid Build Coastguard Worker // Check that our cacheVersion and buildId match
138*38e8c45fSAndroid Build Coastguard Worker struct stat st;
139*38e8c45fSAndroid Build Coastguard Worker if (stat(mMultifileDirName.c_str(), &st) == 0) {
140*38e8c45fSAndroid Build Coastguard Worker if (checkStatus(mMultifileDirName)) {
141*38e8c45fSAndroid Build Coastguard Worker statusGood = true;
142*38e8c45fSAndroid Build Coastguard Worker } else {
143*38e8c45fSAndroid Build Coastguard Worker ALOGV("INIT: Cache status has changed, clearing the cache");
144*38e8c45fSAndroid Build Coastguard Worker if (!clearCache()) {
145*38e8c45fSAndroid Build Coastguard Worker ALOGE("INIT: Unable to clear cache");
146*38e8c45fSAndroid Build Coastguard Worker return;
147*38e8c45fSAndroid Build Coastguard Worker }
148*38e8c45fSAndroid Build Coastguard Worker }
149*38e8c45fSAndroid Build Coastguard Worker }
150*38e8c45fSAndroid Build Coastguard Worker
151*38e8c45fSAndroid Build Coastguard Worker if (statusGood) {
152*38e8c45fSAndroid Build Coastguard Worker // Read all the files and gather details, then preload their contents
153*38e8c45fSAndroid Build Coastguard Worker DIR* dir;
154*38e8c45fSAndroid Build Coastguard Worker struct dirent* entry;
155*38e8c45fSAndroid Build Coastguard Worker if ((dir = opendir(mMultifileDirName.c_str())) != nullptr) {
156*38e8c45fSAndroid Build Coastguard Worker while ((entry = readdir(dir)) != nullptr) {
157*38e8c45fSAndroid Build Coastguard Worker if (entry->d_name == "."s || entry->d_name == ".."s ||
158*38e8c45fSAndroid Build Coastguard Worker strcmp(entry->d_name, kMultifileBlobCacheStatusFile) == 0) {
159*38e8c45fSAndroid Build Coastguard Worker continue;
160*38e8c45fSAndroid Build Coastguard Worker }
161*38e8c45fSAndroid Build Coastguard Worker
162*38e8c45fSAndroid Build Coastguard Worker std::string entryName = entry->d_name;
163*38e8c45fSAndroid Build Coastguard Worker std::string fullPath = mMultifileDirName + "/" + entryName;
164*38e8c45fSAndroid Build Coastguard Worker
165*38e8c45fSAndroid Build Coastguard Worker // The filename is the same as the entryHash
166*38e8c45fSAndroid Build Coastguard Worker uint32_t entryHash = static_cast<uint32_t>(strtoul(entry->d_name, nullptr, 10));
167*38e8c45fSAndroid Build Coastguard Worker
168*38e8c45fSAndroid Build Coastguard Worker ALOGV("INIT: Checking entry %u", entryHash);
169*38e8c45fSAndroid Build Coastguard Worker
170*38e8c45fSAndroid Build Coastguard Worker // Look up the details of the file
171*38e8c45fSAndroid Build Coastguard Worker struct stat st;
172*38e8c45fSAndroid Build Coastguard Worker if (stat(fullPath.c_str(), &st) != 0) {
173*38e8c45fSAndroid Build Coastguard Worker ALOGE("Failed to stat %s", fullPath.c_str());
174*38e8c45fSAndroid Build Coastguard Worker return;
175*38e8c45fSAndroid Build Coastguard Worker }
176*38e8c45fSAndroid Build Coastguard Worker
177*38e8c45fSAndroid Build Coastguard Worker // If the cache entry is damaged or no good, remove it
178*38e8c45fSAndroid Build Coastguard Worker if (st.st_size <= 0 || st.st_atime <= 0) {
179*38e8c45fSAndroid Build Coastguard Worker ALOGE("INIT: Entry %u has invalid stats! Removing.", entryHash);
180*38e8c45fSAndroid Build Coastguard Worker if (remove(fullPath.c_str()) != 0) {
181*38e8c45fSAndroid Build Coastguard Worker ALOGE("INIT: Error removing %s: %s", fullPath.c_str(),
182*38e8c45fSAndroid Build Coastguard Worker std::strerror(errno));
183*38e8c45fSAndroid Build Coastguard Worker }
184*38e8c45fSAndroid Build Coastguard Worker continue;
185*38e8c45fSAndroid Build Coastguard Worker }
186*38e8c45fSAndroid Build Coastguard Worker
187*38e8c45fSAndroid Build Coastguard Worker // Open the file so we can read its header
188*38e8c45fSAndroid Build Coastguard Worker int fd = open(fullPath.c_str(), O_RDONLY);
189*38e8c45fSAndroid Build Coastguard Worker if (fd == -1) {
190*38e8c45fSAndroid Build Coastguard Worker ALOGE("Cache error - failed to open fullPath: %s, error: %s", fullPath.c_str(),
191*38e8c45fSAndroid Build Coastguard Worker std::strerror(errno));
192*38e8c45fSAndroid Build Coastguard Worker return;
193*38e8c45fSAndroid Build Coastguard Worker }
194*38e8c45fSAndroid Build Coastguard Worker
195*38e8c45fSAndroid Build Coastguard Worker // Read the beginning of the file to get header
196*38e8c45fSAndroid Build Coastguard Worker MultifileHeader header;
197*38e8c45fSAndroid Build Coastguard Worker size_t result = read(fd, static_cast<void*>(&header), sizeof(MultifileHeader));
198*38e8c45fSAndroid Build Coastguard Worker if (result != sizeof(MultifileHeader)) {
199*38e8c45fSAndroid Build Coastguard Worker ALOGE("INIT: Error reading MultifileHeader from cache entry (%s): %s",
200*38e8c45fSAndroid Build Coastguard Worker fullPath.c_str(), std::strerror(errno));
201*38e8c45fSAndroid Build Coastguard Worker close(fd);
202*38e8c45fSAndroid Build Coastguard Worker return;
203*38e8c45fSAndroid Build Coastguard Worker }
204*38e8c45fSAndroid Build Coastguard Worker
205*38e8c45fSAndroid Build Coastguard Worker // Verify header magic
206*38e8c45fSAndroid Build Coastguard Worker if (header.magic != kMultifileMagic) {
207*38e8c45fSAndroid Build Coastguard Worker ALOGE("INIT: Entry %u has bad magic (%u)! Removing.", entryHash, header.magic);
208*38e8c45fSAndroid Build Coastguard Worker if (remove(fullPath.c_str()) != 0) {
209*38e8c45fSAndroid Build Coastguard Worker ALOGE("INIT: Error removing %s: %s", fullPath.c_str(),
210*38e8c45fSAndroid Build Coastguard Worker std::strerror(errno));
211*38e8c45fSAndroid Build Coastguard Worker }
212*38e8c45fSAndroid Build Coastguard Worker close(fd);
213*38e8c45fSAndroid Build Coastguard Worker continue;
214*38e8c45fSAndroid Build Coastguard Worker }
215*38e8c45fSAndroid Build Coastguard Worker
216*38e8c45fSAndroid Build Coastguard Worker // Note: Converting from off_t (signed) to size_t (unsigned)
217*38e8c45fSAndroid Build Coastguard Worker size_t fileSize = static_cast<size_t>(st.st_size);
218*38e8c45fSAndroid Build Coastguard Worker
219*38e8c45fSAndroid Build Coastguard Worker // Memory map the file
220*38e8c45fSAndroid Build Coastguard Worker uint8_t* mappedEntry = reinterpret_cast<uint8_t*>(
221*38e8c45fSAndroid Build Coastguard Worker mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0));
222*38e8c45fSAndroid Build Coastguard Worker
223*38e8c45fSAndroid Build Coastguard Worker // We can close the file now and the mmap will remain
224*38e8c45fSAndroid Build Coastguard Worker close(fd);
225*38e8c45fSAndroid Build Coastguard Worker
226*38e8c45fSAndroid Build Coastguard Worker if (mappedEntry == MAP_FAILED) {
227*38e8c45fSAndroid Build Coastguard Worker ALOGE("Failed to mmap cacheEntry, error: %s", std::strerror(errno));
228*38e8c45fSAndroid Build Coastguard Worker return;
229*38e8c45fSAndroid Build Coastguard Worker }
230*38e8c45fSAndroid Build Coastguard Worker
231*38e8c45fSAndroid Build Coastguard Worker // Ensure we have a good CRC
232*38e8c45fSAndroid Build Coastguard Worker if (header.crc != GenerateCRC32(mappedEntry + sizeof(MultifileHeader),
233*38e8c45fSAndroid Build Coastguard Worker fileSize - sizeof(MultifileHeader))) {
234*38e8c45fSAndroid Build Coastguard Worker ALOGV("INIT: Entry %u failed CRC check! Removing.", entryHash);
235*38e8c45fSAndroid Build Coastguard Worker if (remove(fullPath.c_str()) != 0) {
236*38e8c45fSAndroid Build Coastguard Worker ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
237*38e8c45fSAndroid Build Coastguard Worker }
238*38e8c45fSAndroid Build Coastguard Worker continue;
239*38e8c45fSAndroid Build Coastguard Worker }
240*38e8c45fSAndroid Build Coastguard Worker
241*38e8c45fSAndroid Build Coastguard Worker // If the cache entry is damaged or no good, remove it
242*38e8c45fSAndroid Build Coastguard Worker if (header.keySize <= 0 || header.valueSize <= 0) {
243*38e8c45fSAndroid Build Coastguard Worker ALOGV("INIT: Entry %u has a bad header keySize (%lu) or valueSize (%lu), "
244*38e8c45fSAndroid Build Coastguard Worker "removing.",
245*38e8c45fSAndroid Build Coastguard Worker entryHash, header.keySize, header.valueSize);
246*38e8c45fSAndroid Build Coastguard Worker if (remove(fullPath.c_str()) != 0) {
247*38e8c45fSAndroid Build Coastguard Worker ALOGE("INIT: Error removing %s: %s", fullPath.c_str(),
248*38e8c45fSAndroid Build Coastguard Worker std::strerror(errno));
249*38e8c45fSAndroid Build Coastguard Worker }
250*38e8c45fSAndroid Build Coastguard Worker continue;
251*38e8c45fSAndroid Build Coastguard Worker }
252*38e8c45fSAndroid Build Coastguard Worker
253*38e8c45fSAndroid Build Coastguard Worker ALOGV("INIT: Entry %u is good, tracking it now.", entryHash);
254*38e8c45fSAndroid Build Coastguard Worker
255*38e8c45fSAndroid Build Coastguard Worker // Track details for rapid lookup later and update total size
256*38e8c45fSAndroid Build Coastguard Worker // Note access time is a full timespec instead of just seconds
257*38e8c45fSAndroid Build Coastguard Worker trackEntry(entryHash, header.valueSize, fileSize, st.st_atim);
258*38e8c45fSAndroid Build Coastguard Worker
259*38e8c45fSAndroid Build Coastguard Worker // Preload the entry for fast retrieval
260*38e8c45fSAndroid Build Coastguard Worker if ((mHotCacheSize + fileSize) < mHotCacheLimit) {
261*38e8c45fSAndroid Build Coastguard Worker ALOGV("INIT: Populating hot cache with fd = %i, cacheEntry = %p for "
262*38e8c45fSAndroid Build Coastguard Worker "entryHash %u",
263*38e8c45fSAndroid Build Coastguard Worker fd, mappedEntry, entryHash);
264*38e8c45fSAndroid Build Coastguard Worker
265*38e8c45fSAndroid Build Coastguard Worker // Track the details of the preload so they can be retrieved later
266*38e8c45fSAndroid Build Coastguard Worker if (!addToHotCache(entryHash, fd, mappedEntry, fileSize)) {
267*38e8c45fSAndroid Build Coastguard Worker ALOGE("INIT Failed to add %u to hot cache", entryHash);
268*38e8c45fSAndroid Build Coastguard Worker munmap(mappedEntry, fileSize);
269*38e8c45fSAndroid Build Coastguard Worker return;
270*38e8c45fSAndroid Build Coastguard Worker }
271*38e8c45fSAndroid Build Coastguard Worker } else {
272*38e8c45fSAndroid Build Coastguard Worker // If we're not keeping it in hot cache, unmap it now
273*38e8c45fSAndroid Build Coastguard Worker munmap(mappedEntry, fileSize);
274*38e8c45fSAndroid Build Coastguard Worker }
275*38e8c45fSAndroid Build Coastguard Worker }
276*38e8c45fSAndroid Build Coastguard Worker closedir(dir);
277*38e8c45fSAndroid Build Coastguard Worker } else {
278*38e8c45fSAndroid Build Coastguard Worker ALOGE("Unable to open filename: %s", mMultifileDirName.c_str());
279*38e8c45fSAndroid Build Coastguard Worker }
280*38e8c45fSAndroid Build Coastguard Worker } else {
281*38e8c45fSAndroid Build Coastguard Worker // If the multifile directory does not exist, create it and start from scratch
282*38e8c45fSAndroid Build Coastguard Worker if (mkdir(mMultifileDirName.c_str(), 0755) != 0 && (errno != EEXIST)) {
283*38e8c45fSAndroid Build Coastguard Worker ALOGE("Unable to create directory (%s), errno (%i)", mMultifileDirName.c_str(), errno);
284*38e8c45fSAndroid Build Coastguard Worker return;
285*38e8c45fSAndroid Build Coastguard Worker }
286*38e8c45fSAndroid Build Coastguard Worker
287*38e8c45fSAndroid Build Coastguard Worker // Create new status file
288*38e8c45fSAndroid Build Coastguard Worker if (!createStatus(mMultifileDirName.c_str())) {
289*38e8c45fSAndroid Build Coastguard Worker ALOGE("INIT: Failed to create status file!");
290*38e8c45fSAndroid Build Coastguard Worker return;
291*38e8c45fSAndroid Build Coastguard Worker }
292*38e8c45fSAndroid Build Coastguard Worker }
293*38e8c45fSAndroid Build Coastguard Worker
294*38e8c45fSAndroid Build Coastguard Worker ALOGV("INIT: Multifile BlobCache initialization succeeded");
295*38e8c45fSAndroid Build Coastguard Worker mInitialized = true;
296*38e8c45fSAndroid Build Coastguard Worker }
297*38e8c45fSAndroid Build Coastguard Worker
~MultifileBlobCache()298*38e8c45fSAndroid Build Coastguard Worker MultifileBlobCache::~MultifileBlobCache() {
299*38e8c45fSAndroid Build Coastguard Worker if (!mInitialized) {
300*38e8c45fSAndroid Build Coastguard Worker return;
301*38e8c45fSAndroid Build Coastguard Worker }
302*38e8c45fSAndroid Build Coastguard Worker
303*38e8c45fSAndroid Build Coastguard Worker // Inform the worker thread we're done
304*38e8c45fSAndroid Build Coastguard Worker ALOGV("DESCTRUCTOR: Shutting down worker thread");
305*38e8c45fSAndroid Build Coastguard Worker DeferredTask task(TaskCommand::Exit);
306*38e8c45fSAndroid Build Coastguard Worker queueTask(std::move(task));
307*38e8c45fSAndroid Build Coastguard Worker
308*38e8c45fSAndroid Build Coastguard Worker // Wait for it to complete
309*38e8c45fSAndroid Build Coastguard Worker ALOGV("DESCTRUCTOR: Waiting for worker thread to complete");
310*38e8c45fSAndroid Build Coastguard Worker waitForWorkComplete();
311*38e8c45fSAndroid Build Coastguard Worker if (mTaskThread.joinable()) {
312*38e8c45fSAndroid Build Coastguard Worker mTaskThread.join();
313*38e8c45fSAndroid Build Coastguard Worker }
314*38e8c45fSAndroid Build Coastguard Worker }
315*38e8c45fSAndroid Build Coastguard Worker
316*38e8c45fSAndroid Build Coastguard Worker // Set will add the entry to hot cache and start a deferred process to write it to disk
set(const void * key,EGLsizeiANDROID keySize,const void * value,EGLsizeiANDROID valueSize)317*38e8c45fSAndroid Build Coastguard Worker void MultifileBlobCache::set(const void* key, EGLsizeiANDROID keySize, const void* value,
318*38e8c45fSAndroid Build Coastguard Worker EGLsizeiANDROID valueSize) {
319*38e8c45fSAndroid Build Coastguard Worker if (!mInitialized) {
320*38e8c45fSAndroid Build Coastguard Worker return;
321*38e8c45fSAndroid Build Coastguard Worker }
322*38e8c45fSAndroid Build Coastguard Worker
323*38e8c45fSAndroid Build Coastguard Worker // Ensure key and value are under their limits
324*38e8c45fSAndroid Build Coastguard Worker if (keySize > mMaxKeySize || valueSize > mMaxValueSize) {
325*38e8c45fSAndroid Build Coastguard Worker ALOGW("SET: keySize (%lu vs %zu) or valueSize (%lu vs %zu) too large", keySize, mMaxKeySize,
326*38e8c45fSAndroid Build Coastguard Worker valueSize, mMaxValueSize);
327*38e8c45fSAndroid Build Coastguard Worker return;
328*38e8c45fSAndroid Build Coastguard Worker }
329*38e8c45fSAndroid Build Coastguard Worker
330*38e8c45fSAndroid Build Coastguard Worker // Generate a hash of the key and use it to track this entry
331*38e8c45fSAndroid Build Coastguard Worker uint32_t entryHash = android::JenkinsHashMixBytes(0, static_cast<const uint8_t*>(key), keySize);
332*38e8c45fSAndroid Build Coastguard Worker
333*38e8c45fSAndroid Build Coastguard Worker std::string fullPath = mMultifileDirName + "/" + std::to_string(entryHash);
334*38e8c45fSAndroid Build Coastguard Worker
335*38e8c45fSAndroid Build Coastguard Worker // See if we already have this file
336*38e8c45fSAndroid Build Coastguard Worker if (flags::multifile_blobcache_advanced_usage() && contains(entryHash)) {
337*38e8c45fSAndroid Build Coastguard Worker // Remove previous entry from hot cache
338*38e8c45fSAndroid Build Coastguard Worker removeFromHotCache(entryHash);
339*38e8c45fSAndroid Build Coastguard Worker
340*38e8c45fSAndroid Build Coastguard Worker // Remove previous entry and update the overall cache size
341*38e8c45fSAndroid Build Coastguard Worker removeEntry(entryHash);
342*38e8c45fSAndroid Build Coastguard Worker
343*38e8c45fSAndroid Build Coastguard Worker // If valueSize is zero, this is an indication that the user wants to remove the entry from
344*38e8c45fSAndroid Build Coastguard Worker // cache It has already been removed from tracking, now remove it from disk It is safe to do
345*38e8c45fSAndroid Build Coastguard Worker // this immediately because we drained the write queue in removeFromHotCache
346*38e8c45fSAndroid Build Coastguard Worker if (valueSize == 0) {
347*38e8c45fSAndroid Build Coastguard Worker ALOGV("SET: Zero size detected for existing entry, removing %u from cache", entryHash);
348*38e8c45fSAndroid Build Coastguard Worker if (remove(fullPath.c_str()) != 0) {
349*38e8c45fSAndroid Build Coastguard Worker ALOGW("SET: Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
350*38e8c45fSAndroid Build Coastguard Worker }
351*38e8c45fSAndroid Build Coastguard Worker return;
352*38e8c45fSAndroid Build Coastguard Worker }
353*38e8c45fSAndroid Build Coastguard Worker }
354*38e8c45fSAndroid Build Coastguard Worker
355*38e8c45fSAndroid Build Coastguard Worker size_t fileSize = sizeof(MultifileHeader) + keySize + valueSize;
356*38e8c45fSAndroid Build Coastguard Worker
357*38e8c45fSAndroid Build Coastguard Worker // If we're going to be over the cache limit, kick off a trim to clear space
358*38e8c45fSAndroid Build Coastguard Worker if (getTotalSize() + fileSize > mMaxTotalSize || getTotalEntries() + 1 > mMaxTotalEntries) {
359*38e8c45fSAndroid Build Coastguard Worker ALOGV("SET: Cache is full, calling trimCache to clear space");
360*38e8c45fSAndroid Build Coastguard Worker trimCache();
361*38e8c45fSAndroid Build Coastguard Worker }
362*38e8c45fSAndroid Build Coastguard Worker
363*38e8c45fSAndroid Build Coastguard Worker ALOGV("SET: Add %u to cache", entryHash);
364*38e8c45fSAndroid Build Coastguard Worker
365*38e8c45fSAndroid Build Coastguard Worker uint8_t* buffer = new uint8_t[fileSize];
366*38e8c45fSAndroid Build Coastguard Worker
367*38e8c45fSAndroid Build Coastguard Worker // Write placeholders for magic and CRC until deferred thread completes the write
368*38e8c45fSAndroid Build Coastguard Worker android::MultifileHeader header = {kMultifileMagic, kCrcPlaceholder, keySize, valueSize};
369*38e8c45fSAndroid Build Coastguard Worker memcpy(static_cast<void*>(buffer), static_cast<const void*>(&header),
370*38e8c45fSAndroid Build Coastguard Worker sizeof(android::MultifileHeader));
371*38e8c45fSAndroid Build Coastguard Worker // Write the key and value after the header
372*38e8c45fSAndroid Build Coastguard Worker memcpy(static_cast<void*>(buffer + sizeof(MultifileHeader)), static_cast<const void*>(key),
373*38e8c45fSAndroid Build Coastguard Worker keySize);
374*38e8c45fSAndroid Build Coastguard Worker memcpy(static_cast<void*>(buffer + sizeof(MultifileHeader) + keySize),
375*38e8c45fSAndroid Build Coastguard Worker static_cast<const void*>(value), valueSize);
376*38e8c45fSAndroid Build Coastguard Worker
377*38e8c45fSAndroid Build Coastguard Worker // Track the size and access time for quick recall and update the overall cache size
378*38e8c45fSAndroid Build Coastguard Worker struct timespec time = {0, 0};
379*38e8c45fSAndroid Build Coastguard Worker if (flags::multifile_blobcache_advanced_usage()) {
380*38e8c45fSAndroid Build Coastguard Worker clock_gettime(CLOCK_REALTIME, &time);
381*38e8c45fSAndroid Build Coastguard Worker }
382*38e8c45fSAndroid Build Coastguard Worker trackEntry(entryHash, valueSize, fileSize, time);
383*38e8c45fSAndroid Build Coastguard Worker
384*38e8c45fSAndroid Build Coastguard Worker // Keep the entry in hot cache for quick retrieval
385*38e8c45fSAndroid Build Coastguard Worker ALOGV("SET: Adding %u to hot cache.", entryHash);
386*38e8c45fSAndroid Build Coastguard Worker
387*38e8c45fSAndroid Build Coastguard Worker // Sending -1 as the fd indicates we don't have an fd for this
388*38e8c45fSAndroid Build Coastguard Worker if (!addToHotCache(entryHash, -1, buffer, fileSize)) {
389*38e8c45fSAndroid Build Coastguard Worker ALOGE("SET: Failed to add %u to hot cache", entryHash);
390*38e8c45fSAndroid Build Coastguard Worker delete[] buffer;
391*38e8c45fSAndroid Build Coastguard Worker return;
392*38e8c45fSAndroid Build Coastguard Worker }
393*38e8c45fSAndroid Build Coastguard Worker
394*38e8c45fSAndroid Build Coastguard Worker // Track that we're creating a pending write for this entry
395*38e8c45fSAndroid Build Coastguard Worker // Include the buffer to handle the case when multiple writes are pending for an entry
396*38e8c45fSAndroid Build Coastguard Worker {
397*38e8c45fSAndroid Build Coastguard Worker // Synchronize access to deferred write status
398*38e8c45fSAndroid Build Coastguard Worker std::lock_guard<std::mutex> lock(mDeferredWriteStatusMutex);
399*38e8c45fSAndroid Build Coastguard Worker mDeferredWrites.insert(std::make_pair(entryHash, buffer));
400*38e8c45fSAndroid Build Coastguard Worker }
401*38e8c45fSAndroid Build Coastguard Worker
402*38e8c45fSAndroid Build Coastguard Worker // Create deferred task to write to storage
403*38e8c45fSAndroid Build Coastguard Worker ALOGV("SET: Adding task to queue.");
404*38e8c45fSAndroid Build Coastguard Worker DeferredTask task(TaskCommand::WriteToDisk);
405*38e8c45fSAndroid Build Coastguard Worker task.initWriteToDisk(entryHash, fullPath, buffer, fileSize);
406*38e8c45fSAndroid Build Coastguard Worker queueTask(std::move(task));
407*38e8c45fSAndroid Build Coastguard Worker }
408*38e8c45fSAndroid Build Coastguard Worker
409*38e8c45fSAndroid Build Coastguard Worker // Get will check the hot cache, then load it from disk if needed
get(const void * key,EGLsizeiANDROID keySize,void * value,EGLsizeiANDROID valueSize)410*38e8c45fSAndroid Build Coastguard Worker EGLsizeiANDROID MultifileBlobCache::get(const void* key, EGLsizeiANDROID keySize, void* value,
411*38e8c45fSAndroid Build Coastguard Worker EGLsizeiANDROID valueSize) {
412*38e8c45fSAndroid Build Coastguard Worker if (!mInitialized) {
413*38e8c45fSAndroid Build Coastguard Worker return 0;
414*38e8c45fSAndroid Build Coastguard Worker }
415*38e8c45fSAndroid Build Coastguard Worker
416*38e8c45fSAndroid Build Coastguard Worker // Ensure key and value are under their limits
417*38e8c45fSAndroid Build Coastguard Worker if (keySize > mMaxKeySize || valueSize > mMaxValueSize) {
418*38e8c45fSAndroid Build Coastguard Worker ALOGW("GET: keySize (%lu vs %zu) or valueSize (%lu vs %zu) too large", keySize, mMaxKeySize,
419*38e8c45fSAndroid Build Coastguard Worker valueSize, mMaxValueSize);
420*38e8c45fSAndroid Build Coastguard Worker return 0;
421*38e8c45fSAndroid Build Coastguard Worker }
422*38e8c45fSAndroid Build Coastguard Worker
423*38e8c45fSAndroid Build Coastguard Worker // Generate a hash of the key and use it to track this entry
424*38e8c45fSAndroid Build Coastguard Worker uint32_t entryHash = android::JenkinsHashMixBytes(0, static_cast<const uint8_t*>(key), keySize);
425*38e8c45fSAndroid Build Coastguard Worker
426*38e8c45fSAndroid Build Coastguard Worker // See if we have this file
427*38e8c45fSAndroid Build Coastguard Worker if (!contains(entryHash)) {
428*38e8c45fSAndroid Build Coastguard Worker ALOGV("GET: Cache MISS - cache does not contain entry: %u", entryHash);
429*38e8c45fSAndroid Build Coastguard Worker return 0;
430*38e8c45fSAndroid Build Coastguard Worker }
431*38e8c45fSAndroid Build Coastguard Worker
432*38e8c45fSAndroid Build Coastguard Worker // Look up the data for this entry
433*38e8c45fSAndroid Build Coastguard Worker MultifileEntryStats entryStats = getEntryStats(entryHash);
434*38e8c45fSAndroid Build Coastguard Worker
435*38e8c45fSAndroid Build Coastguard Worker size_t cachedValueSize = entryStats.valueSize;
436*38e8c45fSAndroid Build Coastguard Worker if (cachedValueSize > valueSize) {
437*38e8c45fSAndroid Build Coastguard Worker ALOGV("GET: Cache MISS - valueSize not large enough (%lu) for entry %u, returning required"
438*38e8c45fSAndroid Build Coastguard Worker "size (%zu)",
439*38e8c45fSAndroid Build Coastguard Worker valueSize, entryHash, cachedValueSize);
440*38e8c45fSAndroid Build Coastguard Worker return cachedValueSize;
441*38e8c45fSAndroid Build Coastguard Worker }
442*38e8c45fSAndroid Build Coastguard Worker
443*38e8c45fSAndroid Build Coastguard Worker // We have the file and have enough room to write it out, return the entry
444*38e8c45fSAndroid Build Coastguard Worker ALOGV("GET: Cache HIT - cache contains entry: %u", entryHash);
445*38e8c45fSAndroid Build Coastguard Worker
446*38e8c45fSAndroid Build Coastguard Worker // Look up the size of the file
447*38e8c45fSAndroid Build Coastguard Worker size_t fileSize = entryStats.fileSize;
448*38e8c45fSAndroid Build Coastguard Worker if (keySize > fileSize) {
449*38e8c45fSAndroid Build Coastguard Worker ALOGW("keySize (%lu) is larger than entrySize (%zu). This is a hash collision or modified "
450*38e8c45fSAndroid Build Coastguard Worker "file",
451*38e8c45fSAndroid Build Coastguard Worker keySize, fileSize);
452*38e8c45fSAndroid Build Coastguard Worker return 0;
453*38e8c45fSAndroid Build Coastguard Worker }
454*38e8c45fSAndroid Build Coastguard Worker
455*38e8c45fSAndroid Build Coastguard Worker std::string fullPath = mMultifileDirName + "/" + std::to_string(entryHash);
456*38e8c45fSAndroid Build Coastguard Worker
457*38e8c45fSAndroid Build Coastguard Worker // Open the hashed filename path
458*38e8c45fSAndroid Build Coastguard Worker uint8_t* cacheEntry = 0;
459*38e8c45fSAndroid Build Coastguard Worker
460*38e8c45fSAndroid Build Coastguard Worker // Check hot cache
461*38e8c45fSAndroid Build Coastguard Worker if (mHotCache.find(entryHash) != mHotCache.end()) {
462*38e8c45fSAndroid Build Coastguard Worker ALOGV("GET: HotCache HIT for entry %u", entryHash);
463*38e8c45fSAndroid Build Coastguard Worker cacheEntry = mHotCache[entryHash].entryBuffer;
464*38e8c45fSAndroid Build Coastguard Worker
465*38e8c45fSAndroid Build Coastguard Worker if (flags::multifile_blobcache_advanced_usage()) {
466*38e8c45fSAndroid Build Coastguard Worker // Update last access time on disk
467*38e8c45fSAndroid Build Coastguard Worker struct timespec times[2];
468*38e8c45fSAndroid Build Coastguard Worker times[0].tv_nsec = UTIME_NOW;
469*38e8c45fSAndroid Build Coastguard Worker times[1].tv_nsec = UTIME_OMIT;
470*38e8c45fSAndroid Build Coastguard Worker utimensat(0, fullPath.c_str(), times, 0);
471*38e8c45fSAndroid Build Coastguard Worker }
472*38e8c45fSAndroid Build Coastguard Worker } else {
473*38e8c45fSAndroid Build Coastguard Worker ALOGV("GET: HotCache MISS for entry: %u", entryHash);
474*38e8c45fSAndroid Build Coastguard Worker
475*38e8c45fSAndroid Build Coastguard Worker // Wait for writes to complete if there is an outstanding write for this entry
476*38e8c45fSAndroid Build Coastguard Worker bool wait = false;
477*38e8c45fSAndroid Build Coastguard Worker {
478*38e8c45fSAndroid Build Coastguard Worker // Synchronize access to deferred write status
479*38e8c45fSAndroid Build Coastguard Worker std::lock_guard<std::mutex> lock(mDeferredWriteStatusMutex);
480*38e8c45fSAndroid Build Coastguard Worker wait = mDeferredWrites.find(entryHash) != mDeferredWrites.end();
481*38e8c45fSAndroid Build Coastguard Worker }
482*38e8c45fSAndroid Build Coastguard Worker
483*38e8c45fSAndroid Build Coastguard Worker if (wait) {
484*38e8c45fSAndroid Build Coastguard Worker ALOGV("GET: Waiting for write to complete for %u", entryHash);
485*38e8c45fSAndroid Build Coastguard Worker waitForWorkComplete();
486*38e8c45fSAndroid Build Coastguard Worker }
487*38e8c45fSAndroid Build Coastguard Worker
488*38e8c45fSAndroid Build Coastguard Worker // Open the entry file
489*38e8c45fSAndroid Build Coastguard Worker int fd = open(fullPath.c_str(), O_RDONLY);
490*38e8c45fSAndroid Build Coastguard Worker if (fd == -1) {
491*38e8c45fSAndroid Build Coastguard Worker ALOGE("Cache error - failed to open fullPath: %s, error: %s", fullPath.c_str(),
492*38e8c45fSAndroid Build Coastguard Worker std::strerror(errno));
493*38e8c45fSAndroid Build Coastguard Worker return 0;
494*38e8c45fSAndroid Build Coastguard Worker }
495*38e8c45fSAndroid Build Coastguard Worker
496*38e8c45fSAndroid Build Coastguard Worker // Memory map the file
497*38e8c45fSAndroid Build Coastguard Worker cacheEntry =
498*38e8c45fSAndroid Build Coastguard Worker reinterpret_cast<uint8_t*>(mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0));
499*38e8c45fSAndroid Build Coastguard Worker
500*38e8c45fSAndroid Build Coastguard Worker if (flags::multifile_blobcache_advanced_usage()) {
501*38e8c45fSAndroid Build Coastguard Worker // Update last access time and omit last modify time
502*38e8c45fSAndroid Build Coastguard Worker struct timespec times[2];
503*38e8c45fSAndroid Build Coastguard Worker times[0].tv_nsec = UTIME_NOW;
504*38e8c45fSAndroid Build Coastguard Worker times[1].tv_nsec = UTIME_OMIT;
505*38e8c45fSAndroid Build Coastguard Worker futimens(fd, times);
506*38e8c45fSAndroid Build Coastguard Worker }
507*38e8c45fSAndroid Build Coastguard Worker
508*38e8c45fSAndroid Build Coastguard Worker // We can close the file now and the mmap will remain
509*38e8c45fSAndroid Build Coastguard Worker close(fd);
510*38e8c45fSAndroid Build Coastguard Worker
511*38e8c45fSAndroid Build Coastguard Worker if (cacheEntry == MAP_FAILED) {
512*38e8c45fSAndroid Build Coastguard Worker ALOGE("Failed to mmap cacheEntry, error: %s", std::strerror(errno));
513*38e8c45fSAndroid Build Coastguard Worker return 0;
514*38e8c45fSAndroid Build Coastguard Worker }
515*38e8c45fSAndroid Build Coastguard Worker
516*38e8c45fSAndroid Build Coastguard Worker ALOGV("GET: Adding %u to hot cache", entryHash);
517*38e8c45fSAndroid Build Coastguard Worker if (!addToHotCache(entryHash, fd, cacheEntry, fileSize)) {
518*38e8c45fSAndroid Build Coastguard Worker ALOGE("GET: Failed to add %u to hot cache", entryHash);
519*38e8c45fSAndroid Build Coastguard Worker return 0;
520*38e8c45fSAndroid Build Coastguard Worker }
521*38e8c45fSAndroid Build Coastguard Worker
522*38e8c45fSAndroid Build Coastguard Worker cacheEntry = mHotCache[entryHash].entryBuffer;
523*38e8c45fSAndroid Build Coastguard Worker }
524*38e8c45fSAndroid Build Coastguard Worker
525*38e8c45fSAndroid Build Coastguard Worker // Ensure the header matches
526*38e8c45fSAndroid Build Coastguard Worker MultifileHeader* header = reinterpret_cast<MultifileHeader*>(cacheEntry);
527*38e8c45fSAndroid Build Coastguard Worker if (header->keySize != keySize || header->valueSize != valueSize) {
528*38e8c45fSAndroid Build Coastguard Worker ALOGW("Mismatch on keySize(%ld vs. cached %ld) or valueSize(%ld vs. cached %ld) compared "
529*38e8c45fSAndroid Build Coastguard Worker "to cache header values for fullPath: %s",
530*38e8c45fSAndroid Build Coastguard Worker keySize, header->keySize, valueSize, header->valueSize, fullPath.c_str());
531*38e8c45fSAndroid Build Coastguard Worker removeFromHotCache(entryHash);
532*38e8c45fSAndroid Build Coastguard Worker return 0;
533*38e8c45fSAndroid Build Coastguard Worker }
534*38e8c45fSAndroid Build Coastguard Worker
535*38e8c45fSAndroid Build Coastguard Worker // Compare the incoming key with our stored version (the beginning of the entry)
536*38e8c45fSAndroid Build Coastguard Worker uint8_t* cachedKey = cacheEntry + sizeof(MultifileHeader);
537*38e8c45fSAndroid Build Coastguard Worker int compare = memcmp(cachedKey, key, keySize);
538*38e8c45fSAndroid Build Coastguard Worker if (compare != 0) {
539*38e8c45fSAndroid Build Coastguard Worker ALOGW("Cached key and new key do not match! This is a hash collision or modified file");
540*38e8c45fSAndroid Build Coastguard Worker removeFromHotCache(entryHash);
541*38e8c45fSAndroid Build Coastguard Worker return 0;
542*38e8c45fSAndroid Build Coastguard Worker }
543*38e8c45fSAndroid Build Coastguard Worker
544*38e8c45fSAndroid Build Coastguard Worker if (flags::multifile_blobcache_advanced_usage()) {
545*38e8c45fSAndroid Build Coastguard Worker // Update the entry time for this hash, so it reflects LRU
546*38e8c45fSAndroid Build Coastguard Worker struct timespec time;
547*38e8c45fSAndroid Build Coastguard Worker clock_gettime(CLOCK_REALTIME, &time);
548*38e8c45fSAndroid Build Coastguard Worker updateEntryTime(entryHash, time);
549*38e8c45fSAndroid Build Coastguard Worker }
550*38e8c45fSAndroid Build Coastguard Worker
551*38e8c45fSAndroid Build Coastguard Worker // Remaining entry following the key is the value
552*38e8c45fSAndroid Build Coastguard Worker uint8_t* cachedValue = cacheEntry + (keySize + sizeof(MultifileHeader));
553*38e8c45fSAndroid Build Coastguard Worker memcpy(value, cachedValue, cachedValueSize);
554*38e8c45fSAndroid Build Coastguard Worker
555*38e8c45fSAndroid Build Coastguard Worker return cachedValueSize;
556*38e8c45fSAndroid Build Coastguard Worker }
557*38e8c45fSAndroid Build Coastguard Worker
finish()558*38e8c45fSAndroid Build Coastguard Worker void MultifileBlobCache::finish() {
559*38e8c45fSAndroid Build Coastguard Worker if (!mInitialized) {
560*38e8c45fSAndroid Build Coastguard Worker return;
561*38e8c45fSAndroid Build Coastguard Worker }
562*38e8c45fSAndroid Build Coastguard Worker
563*38e8c45fSAndroid Build Coastguard Worker // Wait for all deferred writes to complete
564*38e8c45fSAndroid Build Coastguard Worker ALOGV("FINISH: Waiting for work to complete.");
565*38e8c45fSAndroid Build Coastguard Worker waitForWorkComplete();
566*38e8c45fSAndroid Build Coastguard Worker
567*38e8c45fSAndroid Build Coastguard Worker // Close all entries in the hot cache
568*38e8c45fSAndroid Build Coastguard Worker for (auto hotCacheIter = mHotCache.begin(); hotCacheIter != mHotCache.end();) {
569*38e8c45fSAndroid Build Coastguard Worker uint32_t entryHash = hotCacheIter->first;
570*38e8c45fSAndroid Build Coastguard Worker MultifileHotCache entry = hotCacheIter->second;
571*38e8c45fSAndroid Build Coastguard Worker
572*38e8c45fSAndroid Build Coastguard Worker ALOGV("FINISH: Closing hot cache entry for %u", entryHash);
573*38e8c45fSAndroid Build Coastguard Worker freeHotCacheEntry(entry);
574*38e8c45fSAndroid Build Coastguard Worker
575*38e8c45fSAndroid Build Coastguard Worker mHotCache.erase(hotCacheIter++);
576*38e8c45fSAndroid Build Coastguard Worker }
577*38e8c45fSAndroid Build Coastguard Worker }
578*38e8c45fSAndroid Build Coastguard Worker
createStatus(const std::string & baseDir)579*38e8c45fSAndroid Build Coastguard Worker bool MultifileBlobCache::createStatus(const std::string& baseDir) {
580*38e8c45fSAndroid Build Coastguard Worker // Populate the status struct
581*38e8c45fSAndroid Build Coastguard Worker struct MultifileStatus status;
582*38e8c45fSAndroid Build Coastguard Worker memset(&status, 0, sizeof(status));
583*38e8c45fSAndroid Build Coastguard Worker status.magic = kMultifileMagic;
584*38e8c45fSAndroid Build Coastguard Worker status.cacheVersion = mCacheVersion;
585*38e8c45fSAndroid Build Coastguard Worker
586*38e8c45fSAndroid Build Coastguard Worker // Copy the buildId string in, up to our allocated space
587*38e8c45fSAndroid Build Coastguard Worker strncpy(status.buildId, mBuildId.c_str(),
588*38e8c45fSAndroid Build Coastguard Worker mBuildId.length() > PROP_VALUE_MAX ? PROP_VALUE_MAX : mBuildId.length());
589*38e8c45fSAndroid Build Coastguard Worker
590*38e8c45fSAndroid Build Coastguard Worker // Finally update the crc, using cacheVersion and everything the follows
591*38e8c45fSAndroid Build Coastguard Worker status.crc = GenerateCRC32(
592*38e8c45fSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&status) + offsetof(MultifileStatus, cacheVersion),
593*38e8c45fSAndroid Build Coastguard Worker sizeof(status) - offsetof(MultifileStatus, cacheVersion));
594*38e8c45fSAndroid Build Coastguard Worker
595*38e8c45fSAndroid Build Coastguard Worker // Create the status file
596*38e8c45fSAndroid Build Coastguard Worker std::string cacheStatus = baseDir + "/" + kMultifileBlobCacheStatusFile;
597*38e8c45fSAndroid Build Coastguard Worker int fd = open(cacheStatus.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
598*38e8c45fSAndroid Build Coastguard Worker if (fd == -1) {
599*38e8c45fSAndroid Build Coastguard Worker ALOGE("STATUS(CREATE): Unable to create status file: %s, error: %s", cacheStatus.c_str(),
600*38e8c45fSAndroid Build Coastguard Worker std::strerror(errno));
601*38e8c45fSAndroid Build Coastguard Worker return false;
602*38e8c45fSAndroid Build Coastguard Worker }
603*38e8c45fSAndroid Build Coastguard Worker
604*38e8c45fSAndroid Build Coastguard Worker // Write the buffer contents to disk
605*38e8c45fSAndroid Build Coastguard Worker ssize_t result = write(fd, &status, sizeof(status));
606*38e8c45fSAndroid Build Coastguard Worker close(fd);
607*38e8c45fSAndroid Build Coastguard Worker if (result != sizeof(status)) {
608*38e8c45fSAndroid Build Coastguard Worker ALOGE("STATUS(CREATE): Error writing cache status file: %s, error %s", cacheStatus.c_str(),
609*38e8c45fSAndroid Build Coastguard Worker std::strerror(errno));
610*38e8c45fSAndroid Build Coastguard Worker return false;
611*38e8c45fSAndroid Build Coastguard Worker }
612*38e8c45fSAndroid Build Coastguard Worker
613*38e8c45fSAndroid Build Coastguard Worker ALOGV("STATUS(CREATE): Created status file: %s", cacheStatus.c_str());
614*38e8c45fSAndroid Build Coastguard Worker return true;
615*38e8c45fSAndroid Build Coastguard Worker }
616*38e8c45fSAndroid Build Coastguard Worker
checkStatus(const std::string & baseDir)617*38e8c45fSAndroid Build Coastguard Worker bool MultifileBlobCache::checkStatus(const std::string& baseDir) {
618*38e8c45fSAndroid Build Coastguard Worker std::string cacheStatus = baseDir + "/" + kMultifileBlobCacheStatusFile;
619*38e8c45fSAndroid Build Coastguard Worker
620*38e8c45fSAndroid Build Coastguard Worker // Does status exist
621*38e8c45fSAndroid Build Coastguard Worker struct stat st;
622*38e8c45fSAndroid Build Coastguard Worker if (stat(cacheStatus.c_str(), &st) != 0) {
623*38e8c45fSAndroid Build Coastguard Worker ALOGV("STATUS(CHECK): Status file (%s) missing", cacheStatus.c_str());
624*38e8c45fSAndroid Build Coastguard Worker return false;
625*38e8c45fSAndroid Build Coastguard Worker }
626*38e8c45fSAndroid Build Coastguard Worker
627*38e8c45fSAndroid Build Coastguard Worker // If the status entry is damaged or no good, remove it
628*38e8c45fSAndroid Build Coastguard Worker if (st.st_size <= 0 || st.st_atime <= 0) {
629*38e8c45fSAndroid Build Coastguard Worker ALOGE("STATUS(CHECK): Cache status has invalid stats!");
630*38e8c45fSAndroid Build Coastguard Worker return false;
631*38e8c45fSAndroid Build Coastguard Worker }
632*38e8c45fSAndroid Build Coastguard Worker
633*38e8c45fSAndroid Build Coastguard Worker // Open the file so we can read its header
634*38e8c45fSAndroid Build Coastguard Worker int fd = open(cacheStatus.c_str(), O_RDONLY);
635*38e8c45fSAndroid Build Coastguard Worker if (fd == -1) {
636*38e8c45fSAndroid Build Coastguard Worker ALOGE("STATUS(CHECK): Cache error - failed to open cacheStatus: %s, error: %s",
637*38e8c45fSAndroid Build Coastguard Worker cacheStatus.c_str(), std::strerror(errno));
638*38e8c45fSAndroid Build Coastguard Worker return false;
639*38e8c45fSAndroid Build Coastguard Worker }
640*38e8c45fSAndroid Build Coastguard Worker
641*38e8c45fSAndroid Build Coastguard Worker // Read in the status header
642*38e8c45fSAndroid Build Coastguard Worker MultifileStatus status;
643*38e8c45fSAndroid Build Coastguard Worker size_t result = read(fd, static_cast<void*>(&status), sizeof(MultifileStatus));
644*38e8c45fSAndroid Build Coastguard Worker close(fd);
645*38e8c45fSAndroid Build Coastguard Worker if (result != sizeof(MultifileStatus)) {
646*38e8c45fSAndroid Build Coastguard Worker ALOGE("STATUS(CHECK): Error reading cache status (%s): %s", cacheStatus.c_str(),
647*38e8c45fSAndroid Build Coastguard Worker std::strerror(errno));
648*38e8c45fSAndroid Build Coastguard Worker return false;
649*38e8c45fSAndroid Build Coastguard Worker }
650*38e8c45fSAndroid Build Coastguard Worker
651*38e8c45fSAndroid Build Coastguard Worker // Verify header magic
652*38e8c45fSAndroid Build Coastguard Worker if (status.magic != kMultifileMagic) {
653*38e8c45fSAndroid Build Coastguard Worker ALOGE("STATUS(CHECK): Cache status has bad magic (%u)!", status.magic);
654*38e8c45fSAndroid Build Coastguard Worker return false;
655*38e8c45fSAndroid Build Coastguard Worker }
656*38e8c45fSAndroid Build Coastguard Worker
657*38e8c45fSAndroid Build Coastguard Worker // Ensure we have a good CRC
658*38e8c45fSAndroid Build Coastguard Worker if (status.crc != GenerateCRC32(reinterpret_cast<uint8_t *>(&status) +
659*38e8c45fSAndroid Build Coastguard Worker offsetof(MultifileStatus, cacheVersion),
660*38e8c45fSAndroid Build Coastguard Worker sizeof(status) - offsetof(MultifileStatus, cacheVersion))) {
661*38e8c45fSAndroid Build Coastguard Worker ALOGE("STATUS(CHECK): Cache status failed CRC check!");
662*38e8c45fSAndroid Build Coastguard Worker return false;
663*38e8c45fSAndroid Build Coastguard Worker }
664*38e8c45fSAndroid Build Coastguard Worker
665*38e8c45fSAndroid Build Coastguard Worker // Check cacheVersion
666*38e8c45fSAndroid Build Coastguard Worker if (status.cacheVersion != mCacheVersion) {
667*38e8c45fSAndroid Build Coastguard Worker ALOGV("STATUS(CHECK): Cache version has changed! old(%u) new(%u)", status.cacheVersion,
668*38e8c45fSAndroid Build Coastguard Worker mCacheVersion);
669*38e8c45fSAndroid Build Coastguard Worker return false;
670*38e8c45fSAndroid Build Coastguard Worker }
671*38e8c45fSAndroid Build Coastguard Worker
672*38e8c45fSAndroid Build Coastguard Worker // Check buildId
673*38e8c45fSAndroid Build Coastguard Worker if (strcmp(status.buildId, mBuildId.c_str()) != 0) {
674*38e8c45fSAndroid Build Coastguard Worker ALOGV("STATUS(CHECK): BuildId has changed! old(%s) new(%s)", status.buildId,
675*38e8c45fSAndroid Build Coastguard Worker mBuildId.c_str());
676*38e8c45fSAndroid Build Coastguard Worker return false;
677*38e8c45fSAndroid Build Coastguard Worker }
678*38e8c45fSAndroid Build Coastguard Worker
679*38e8c45fSAndroid Build Coastguard Worker // All checks passed!
680*38e8c45fSAndroid Build Coastguard Worker ALOGV("STATUS(CHECK): Status file is good! cacheVersion(%u), buildId(%s) file(%s)",
681*38e8c45fSAndroid Build Coastguard Worker status.cacheVersion, status.buildId, cacheStatus.c_str());
682*38e8c45fSAndroid Build Coastguard Worker return true;
683*38e8c45fSAndroid Build Coastguard Worker }
684*38e8c45fSAndroid Build Coastguard Worker
trackEntry(uint32_t entryHash,EGLsizeiANDROID valueSize,size_t fileSize,const timespec & accessTime)685*38e8c45fSAndroid Build Coastguard Worker void MultifileBlobCache::trackEntry(uint32_t entryHash, EGLsizeiANDROID valueSize, size_t fileSize,
686*38e8c45fSAndroid Build Coastguard Worker const timespec& accessTime) {
687*38e8c45fSAndroid Build Coastguard Worker #if COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
688*38e8c45fSAndroid Build Coastguard Worker // When we add this entry to the map, it is sorted by accessTime
689*38e8c45fSAndroid Build Coastguard Worker MultifileEntryStatsMapIter entryStatsIter =
690*38e8c45fSAndroid Build Coastguard Worker mEntryStats.emplace(std::piecewise_construct, std::forward_as_tuple(accessTime),
691*38e8c45fSAndroid Build Coastguard Worker std::forward_as_tuple(entryHash, valueSize, fileSize));
692*38e8c45fSAndroid Build Coastguard Worker
693*38e8c45fSAndroid Build Coastguard Worker // Track all entries with quick access to its stats
694*38e8c45fSAndroid Build Coastguard Worker mEntries.emplace(entryHash, entryStatsIter);
695*38e8c45fSAndroid Build Coastguard Worker #else
696*38e8c45fSAndroid Build Coastguard Worker (void)accessTime;
697*38e8c45fSAndroid Build Coastguard Worker mEntries.insert(entryHash);
698*38e8c45fSAndroid Build Coastguard Worker mEntryStats[entryHash] = {entryHash, valueSize, fileSize};
699*38e8c45fSAndroid Build Coastguard Worker #endif // COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
700*38e8c45fSAndroid Build Coastguard Worker
701*38e8c45fSAndroid Build Coastguard Worker increaseTotalCacheSize(fileSize);
702*38e8c45fSAndroid Build Coastguard Worker }
703*38e8c45fSAndroid Build Coastguard Worker
removeEntry(uint32_t entryHash)704*38e8c45fSAndroid Build Coastguard Worker bool MultifileBlobCache::removeEntry(uint32_t entryHash) {
705*38e8c45fSAndroid Build Coastguard Worker auto entryIter = mEntries.find(entryHash);
706*38e8c45fSAndroid Build Coastguard Worker if (entryIter == mEntries.end()) {
707*38e8c45fSAndroid Build Coastguard Worker return false;
708*38e8c45fSAndroid Build Coastguard Worker }
709*38e8c45fSAndroid Build Coastguard Worker
710*38e8c45fSAndroid Build Coastguard Worker #if COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
711*38e8c45fSAndroid Build Coastguard Worker MultifileEntryStatsMapIter entryStatsIter = entryIter->second;
712*38e8c45fSAndroid Build Coastguard Worker MultifileEntryStats entryStats = entryStatsIter->second;
713*38e8c45fSAndroid Build Coastguard Worker decreaseTotalCacheSize(entryStats.fileSize);
714*38e8c45fSAndroid Build Coastguard Worker #else
715*38e8c45fSAndroid Build Coastguard Worker auto entryStatsIter = mEntryStats.find(entryHash);
716*38e8c45fSAndroid Build Coastguard Worker if (entryStatsIter == mEntryStats.end()) {
717*38e8c45fSAndroid Build Coastguard Worker ALOGE("Failed to remove entryHash (%u) from mEntryStats", entryHash);
718*38e8c45fSAndroid Build Coastguard Worker return false;
719*38e8c45fSAndroid Build Coastguard Worker }
720*38e8c45fSAndroid Build Coastguard Worker decreaseTotalCacheSize(entryStatsIter->second.fileSize);
721*38e8c45fSAndroid Build Coastguard Worker #endif // COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
722*38e8c45fSAndroid Build Coastguard Worker
723*38e8c45fSAndroid Build Coastguard Worker mEntryStats.erase(entryStatsIter);
724*38e8c45fSAndroid Build Coastguard Worker mEntries.erase(entryIter);
725*38e8c45fSAndroid Build Coastguard Worker
726*38e8c45fSAndroid Build Coastguard Worker return true;
727*38e8c45fSAndroid Build Coastguard Worker }
728*38e8c45fSAndroid Build Coastguard Worker
contains(uint32_t hashEntry) const729*38e8c45fSAndroid Build Coastguard Worker bool MultifileBlobCache::contains(uint32_t hashEntry) const {
730*38e8c45fSAndroid Build Coastguard Worker return mEntries.find(hashEntry) != mEntries.end();
731*38e8c45fSAndroid Build Coastguard Worker }
732*38e8c45fSAndroid Build Coastguard Worker
getEntryStats(uint32_t entryHash)733*38e8c45fSAndroid Build Coastguard Worker MultifileEntryStats MultifileBlobCache::getEntryStats(uint32_t entryHash) {
734*38e8c45fSAndroid Build Coastguard Worker #if COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
735*38e8c45fSAndroid Build Coastguard Worker auto entryIter = mEntries.find(entryHash);
736*38e8c45fSAndroid Build Coastguard Worker if (entryIter == mEntries.end()) {
737*38e8c45fSAndroid Build Coastguard Worker return {};
738*38e8c45fSAndroid Build Coastguard Worker }
739*38e8c45fSAndroid Build Coastguard Worker
740*38e8c45fSAndroid Build Coastguard Worker MultifileEntryStatsMapIter entryStatsIter = entryIter->second;
741*38e8c45fSAndroid Build Coastguard Worker MultifileEntryStats entryStats = entryStatsIter->second;
742*38e8c45fSAndroid Build Coastguard Worker return entryStats;
743*38e8c45fSAndroid Build Coastguard Worker #else
744*38e8c45fSAndroid Build Coastguard Worker return mEntryStats[entryHash];
745*38e8c45fSAndroid Build Coastguard Worker #endif // COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
746*38e8c45fSAndroid Build Coastguard Worker }
747*38e8c45fSAndroid Build Coastguard Worker
updateEntryTime(uint32_t entryHash,const timespec & newTime)748*38e8c45fSAndroid Build Coastguard Worker void MultifileBlobCache::updateEntryTime(uint32_t entryHash, const timespec& newTime) {
749*38e8c45fSAndroid Build Coastguard Worker #if COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
750*38e8c45fSAndroid Build Coastguard Worker // This function updates the ordering of the map by removing the old iterators
751*38e8c45fSAndroid Build Coastguard Worker // and re-adding them. If should be perforant as it does not perform a full re-sort.
752*38e8c45fSAndroid Build Coastguard Worker // First, pull out the old entryStats
753*38e8c45fSAndroid Build Coastguard Worker auto entryIter = mEntries.find(entryHash);
754*38e8c45fSAndroid Build Coastguard Worker MultifileEntryStatsMapIter entryStatsIter = entryIter->second;
755*38e8c45fSAndroid Build Coastguard Worker MultifileEntryStats entryStats = std::move(entryStatsIter->second);
756*38e8c45fSAndroid Build Coastguard Worker
757*38e8c45fSAndroid Build Coastguard Worker // Remove the old iterators
758*38e8c45fSAndroid Build Coastguard Worker mEntryStats.erase(entryStatsIter);
759*38e8c45fSAndroid Build Coastguard Worker mEntries.erase(entryIter);
760*38e8c45fSAndroid Build Coastguard Worker
761*38e8c45fSAndroid Build Coastguard Worker // Insert the new with updated time
762*38e8c45fSAndroid Build Coastguard Worker entryStatsIter = mEntryStats.emplace(std::make_pair(newTime, std::move(entryStats)));
763*38e8c45fSAndroid Build Coastguard Worker mEntries.emplace(entryHash, entryStatsIter);
764*38e8c45fSAndroid Build Coastguard Worker #else
765*38e8c45fSAndroid Build Coastguard Worker (void)entryHash;
766*38e8c45fSAndroid Build Coastguard Worker (void)newTime;
767*38e8c45fSAndroid Build Coastguard Worker #endif // COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
768*38e8c45fSAndroid Build Coastguard Worker }
769*38e8c45fSAndroid Build Coastguard Worker
increaseTotalCacheSize(size_t fileSize)770*38e8c45fSAndroid Build Coastguard Worker void MultifileBlobCache::increaseTotalCacheSize(size_t fileSize) {
771*38e8c45fSAndroid Build Coastguard Worker mTotalCacheSize += fileSize;
772*38e8c45fSAndroid Build Coastguard Worker mTotalCacheEntries++;
773*38e8c45fSAndroid Build Coastguard Worker }
774*38e8c45fSAndroid Build Coastguard Worker
decreaseTotalCacheSize(size_t fileSize)775*38e8c45fSAndroid Build Coastguard Worker void MultifileBlobCache::decreaseTotalCacheSize(size_t fileSize) {
776*38e8c45fSAndroid Build Coastguard Worker mTotalCacheSize -= fileSize;
777*38e8c45fSAndroid Build Coastguard Worker mTotalCacheEntries--;
778*38e8c45fSAndroid Build Coastguard Worker }
779*38e8c45fSAndroid Build Coastguard Worker
addToHotCache(uint32_t newEntryHash,int newFd,uint8_t * newEntryBuffer,size_t newEntrySize)780*38e8c45fSAndroid Build Coastguard Worker bool MultifileBlobCache::addToHotCache(uint32_t newEntryHash, int newFd, uint8_t* newEntryBuffer,
781*38e8c45fSAndroid Build Coastguard Worker size_t newEntrySize) {
782*38e8c45fSAndroid Build Coastguard Worker ALOGV("HOTCACHE(ADD): Adding %u to hot cache", newEntryHash);
783*38e8c45fSAndroid Build Coastguard Worker
784*38e8c45fSAndroid Build Coastguard Worker // Clear space if we need to
785*38e8c45fSAndroid Build Coastguard Worker if ((mHotCacheSize + newEntrySize) > mHotCacheLimit) {
786*38e8c45fSAndroid Build Coastguard Worker ALOGV("HOTCACHE(ADD): mHotCacheSize (%zu) + newEntrySize (%zu) is to big for "
787*38e8c45fSAndroid Build Coastguard Worker "mHotCacheLimit "
788*38e8c45fSAndroid Build Coastguard Worker "(%zu), freeing up space for %u",
789*38e8c45fSAndroid Build Coastguard Worker mHotCacheSize, newEntrySize, mHotCacheLimit, newEntryHash);
790*38e8c45fSAndroid Build Coastguard Worker
791*38e8c45fSAndroid Build Coastguard Worker // Wait for all the files to complete writing so our hot cache is accurate
792*38e8c45fSAndroid Build Coastguard Worker ALOGV("HOTCACHE(ADD): Waiting for work to complete for %u", newEntryHash);
793*38e8c45fSAndroid Build Coastguard Worker waitForWorkComplete();
794*38e8c45fSAndroid Build Coastguard Worker
795*38e8c45fSAndroid Build Coastguard Worker // Free up old entries until under the limit
796*38e8c45fSAndroid Build Coastguard Worker for (auto hotCacheIter = mHotCache.begin(); hotCacheIter != mHotCache.end();) {
797*38e8c45fSAndroid Build Coastguard Worker uint32_t oldEntryHash = hotCacheIter->first;
798*38e8c45fSAndroid Build Coastguard Worker MultifileHotCache oldEntry = hotCacheIter->second;
799*38e8c45fSAndroid Build Coastguard Worker
800*38e8c45fSAndroid Build Coastguard Worker // Move our iterator before deleting the entry
801*38e8c45fSAndroid Build Coastguard Worker hotCacheIter++;
802*38e8c45fSAndroid Build Coastguard Worker if (!removeFromHotCache(oldEntryHash)) {
803*38e8c45fSAndroid Build Coastguard Worker ALOGE("HOTCACHE(ADD): Unable to remove entry %u", oldEntryHash);
804*38e8c45fSAndroid Build Coastguard Worker return false;
805*38e8c45fSAndroid Build Coastguard Worker }
806*38e8c45fSAndroid Build Coastguard Worker
807*38e8c45fSAndroid Build Coastguard Worker // Clear at least half the hot cache
808*38e8c45fSAndroid Build Coastguard Worker if ((mHotCacheSize + newEntrySize) <= mHotCacheLimit / 2) {
809*38e8c45fSAndroid Build Coastguard Worker ALOGV("HOTCACHE(ADD): Freed enough space for %zu", mHotCacheSize);
810*38e8c45fSAndroid Build Coastguard Worker break;
811*38e8c45fSAndroid Build Coastguard Worker }
812*38e8c45fSAndroid Build Coastguard Worker }
813*38e8c45fSAndroid Build Coastguard Worker }
814*38e8c45fSAndroid Build Coastguard Worker
815*38e8c45fSAndroid Build Coastguard Worker // Track it
816*38e8c45fSAndroid Build Coastguard Worker mHotCache[newEntryHash] = {newFd, newEntryBuffer, newEntrySize};
817*38e8c45fSAndroid Build Coastguard Worker mHotCacheSize += newEntrySize;
818*38e8c45fSAndroid Build Coastguard Worker
819*38e8c45fSAndroid Build Coastguard Worker ALOGV("HOTCACHE(ADD): New hot cache size: %zu", mHotCacheSize);
820*38e8c45fSAndroid Build Coastguard Worker
821*38e8c45fSAndroid Build Coastguard Worker return true;
822*38e8c45fSAndroid Build Coastguard Worker }
823*38e8c45fSAndroid Build Coastguard Worker
removeFromHotCache(uint32_t entryHash)824*38e8c45fSAndroid Build Coastguard Worker bool MultifileBlobCache::removeFromHotCache(uint32_t entryHash) {
825*38e8c45fSAndroid Build Coastguard Worker if (mHotCache.find(entryHash) != mHotCache.end()) {
826*38e8c45fSAndroid Build Coastguard Worker ALOGV("HOTCACHE(REMOVE): Removing %u from hot cache", entryHash);
827*38e8c45fSAndroid Build Coastguard Worker
828*38e8c45fSAndroid Build Coastguard Worker // Wait for all the files to complete writing so our hot cache is accurate
829*38e8c45fSAndroid Build Coastguard Worker ALOGV("HOTCACHE(REMOVE): Waiting for work to complete for %u", entryHash);
830*38e8c45fSAndroid Build Coastguard Worker waitForWorkComplete();
831*38e8c45fSAndroid Build Coastguard Worker
832*38e8c45fSAndroid Build Coastguard Worker ALOGV("HOTCACHE(REMOVE): Closing hot cache entry for %u", entryHash);
833*38e8c45fSAndroid Build Coastguard Worker MultifileHotCache entry = mHotCache[entryHash];
834*38e8c45fSAndroid Build Coastguard Worker freeHotCacheEntry(entry);
835*38e8c45fSAndroid Build Coastguard Worker
836*38e8c45fSAndroid Build Coastguard Worker // Delete the entry from our tracking
837*38e8c45fSAndroid Build Coastguard Worker mHotCacheSize -= entry.entrySize;
838*38e8c45fSAndroid Build Coastguard Worker mHotCache.erase(entryHash);
839*38e8c45fSAndroid Build Coastguard Worker
840*38e8c45fSAndroid Build Coastguard Worker return true;
841*38e8c45fSAndroid Build Coastguard Worker }
842*38e8c45fSAndroid Build Coastguard Worker
843*38e8c45fSAndroid Build Coastguard Worker return false;
844*38e8c45fSAndroid Build Coastguard Worker }
845*38e8c45fSAndroid Build Coastguard Worker
applyLRU(size_t cacheSizeLimit,size_t cacheEntryLimit)846*38e8c45fSAndroid Build Coastguard Worker bool MultifileBlobCache::applyLRU(size_t cacheSizeLimit, size_t cacheEntryLimit) {
847*38e8c45fSAndroid Build Coastguard Worker // Walk through our map of sorted last access times and remove files until under the limit
848*38e8c45fSAndroid Build Coastguard Worker for (auto cacheEntryIter = mEntryStats.begin(); cacheEntryIter != mEntryStats.end();) {
849*38e8c45fSAndroid Build Coastguard Worker #if COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
850*38e8c45fSAndroid Build Coastguard Worker const MultifileEntryStats& entryStats = cacheEntryIter->second;
851*38e8c45fSAndroid Build Coastguard Worker uint32_t entryHash = entryStats.entryHash;
852*38e8c45fSAndroid Build Coastguard Worker #else
853*38e8c45fSAndroid Build Coastguard Worker uint32_t entryHash = cacheEntryIter->first;
854*38e8c45fSAndroid Build Coastguard Worker const MultifileEntryStats& entryStats = cacheEntryIter->second;
855*38e8c45fSAndroid Build Coastguard Worker #endif // COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
856*38e8c45fSAndroid Build Coastguard Worker
857*38e8c45fSAndroid Build Coastguard Worker ALOGV("LRU: Removing entryHash %u", entryHash);
858*38e8c45fSAndroid Build Coastguard Worker
859*38e8c45fSAndroid Build Coastguard Worker // Remove it from hot cache if present
860*38e8c45fSAndroid Build Coastguard Worker removeFromHotCache(entryHash);
861*38e8c45fSAndroid Build Coastguard Worker
862*38e8c45fSAndroid Build Coastguard Worker // Remove it from the system
863*38e8c45fSAndroid Build Coastguard Worker std::string entryPath = mMultifileDirName + "/" + std::to_string(entryHash);
864*38e8c45fSAndroid Build Coastguard Worker if (remove(entryPath.c_str()) != 0) {
865*38e8c45fSAndroid Build Coastguard Worker // Continue evicting invalid item (app's cache might be cleared)
866*38e8c45fSAndroid Build Coastguard Worker ALOGW("LRU: Error removing %s: %s", entryPath.c_str(), std::strerror(errno));
867*38e8c45fSAndroid Build Coastguard Worker }
868*38e8c45fSAndroid Build Coastguard Worker
869*38e8c45fSAndroid Build Coastguard Worker // Increment the iterator before clearing the entry
870*38e8c45fSAndroid Build Coastguard Worker cacheEntryIter++;
871*38e8c45fSAndroid Build Coastguard Worker
872*38e8c45fSAndroid Build Coastguard Worker // Delete the entry from our tracking and update the overall cache size
873*38e8c45fSAndroid Build Coastguard Worker if (!removeEntry(entryHash)) {
874*38e8c45fSAndroid Build Coastguard Worker ALOGE("LRU: Failed to remove entryHash %u", entryHash);
875*38e8c45fSAndroid Build Coastguard Worker return false;
876*38e8c45fSAndroid Build Coastguard Worker }
877*38e8c45fSAndroid Build Coastguard Worker
878*38e8c45fSAndroid Build Coastguard Worker // See if it has been reduced enough
879*38e8c45fSAndroid Build Coastguard Worker size_t totalCacheSize = getTotalSize();
880*38e8c45fSAndroid Build Coastguard Worker size_t totalCacheEntries = getTotalEntries();
881*38e8c45fSAndroid Build Coastguard Worker if (totalCacheSize <= cacheSizeLimit && totalCacheEntries <= cacheEntryLimit) {
882*38e8c45fSAndroid Build Coastguard Worker // Success
883*38e8c45fSAndroid Build Coastguard Worker ALOGV("LRU: Reduced cache to size %zu entries %zu", totalCacheSize, totalCacheEntries);
884*38e8c45fSAndroid Build Coastguard Worker return true;
885*38e8c45fSAndroid Build Coastguard Worker }
886*38e8c45fSAndroid Build Coastguard Worker }
887*38e8c45fSAndroid Build Coastguard Worker
888*38e8c45fSAndroid Build Coastguard Worker ALOGV("LRU: Cache is empty");
889*38e8c45fSAndroid Build Coastguard Worker return false;
890*38e8c45fSAndroid Build Coastguard Worker }
891*38e8c45fSAndroid Build Coastguard Worker
892*38e8c45fSAndroid Build Coastguard Worker // Clear the cache by removing all entries and deleting the directory
clearCache()893*38e8c45fSAndroid Build Coastguard Worker bool MultifileBlobCache::clearCache() {
894*38e8c45fSAndroid Build Coastguard Worker DIR* dir;
895*38e8c45fSAndroid Build Coastguard Worker struct dirent* entry;
896*38e8c45fSAndroid Build Coastguard Worker dir = opendir(mMultifileDirName.c_str());
897*38e8c45fSAndroid Build Coastguard Worker if (dir == nullptr) {
898*38e8c45fSAndroid Build Coastguard Worker ALOGE("CLEAR: Unable to open multifile dir: %s", mMultifileDirName.c_str());
899*38e8c45fSAndroid Build Coastguard Worker return false;
900*38e8c45fSAndroid Build Coastguard Worker }
901*38e8c45fSAndroid Build Coastguard Worker
902*38e8c45fSAndroid Build Coastguard Worker // Delete all entries and the status file
903*38e8c45fSAndroid Build Coastguard Worker while ((entry = readdir(dir)) != nullptr) {
904*38e8c45fSAndroid Build Coastguard Worker if (entry->d_name == "."s || entry->d_name == ".."s) {
905*38e8c45fSAndroid Build Coastguard Worker continue;
906*38e8c45fSAndroid Build Coastguard Worker }
907*38e8c45fSAndroid Build Coastguard Worker
908*38e8c45fSAndroid Build Coastguard Worker std::string entryName = entry->d_name;
909*38e8c45fSAndroid Build Coastguard Worker std::string fullPath = mMultifileDirName + "/" + entryName;
910*38e8c45fSAndroid Build Coastguard Worker if (remove(fullPath.c_str()) != 0) {
911*38e8c45fSAndroid Build Coastguard Worker ALOGE("CLEAR: Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
912*38e8c45fSAndroid Build Coastguard Worker return false;
913*38e8c45fSAndroid Build Coastguard Worker }
914*38e8c45fSAndroid Build Coastguard Worker }
915*38e8c45fSAndroid Build Coastguard Worker
916*38e8c45fSAndroid Build Coastguard Worker // Delete the directory
917*38e8c45fSAndroid Build Coastguard Worker if (remove(mMultifileDirName.c_str()) != 0) {
918*38e8c45fSAndroid Build Coastguard Worker ALOGE("CLEAR: Error removing %s: %s", mMultifileDirName.c_str(), std::strerror(errno));
919*38e8c45fSAndroid Build Coastguard Worker return false;
920*38e8c45fSAndroid Build Coastguard Worker }
921*38e8c45fSAndroid Build Coastguard Worker
922*38e8c45fSAndroid Build Coastguard Worker ALOGV("CLEAR: Cleared the multifile blobcache");
923*38e8c45fSAndroid Build Coastguard Worker return true;
924*38e8c45fSAndroid Build Coastguard Worker }
925*38e8c45fSAndroid Build Coastguard Worker
926*38e8c45fSAndroid Build Coastguard Worker // Calculate the cache size and remove old entries until under the limit
trimCache()927*38e8c45fSAndroid Build Coastguard Worker void MultifileBlobCache::trimCache() {
928*38e8c45fSAndroid Build Coastguard Worker // Wait for all deferred writes to complete
929*38e8c45fSAndroid Build Coastguard Worker ALOGV("TRIM: Waiting for work to complete.");
930*38e8c45fSAndroid Build Coastguard Worker waitForWorkComplete();
931*38e8c45fSAndroid Build Coastguard Worker
932*38e8c45fSAndroid Build Coastguard Worker ALOGV("TRIM: Reducing multifile cache size to %zu, entries %zu",
933*38e8c45fSAndroid Build Coastguard Worker mMaxTotalSize / mTotalCacheSizeDivisor, mMaxTotalEntries / mTotalCacheSizeDivisor);
934*38e8c45fSAndroid Build Coastguard Worker
935*38e8c45fSAndroid Build Coastguard Worker if (!applyLRU(mMaxTotalSize / mTotalCacheSizeDivisor,
936*38e8c45fSAndroid Build Coastguard Worker mMaxTotalEntries / mTotalCacheSizeDivisor)) {
937*38e8c45fSAndroid Build Coastguard Worker ALOGE("Error when clearing multifile shader cache");
938*38e8c45fSAndroid Build Coastguard Worker return;
939*38e8c45fSAndroid Build Coastguard Worker }
940*38e8c45fSAndroid Build Coastguard Worker }
941*38e8c45fSAndroid Build Coastguard Worker
942*38e8c45fSAndroid Build Coastguard Worker // This function performs a task. It only knows how to write files to disk,
943*38e8c45fSAndroid Build Coastguard Worker // but it could be expanded if needed.
processTask(DeferredTask & task)944*38e8c45fSAndroid Build Coastguard Worker void MultifileBlobCache::processTask(DeferredTask& task) {
945*38e8c45fSAndroid Build Coastguard Worker switch (task.getTaskCommand()) {
946*38e8c45fSAndroid Build Coastguard Worker case TaskCommand::Exit: {
947*38e8c45fSAndroid Build Coastguard Worker ALOGV("DEFERRED: Shutting down");
948*38e8c45fSAndroid Build Coastguard Worker return;
949*38e8c45fSAndroid Build Coastguard Worker }
950*38e8c45fSAndroid Build Coastguard Worker case TaskCommand::WriteToDisk: {
951*38e8c45fSAndroid Build Coastguard Worker uint32_t entryHash = task.getEntryHash();
952*38e8c45fSAndroid Build Coastguard Worker std::string& fullPath = task.getFullPath();
953*38e8c45fSAndroid Build Coastguard Worker uint8_t* buffer = task.getBuffer();
954*38e8c45fSAndroid Build Coastguard Worker size_t bufferSize = task.getBufferSize();
955*38e8c45fSAndroid Build Coastguard Worker
956*38e8c45fSAndroid Build Coastguard Worker // Create the file or reset it if already present, read+write for user only
957*38e8c45fSAndroid Build Coastguard Worker int fd = open(fullPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
958*38e8c45fSAndroid Build Coastguard Worker if (fd == -1) {
959*38e8c45fSAndroid Build Coastguard Worker if (flags::multifile_blobcache_advanced_usage()) {
960*38e8c45fSAndroid Build Coastguard Worker struct stat st;
961*38e8c45fSAndroid Build Coastguard Worker if (stat(mMultifileDirName.c_str(), &st) == -1) {
962*38e8c45fSAndroid Build Coastguard Worker ALOGW("Cache directory missing (app's cache cleared?). Recreating...");
963*38e8c45fSAndroid Build Coastguard Worker
964*38e8c45fSAndroid Build Coastguard Worker // Restore the multifile directory
965*38e8c45fSAndroid Build Coastguard Worker if (mkdir(mMultifileDirName.c_str(), 0755) != 0 && (errno != EEXIST)) {
966*38e8c45fSAndroid Build Coastguard Worker ALOGE("Cache error in SET - Unable to create directory (%s), errno "
967*38e8c45fSAndroid Build Coastguard Worker "(%i)",
968*38e8c45fSAndroid Build Coastguard Worker mMultifileDirName.c_str(), errno);
969*38e8c45fSAndroid Build Coastguard Worker return;
970*38e8c45fSAndroid Build Coastguard Worker }
971*38e8c45fSAndroid Build Coastguard Worker
972*38e8c45fSAndroid Build Coastguard Worker // Create new status file
973*38e8c45fSAndroid Build Coastguard Worker if (!createStatus(mMultifileDirName.c_str())) {
974*38e8c45fSAndroid Build Coastguard Worker ALOGE("Cache error in SET - Failed to create status file!");
975*38e8c45fSAndroid Build Coastguard Worker return;
976*38e8c45fSAndroid Build Coastguard Worker }
977*38e8c45fSAndroid Build Coastguard Worker
978*38e8c45fSAndroid Build Coastguard Worker // Try to open the file again
979*38e8c45fSAndroid Build Coastguard Worker fd = open(fullPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC,
980*38e8c45fSAndroid Build Coastguard Worker S_IRUSR | S_IWUSR);
981*38e8c45fSAndroid Build Coastguard Worker }
982*38e8c45fSAndroid Build Coastguard Worker }
983*38e8c45fSAndroid Build Coastguard Worker
984*38e8c45fSAndroid Build Coastguard Worker if (fd == -1) {
985*38e8c45fSAndroid Build Coastguard Worker ALOGE("Cache error in SET - failed to open fullPath: %s, error: %s",
986*38e8c45fSAndroid Build Coastguard Worker fullPath.c_str(), std::strerror(errno));
987*38e8c45fSAndroid Build Coastguard Worker return;
988*38e8c45fSAndroid Build Coastguard Worker }
989*38e8c45fSAndroid Build Coastguard Worker }
990*38e8c45fSAndroid Build Coastguard Worker
991*38e8c45fSAndroid Build Coastguard Worker ALOGV("DEFERRED: Opened fd %i from %s", fd, fullPath.c_str());
992*38e8c45fSAndroid Build Coastguard Worker
993*38e8c45fSAndroid Build Coastguard Worker // Add CRC check to the header (always do this last!)
994*38e8c45fSAndroid Build Coastguard Worker MultifileHeader* header = reinterpret_cast<MultifileHeader*>(buffer);
995*38e8c45fSAndroid Build Coastguard Worker header->crc = GenerateCRC32(buffer + sizeof(MultifileHeader),
996*38e8c45fSAndroid Build Coastguard Worker bufferSize - sizeof(MultifileHeader));
997*38e8c45fSAndroid Build Coastguard Worker
998*38e8c45fSAndroid Build Coastguard Worker ssize_t result = write(fd, buffer, bufferSize);
999*38e8c45fSAndroid Build Coastguard Worker if (result != bufferSize) {
1000*38e8c45fSAndroid Build Coastguard Worker ALOGE("Error writing fileSize to cache entry (%s): %s", fullPath.c_str(),
1001*38e8c45fSAndroid Build Coastguard Worker std::strerror(errno));
1002*38e8c45fSAndroid Build Coastguard Worker return;
1003*38e8c45fSAndroid Build Coastguard Worker }
1004*38e8c45fSAndroid Build Coastguard Worker
1005*38e8c45fSAndroid Build Coastguard Worker if (flags::multifile_blobcache_advanced_usage()) {
1006*38e8c45fSAndroid Build Coastguard Worker // Update last access time and last modify time
1007*38e8c45fSAndroid Build Coastguard Worker struct timespec times[2];
1008*38e8c45fSAndroid Build Coastguard Worker times[0].tv_nsec = UTIME_NOW;
1009*38e8c45fSAndroid Build Coastguard Worker times[1].tv_nsec = UTIME_NOW;
1010*38e8c45fSAndroid Build Coastguard Worker futimens(fd, times);
1011*38e8c45fSAndroid Build Coastguard Worker }
1012*38e8c45fSAndroid Build Coastguard Worker
1013*38e8c45fSAndroid Build Coastguard Worker ALOGV("DEFERRED: Completed write for: %s", fullPath.c_str());
1014*38e8c45fSAndroid Build Coastguard Worker close(fd);
1015*38e8c45fSAndroid Build Coastguard Worker
1016*38e8c45fSAndroid Build Coastguard Worker // Erase the entry from mDeferredWrites
1017*38e8c45fSAndroid Build Coastguard Worker // Since there could be multiple outstanding writes for an entry, find the matching one
1018*38e8c45fSAndroid Build Coastguard Worker {
1019*38e8c45fSAndroid Build Coastguard Worker // Synchronize access to deferred write status
1020*38e8c45fSAndroid Build Coastguard Worker std::lock_guard<std::mutex> lock(mDeferredWriteStatusMutex);
1021*38e8c45fSAndroid Build Coastguard Worker typedef std::multimap<uint32_t, uint8_t*>::iterator entryIter;
1022*38e8c45fSAndroid Build Coastguard Worker std::pair<entryIter, entryIter> iterPair = mDeferredWrites.equal_range(entryHash);
1023*38e8c45fSAndroid Build Coastguard Worker for (entryIter it = iterPair.first; it != iterPair.second; ++it) {
1024*38e8c45fSAndroid Build Coastguard Worker if (it->second == buffer) {
1025*38e8c45fSAndroid Build Coastguard Worker ALOGV("DEFERRED: Marking write complete for %u at %p", it->first,
1026*38e8c45fSAndroid Build Coastguard Worker it->second);
1027*38e8c45fSAndroid Build Coastguard Worker mDeferredWrites.erase(it);
1028*38e8c45fSAndroid Build Coastguard Worker break;
1029*38e8c45fSAndroid Build Coastguard Worker }
1030*38e8c45fSAndroid Build Coastguard Worker }
1031*38e8c45fSAndroid Build Coastguard Worker }
1032*38e8c45fSAndroid Build Coastguard Worker
1033*38e8c45fSAndroid Build Coastguard Worker return;
1034*38e8c45fSAndroid Build Coastguard Worker }
1035*38e8c45fSAndroid Build Coastguard Worker default: {
1036*38e8c45fSAndroid Build Coastguard Worker ALOGE("DEFERRED: Unhandled task type");
1037*38e8c45fSAndroid Build Coastguard Worker return;
1038*38e8c45fSAndroid Build Coastguard Worker }
1039*38e8c45fSAndroid Build Coastguard Worker }
1040*38e8c45fSAndroid Build Coastguard Worker }
1041*38e8c45fSAndroid Build Coastguard Worker
1042*38e8c45fSAndroid Build Coastguard Worker // This function will wait until tasks arrive, then execute them
1043*38e8c45fSAndroid Build Coastguard Worker // If the exit command is submitted, the loop will terminate
processTasksImpl(bool * exitThread)1044*38e8c45fSAndroid Build Coastguard Worker void MultifileBlobCache::processTasksImpl(bool* exitThread) {
1045*38e8c45fSAndroid Build Coastguard Worker while (true) {
1046*38e8c45fSAndroid Build Coastguard Worker std::unique_lock<std::mutex> lock(mWorkerMutex);
1047*38e8c45fSAndroid Build Coastguard Worker if (mTasks.empty()) {
1048*38e8c45fSAndroid Build Coastguard Worker ALOGV("WORKER: No tasks available, waiting");
1049*38e8c45fSAndroid Build Coastguard Worker mWorkerThreadIdle = true;
1050*38e8c45fSAndroid Build Coastguard Worker mWorkerIdleCondition.notify_all();
1051*38e8c45fSAndroid Build Coastguard Worker // Only wake if notified and command queue is not empty
1052*38e8c45fSAndroid Build Coastguard Worker mWorkAvailableCondition.wait(lock, [this] { return !mTasks.empty(); });
1053*38e8c45fSAndroid Build Coastguard Worker }
1054*38e8c45fSAndroid Build Coastguard Worker
1055*38e8c45fSAndroid Build Coastguard Worker ALOGV("WORKER: Task available, waking up.");
1056*38e8c45fSAndroid Build Coastguard Worker mWorkerThreadIdle = false;
1057*38e8c45fSAndroid Build Coastguard Worker DeferredTask task = std::move(mTasks.front());
1058*38e8c45fSAndroid Build Coastguard Worker mTasks.pop();
1059*38e8c45fSAndroid Build Coastguard Worker
1060*38e8c45fSAndroid Build Coastguard Worker if (task.getTaskCommand() == TaskCommand::Exit) {
1061*38e8c45fSAndroid Build Coastguard Worker ALOGV("WORKER: Exiting work loop.");
1062*38e8c45fSAndroid Build Coastguard Worker *exitThread = true;
1063*38e8c45fSAndroid Build Coastguard Worker mWorkerThreadIdle = true;
1064*38e8c45fSAndroid Build Coastguard Worker mWorkerIdleCondition.notify_one();
1065*38e8c45fSAndroid Build Coastguard Worker return;
1066*38e8c45fSAndroid Build Coastguard Worker }
1067*38e8c45fSAndroid Build Coastguard Worker
1068*38e8c45fSAndroid Build Coastguard Worker lock.unlock();
1069*38e8c45fSAndroid Build Coastguard Worker processTask(task);
1070*38e8c45fSAndroid Build Coastguard Worker }
1071*38e8c45fSAndroid Build Coastguard Worker }
1072*38e8c45fSAndroid Build Coastguard Worker
1073*38e8c45fSAndroid Build Coastguard Worker // Process tasks until the exit task is submitted
processTasks()1074*38e8c45fSAndroid Build Coastguard Worker void MultifileBlobCache::processTasks() {
1075*38e8c45fSAndroid Build Coastguard Worker while (true) {
1076*38e8c45fSAndroid Build Coastguard Worker bool exitThread = false;
1077*38e8c45fSAndroid Build Coastguard Worker processTasksImpl(&exitThread);
1078*38e8c45fSAndroid Build Coastguard Worker if (exitThread) {
1079*38e8c45fSAndroid Build Coastguard Worker break;
1080*38e8c45fSAndroid Build Coastguard Worker }
1081*38e8c45fSAndroid Build Coastguard Worker }
1082*38e8c45fSAndroid Build Coastguard Worker }
1083*38e8c45fSAndroid Build Coastguard Worker
1084*38e8c45fSAndroid Build Coastguard Worker // Add a task to the queue to be processed by the worker thread
queueTask(DeferredTask && task)1085*38e8c45fSAndroid Build Coastguard Worker void MultifileBlobCache::queueTask(DeferredTask&& task) {
1086*38e8c45fSAndroid Build Coastguard Worker std::lock_guard<std::mutex> queueLock(mWorkerMutex);
1087*38e8c45fSAndroid Build Coastguard Worker mTasks.emplace(std::move(task));
1088*38e8c45fSAndroid Build Coastguard Worker mWorkAvailableCondition.notify_one();
1089*38e8c45fSAndroid Build Coastguard Worker }
1090*38e8c45fSAndroid Build Coastguard Worker
1091*38e8c45fSAndroid Build Coastguard Worker // Wait until all tasks have been completed
waitForWorkComplete()1092*38e8c45fSAndroid Build Coastguard Worker void MultifileBlobCache::waitForWorkComplete() {
1093*38e8c45fSAndroid Build Coastguard Worker std::unique_lock<std::mutex> lock(mWorkerMutex);
1094*38e8c45fSAndroid Build Coastguard Worker mWorkerIdleCondition.wait(lock, [this] { return (mTasks.empty() && mWorkerThreadIdle); });
1095*38e8c45fSAndroid Build Coastguard Worker }
1096*38e8c45fSAndroid Build Coastguard Worker
1097*38e8c45fSAndroid Build Coastguard Worker }; // namespace android
1098