xref: /aosp_15_r20/frameworks/native/opengl/libs/EGL/MultifileBlobCache.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
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