EGL BlobCache: Synchronize access to deferred write status

Protect access to mDeferredWrites with a mutex. It is updated
by the worker thread upon completion.

Test: pubg_mobile_launch ANGLE trace
Test: /data/nativetest64/EGL_test/EGL_test
Test: /data/nativetest64/libEGL_test/libEGL_test
Bug: b/271975248
Change-Id: Id278f4677a72f2d981a9400ee871bdcc1b0168ef
diff --git a/opengl/libs/EGL/MultifileBlobCache.cpp b/opengl/libs/EGL/MultifileBlobCache.cpp
index 607e214..f1b7a5c 100644
--- a/opengl/libs/EGL/MultifileBlobCache.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache.cpp
@@ -298,13 +298,17 @@
 
     // Sending -1 as the fd indicates we don't have an fd for this
     if (!addToHotCache(entryHash, -1, buffer, fileSize)) {
-        ALOGE("GET: Failed to add %u to hot cache", entryHash);
+        ALOGE("SET: Failed to add %u to hot cache", entryHash);
         return;
     }
 
     // Track that we're creating a pending write for this entry
     // Include the buffer to handle the case when multiple writes are pending for an entry
-    mDeferredWrites.insert(std::make_pair(entryHash, buffer));
+    {
+        // Synchronize access to deferred write status
+        std::lock_guard<std::mutex> lock(mDeferredWriteStatusMutex);
+        mDeferredWrites.insert(std::make_pair(entryHash, buffer));
+    }
 
     // Create deferred task to write to storage
     ALOGV("SET: Adding task to queue.");
@@ -371,8 +375,15 @@
     } else {
         ALOGV("GET: HotCache MISS for entry: %u", entryHash);
 
-        if (mDeferredWrites.find(entryHash) != mDeferredWrites.end()) {
-            // Wait for writes to complete if there is an outstanding write for this entry
+        // Wait for writes to complete if there is an outstanding write for this entry
+        bool wait = false;
+        {
+            // Synchronize access to deferred write status
+            std::lock_guard<std::mutex> lock(mDeferredWriteStatusMutex);
+            wait = mDeferredWrites.find(entryHash) != mDeferredWrites.end();
+        }
+
+        if (wait) {
             ALOGV("GET: Waiting for write to complete for %u", entryHash);
             waitForWorkComplete();
         }
@@ -484,6 +495,7 @@
               mHotCacheSize, newEntrySize, mHotCacheLimit, newEntryHash);
 
         // Wait for all the files to complete writing so our hot cache is accurate
+        ALOGV("HOTCACHE(ADD): Waiting for work to complete for %u", newEntryHash);
         waitForWorkComplete();
 
         // Free up old entries until under the limit
@@ -520,6 +532,7 @@
         ALOGV("HOTCACHE(REMOVE): Removing %u from hot cache", entryHash);
 
         // Wait for all the files to complete writing so our hot cache is accurate
+        ALOGV("HOTCACHE(REMOVE): Waiting for work to complete for %u", entryHash);
         waitForWorkComplete();
 
         ALOGV("HOTCACHE(REMOVE): Closing hot cache entry for %u", entryHash);
@@ -590,6 +603,7 @@
     size_t limit = cacheByteLimit;
 
     // Wait for all deferred writes to complete
+    ALOGV("TRIM: Waiting for work to complete.");
     waitForWorkComplete();
 
     size_t size = getTotalSize();
@@ -646,13 +660,18 @@
 
             // Erase the entry from mDeferredWrites
             // Since there could be multiple outstanding writes for an entry, find the matching one
-            typedef std::multimap<uint32_t, uint8_t*>::iterator entryIter;
-            std::pair<entryIter, entryIter> iterPair = mDeferredWrites.equal_range(entryHash);
-            for (entryIter it = iterPair.first; it != iterPair.second; ++it) {
-                if (it->second == buffer) {
-                    ALOGV("DEFERRED: Marking write complete for %u at %p", it->first, it->second);
-                    mDeferredWrites.erase(it);
-                    break;
+            {
+                // Synchronize access to deferred write status
+                std::lock_guard<std::mutex> lock(mDeferredWriteStatusMutex);
+                typedef std::multimap<uint32_t, uint8_t*>::iterator entryIter;
+                std::pair<entryIter, entryIter> iterPair = mDeferredWrites.equal_range(entryHash);
+                for (entryIter it = iterPair.first; it != iterPair.second; ++it) {
+                    if (it->second == buffer) {
+                        ALOGV("DEFERRED: Marking write complete for %u at %p", it->first,
+                              it->second);
+                        mDeferredWrites.erase(it);
+                        break;
+                    }
                 }
             }
 
diff --git a/opengl/libs/EGL/MultifileBlobCache.h b/opengl/libs/EGL/MultifileBlobCache.h
index cb105fb..b461ef4 100644
--- a/opengl/libs/EGL/MultifileBlobCache.h
+++ b/opengl/libs/EGL/MultifileBlobCache.h
@@ -20,6 +20,7 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
+#include <android-base/thread_annotations.h>
 #include <future>
 #include <map>
 #include <queue>
@@ -139,7 +140,8 @@
     // Below are the components used for deferred writes
 
     // Track whether we have pending writes for an entry
-    std::multimap<uint32_t, uint8_t*> mDeferredWrites;
+    std::mutex mDeferredWriteStatusMutex;
+    std::multimap<uint32_t, uint8_t*> mDeferredWrites GUARDED_BY(mDeferredWriteStatusMutex);
 
     // Functions to work through tasks in the queue
     void processTasks();
diff --git a/opengl/libs/EGL/MultifileBlobCache_test.cpp b/opengl/libs/EGL/MultifileBlobCache_test.cpp
index 1a55a4f..670e113 100644
--- a/opengl/libs/EGL/MultifileBlobCache_test.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache_test.cpp
@@ -190,6 +190,26 @@
     }
 }
 
+TEST_F(MultifileBlobCacheTest, CacheMaxKeyAndValueSizeSucceeds) {
+    char key[kMaxKeySize];
+    for (int i = 0; i < kMaxKeySize; i++) {
+        key[i] = 'a';
+    }
+    char buf[kMaxValueSize];
+    for (int i = 0; i < kMaxValueSize; i++) {
+        buf[i] = 'b';
+    }
+    mMBC->set(key, kMaxKeySize, buf, kMaxValueSize);
+    for (int i = 0; i < kMaxValueSize; i++) {
+        buf[i] = 0xee;
+    }
+    mMBC->get(key, kMaxKeySize, buf, kMaxValueSize);
+    for (int i = 0; i < kMaxValueSize; i++) {
+        SCOPED_TRACE(i);
+        ASSERT_EQ('b', buf[i]);
+    }
+}
+
 TEST_F(MultifileBlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
     unsigned char buf[1] = {0xee};
     mMBC->set("x", 1, "y", 1);