tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

BlobCache.cpp (8615B)


      1 //
      2 // Copyright 2018 The ANGLE Project Authors. All rights reserved.
      3 // Use of this source code is governed by a BSD-style license that can be
      4 // found in the LICENSE file.
      5 //
      6 // BlobCache: Stores keyed blobs in memory to support EGL_ANDROID_blob_cache.
      7 // Can be used in conjunction with the platform layer to warm up the cache from
      8 // disk.  MemoryProgramCache uses this to handle caching of compiled programs.
      9 
     10 #include "libANGLE/BlobCache.h"
     11 #include "common/utilities.h"
     12 #include "libANGLE/Context.h"
     13 #include "libANGLE/Display.h"
     14 #include "libANGLE/histogram_macros.h"
     15 #include "platform/PlatformMethods.h"
     16 
     17 #define USE_SYSTEM_ZLIB
     18 #include "compression_utils_portable.h"
     19 
     20 namespace egl
     21 {
     22 
     23 namespace
     24 {
     25 enum CacheResult
     26 {
     27    kCacheMiss,
     28    kCacheHitMemory,
     29    kCacheHitDisk,
     30    kCacheResultMax,
     31 };
     32 
     33 }  // anonymous namespace
     34 
     35 // In oder to store more cache in blob cache, compress cacheData to compressedData
     36 // before being stored.
     37 bool CompressBlobCacheData(const size_t cacheSize,
     38                           const uint8_t *cacheData,
     39                           angle::MemoryBuffer *compressedData)
     40 {
     41    uLong uncompressedSize       = static_cast<uLong>(cacheSize);
     42    uLong expectedCompressedSize = zlib_internal::GzipExpectedCompressedSize(uncompressedSize);
     43 
     44    // Allocate memory.
     45    if (!compressedData->resize(expectedCompressedSize))
     46    {
     47        ERR() << "Failed to allocate memory for compression";
     48        return false;
     49    }
     50 
     51    int zResult = zlib_internal::GzipCompressHelper(compressedData->data(), &expectedCompressedSize,
     52                                                    cacheData, uncompressedSize, nullptr, nullptr);
     53 
     54    if (zResult != Z_OK)
     55    {
     56        ERR() << "Failed to compress cache data: " << zResult;
     57        return false;
     58    }
     59 
     60    // Resize it to expected size.
     61    if (!compressedData->resize(expectedCompressedSize))
     62    {
     63        return false;
     64    }
     65 
     66    return true;
     67 }
     68 
     69 bool DecompressBlobCacheData(const uint8_t *compressedData,
     70                             const size_t compressedSize,
     71                             angle::MemoryBuffer *uncompressedData)
     72 {
     73    // Call zlib function to decompress.
     74    uint32_t uncompressedSize =
     75        zlib_internal::GetGzipUncompressedSize(compressedData, compressedSize);
     76 
     77    // Allocate enough memory.
     78    if (!uncompressedData->resize(uncompressedSize))
     79    {
     80        ERR() << "Failed to allocate memory for decompression";
     81        return false;
     82    }
     83 
     84    uLong destLen = uncompressedSize;
     85    int zResult   = zlib_internal::GzipUncompressHelper(
     86          uncompressedData->data(), &destLen, compressedData, static_cast<uLong>(compressedSize));
     87 
     88    if (zResult != Z_OK)
     89    {
     90        ERR() << "Failed to decompress data: " << zResult << "\n";
     91        return false;
     92    }
     93 
     94    // Resize it to expected size.
     95    if (!uncompressedData->resize(destLen))
     96    {
     97        return false;
     98    }
     99 
    100    return true;
    101 }
    102 
    103 BlobCache::BlobCache(size_t maxCacheSizeBytes)
    104    : mBlobCache(maxCacheSizeBytes), mSetBlobFunc(nullptr), mGetBlobFunc(nullptr)
    105 {}
    106 
    107 BlobCache::~BlobCache() {}
    108 
    109 void BlobCache::put(const BlobCache::Key &key, angle::MemoryBuffer &&value)
    110 {
    111    if (areBlobCacheFuncsSet())
    112    {
    113        std::scoped_lock<std::mutex> lock(mBlobCacheMutex);
    114        // Store the result in the application's cache
    115        mSetBlobFunc(key.data(), key.size(), value.data(), value.size());
    116    }
    117    else
    118    {
    119        populate(key, std::move(value), CacheSource::Memory);
    120    }
    121 }
    122 
    123 bool BlobCache::compressAndPut(const BlobCache::Key &key,
    124                               angle::MemoryBuffer &&uncompressedValue,
    125                               size_t *compressedSize)
    126 {
    127    angle::MemoryBuffer compressedValue;
    128    if (!CompressBlobCacheData(uncompressedValue.size(), uncompressedValue.data(),
    129                               &compressedValue))
    130    {
    131        return false;
    132    }
    133    if (compressedSize != nullptr)
    134        *compressedSize = compressedValue.size();
    135    put(key, std::move(compressedValue));
    136    return true;
    137 }
    138 
    139 void BlobCache::putApplication(const BlobCache::Key &key, const angle::MemoryBuffer &value)
    140 {
    141    if (areBlobCacheFuncsSet())
    142    {
    143        std::scoped_lock<std::mutex> lock(mBlobCacheMutex);
    144        mSetBlobFunc(key.data(), key.size(), value.data(), value.size());
    145    }
    146 }
    147 
    148 void BlobCache::populate(const BlobCache::Key &key, angle::MemoryBuffer &&value, CacheSource source)
    149 {
    150    std::scoped_lock<std::mutex> lock(mBlobCacheMutex);
    151    CacheEntry newEntry;
    152    newEntry.first  = std::move(value);
    153    newEntry.second = source;
    154 
    155    // Cache it inside blob cache only if caching inside the application is not possible.
    156    mBlobCache.put(key, std::move(newEntry), newEntry.first.size());
    157 }
    158 
    159 bool BlobCache::get(angle::ScratchBuffer *scratchBuffer,
    160                    const BlobCache::Key &key,
    161                    BlobCache::Value *valueOut,
    162                    size_t *bufferSizeOut)
    163 {
    164    // Look into the application's cache, if there is such a cache
    165    if (areBlobCacheFuncsSet())
    166    {
    167        std::scoped_lock<std::mutex> lock(mBlobCacheMutex);
    168        EGLsizeiANDROID valueSize = mGetBlobFunc(key.data(), key.size(), nullptr, 0);
    169        if (valueSize <= 0)
    170        {
    171            return false;
    172        }
    173 
    174        angle::MemoryBuffer *scratchMemory;
    175        bool result = scratchBuffer->get(valueSize, &scratchMemory);
    176        if (!result)
    177        {
    178            ERR() << "Failed to allocate memory for binary blob";
    179            return false;
    180        }
    181 
    182        EGLsizeiANDROID originalValueSize = valueSize;
    183        valueSize = mGetBlobFunc(key.data(), key.size(), scratchMemory->data(), valueSize);
    184 
    185        // Make sure the key/value pair still exists/is unchanged after the second call
    186        // (modifications to the application cache by another thread are a possibility)
    187        if (valueSize != originalValueSize)
    188        {
    189            // This warning serves to find issues with the application cache, none of which are
    190            // currently known to be thread-safe.  If such a use ever arises, this WARN can be
    191            // removed.
    192            WARN() << "Binary blob no longer available in cache (removed by a thread?)";
    193            return false;
    194        }
    195 
    196        *valueOut      = BlobCache::Value(scratchMemory->data(), scratchMemory->size());
    197        *bufferSizeOut = valueSize;
    198        return true;
    199    }
    200 
    201    std::scoped_lock<std::mutex> lock(mBlobCacheMutex);
    202    // Otherwise we are doing caching internally, so try to find it there
    203    const CacheEntry *entry;
    204    bool result = mBlobCache.get(key, &entry);
    205 
    206    if (result)
    207    {
    208 
    209        *valueOut      = BlobCache::Value(entry->first.data(), entry->first.size());
    210        *bufferSizeOut = entry->first.size();
    211    }
    212 
    213    return result;
    214 }
    215 
    216 bool BlobCache::getAt(size_t index, const BlobCache::Key **keyOut, BlobCache::Value *valueOut)
    217 {
    218    std::scoped_lock<std::mutex> lock(mBlobCacheMutex);
    219    const CacheEntry *valueBuf;
    220    bool result = mBlobCache.getAt(index, keyOut, &valueBuf);
    221    if (result)
    222    {
    223        *valueOut = BlobCache::Value(valueBuf->first.data(), valueBuf->first.size());
    224    }
    225    return result;
    226 }
    227 
    228 BlobCache::GetAndDecompressResult BlobCache::getAndDecompress(
    229    angle::ScratchBuffer *scratchBuffer,
    230    const BlobCache::Key &key,
    231    angle::MemoryBuffer *uncompressedValueOut)
    232 {
    233    ASSERT(uncompressedValueOut);
    234 
    235    Value compressedValue;
    236    size_t compressedSize;
    237    if (!get(scratchBuffer, key, &compressedValue, &compressedSize))
    238    {
    239        return GetAndDecompressResult::NotFound;
    240    }
    241 
    242    {
    243        // This needs to be locked because `DecompressBlobCacheData` is reading shared memory from
    244        // `compressedValue.data()`.
    245        std::scoped_lock<std::mutex> lock(mBlobCacheMutex);
    246        if (!DecompressBlobCacheData(compressedValue.data(), compressedSize, uncompressedValueOut))
    247        {
    248            return GetAndDecompressResult::DecompressFailure;
    249        }
    250    }
    251 
    252    return GetAndDecompressResult::GetSuccess;
    253 }
    254 
    255 void BlobCache::remove(const BlobCache::Key &key)
    256 {
    257    std::scoped_lock<std::mutex> lock(mBlobCacheMutex);
    258    mBlobCache.eraseByKey(key);
    259 }
    260 
    261 void BlobCache::setBlobCacheFuncs(EGLSetBlobFuncANDROID set, EGLGetBlobFuncANDROID get)
    262 {
    263    std::scoped_lock<std::mutex> lock(mBlobCacheMutex);
    264    mSetBlobFunc = set;
    265    mGetBlobFunc = get;
    266 }
    267 
    268 bool BlobCache::areBlobCacheFuncsSet() const
    269 {
    270    std::scoped_lock<std::mutex> lock(mBlobCacheMutex);
    271    // Either none or both of the callbacks should be set.
    272    ASSERT((mSetBlobFunc != nullptr) == (mGetBlobFunc != nullptr));
    273 
    274    return mSetBlobFunc != nullptr && mGetBlobFunc != nullptr;
    275 }
    276 
    277 }  // namespace egl