Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit be16373f authored by Cody Northrop's avatar Cody Northrop
Browse files

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
parent 999db233
Loading
Loading
Loading
Loading
+30 −11
Original line number Diff line number Diff line
@@ -298,13 +298,17 @@ void MultifileBlobCache::set(const void* key, EGLsizeiANDROID keySize, const voi

    // 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
    {
        // 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 @@ EGLsizeiANDROID MultifileBlobCache::get(const void* key, EGLsizeiANDROID keySize
    } 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
        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 @@ bool MultifileBlobCache::addToHotCache(uint32_t newEntryHash, int newFd, uint8_t
              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 @@ bool MultifileBlobCache::removeFromHotCache(uint32_t entryHash) {
        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 @@ void MultifileBlobCache::trimCache(size_t cacheByteLimit) {
    size_t limit = cacheByteLimit;

    // Wait for all deferred writes to complete
    ALOGV("TRIM: Waiting for work to complete.");
    waitForWorkComplete();

    size_t size = getTotalSize();
@@ -646,15 +660,20 @@ void MultifileBlobCache::processTask(DeferredTask& task) {

            // Erase the entry from mDeferredWrites
            // Since there could be multiple outstanding writes for an entry, find the matching one
            {
                // 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);
                        ALOGV("DEFERRED: Marking write complete for %u at %p", it->first,
                              it->second);
                        mDeferredWrites.erase(it);
                        break;
                    }
                }
            }

            return;
        }
+3 −1
Original line number Diff line number Diff line
@@ -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 @@ private:
    // 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();
+20 −0
Original line number Diff line number Diff line
@@ -190,6 +190,26 @@ TEST_F(MultifileBlobCacheTest, CacheMaxValueSizeSucceeds) {
    }
}

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);