1/* 2 * Copyright (C) 2012 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "platform/graphics/ImageDecodingStore.h" 28 29#include "platform/TraceEvent.h" 30#include "wtf/Threading.h" 31 32namespace blink { 33 34namespace { 35 36static const size_t defaultMaxTotalSizeOfHeapEntries = 32 * 1024 * 1024; 37 38} // namespace 39 40ImageDecodingStore::ImageDecodingStore() 41 : m_heapLimitInBytes(defaultMaxTotalSizeOfHeapEntries) 42 , m_heapMemoryUsageInBytes(0) 43{ 44} 45 46ImageDecodingStore::~ImageDecodingStore() 47{ 48#if ENABLE(ASSERT) 49 setCacheLimitInBytes(0); 50 ASSERT(!m_decoderCacheMap.size()); 51 ASSERT(!m_orderedCacheList.size()); 52 ASSERT(!m_decoderCacheKeyMap.size()); 53#endif 54} 55 56ImageDecodingStore* ImageDecodingStore::instance() 57{ 58 AtomicallyInitializedStatic(ImageDecodingStore*, store = ImageDecodingStore::create().leakPtr()); 59 return store; 60} 61 62bool ImageDecodingStore::lockDecoder(const ImageFrameGenerator* generator, const SkISize& scaledSize, ImageDecoder** decoder) 63{ 64 ASSERT(decoder); 65 66 MutexLocker lock(m_mutex); 67 DecoderCacheMap::iterator iter = m_decoderCacheMap.find(DecoderCacheEntry::makeCacheKey(generator, scaledSize)); 68 if (iter == m_decoderCacheMap.end()) 69 return false; 70 71 DecoderCacheEntry* cacheEntry = iter->value.get(); 72 73 // There can only be one user of a decoder at a time. 74 ASSERT(!cacheEntry->useCount()); 75 cacheEntry->incrementUseCount(); 76 *decoder = cacheEntry->cachedDecoder(); 77 return true; 78} 79 80void ImageDecodingStore::unlockDecoder(const ImageFrameGenerator* generator, const ImageDecoder* decoder) 81{ 82 MutexLocker lock(m_mutex); 83 DecoderCacheMap::iterator iter = m_decoderCacheMap.find(DecoderCacheEntry::makeCacheKey(generator, decoder)); 84 ASSERT_WITH_SECURITY_IMPLICATION(iter != m_decoderCacheMap.end()); 85 86 CacheEntry* cacheEntry = iter->value.get(); 87 cacheEntry->decrementUseCount(); 88 89 // Put the entry to the end of list. 90 m_orderedCacheList.remove(cacheEntry); 91 m_orderedCacheList.append(cacheEntry); 92} 93 94void ImageDecodingStore::insertDecoder(const ImageFrameGenerator* generator, PassOwnPtr<ImageDecoder> decoder) 95{ 96 // Prune old cache entries to give space for the new one. 97 prune(); 98 99 OwnPtr<DecoderCacheEntry> newCacheEntry = DecoderCacheEntry::create(generator, decoder); 100 101 MutexLocker lock(m_mutex); 102 ASSERT(!m_decoderCacheMap.contains(newCacheEntry->cacheKey())); 103 insertCacheInternal(newCacheEntry.release(), &m_decoderCacheMap, &m_decoderCacheKeyMap); 104} 105 106void ImageDecodingStore::removeDecoder(const ImageFrameGenerator* generator, const ImageDecoder* decoder) 107{ 108 Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete; 109 { 110 MutexLocker lock(m_mutex); 111 DecoderCacheMap::iterator iter = m_decoderCacheMap.find(DecoderCacheEntry::makeCacheKey(generator, decoder)); 112 ASSERT_WITH_SECURITY_IMPLICATION(iter != m_decoderCacheMap.end()); 113 114 CacheEntry* cacheEntry = iter->value.get(); 115 ASSERT(cacheEntry->useCount()); 116 cacheEntry->decrementUseCount(); 117 118 // Delete only one decoder cache entry. Ownership of the cache entry 119 // is transfered to cacheEntriesToDelete such that object can be deleted 120 // outside of the lock. 121 removeFromCacheInternal(cacheEntry, &cacheEntriesToDelete); 122 123 // Remove from LRU list. 124 removeFromCacheListInternal(cacheEntriesToDelete); 125 } 126} 127 128void ImageDecodingStore::removeCacheIndexedByGenerator(const ImageFrameGenerator* generator) 129{ 130 Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete; 131 { 132 MutexLocker lock(m_mutex); 133 134 // Remove image cache objects and decoder cache objects associated 135 // with a ImageFrameGenerator. 136 removeCacheIndexedByGeneratorInternal(&m_decoderCacheMap, &m_decoderCacheKeyMap, generator, &cacheEntriesToDelete); 137 138 // Remove from LRU list as well. 139 removeFromCacheListInternal(cacheEntriesToDelete); 140 } 141} 142 143void ImageDecodingStore::clear() 144{ 145 size_t cacheLimitInBytes; 146 { 147 MutexLocker lock(m_mutex); 148 cacheLimitInBytes = m_heapLimitInBytes; 149 m_heapLimitInBytes = 0; 150 } 151 152 prune(); 153 154 { 155 MutexLocker lock(m_mutex); 156 m_heapLimitInBytes = cacheLimitInBytes; 157 } 158} 159 160void ImageDecodingStore::setCacheLimitInBytes(size_t cacheLimit) 161{ 162 { 163 MutexLocker lock(m_mutex); 164 m_heapLimitInBytes = cacheLimit; 165 } 166 prune(); 167} 168 169size_t ImageDecodingStore::memoryUsageInBytes() 170{ 171 MutexLocker lock(m_mutex); 172 return m_heapMemoryUsageInBytes; 173} 174 175int ImageDecodingStore::cacheEntries() 176{ 177 MutexLocker lock(m_mutex); 178 return m_decoderCacheMap.size(); 179} 180 181int ImageDecodingStore::decoderCacheEntries() 182{ 183 MutexLocker lock(m_mutex); 184 return m_decoderCacheMap.size(); 185} 186 187void ImageDecodingStore::prune() 188{ 189 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"), "ImageDecodingStore::prune"); 190 191 Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete; 192 { 193 MutexLocker lock(m_mutex); 194 195 // Head of the list is the least recently used entry. 196 const CacheEntry* cacheEntry = m_orderedCacheList.head(); 197 198 // Walk the list of cache entries starting from the least recently used 199 // and then keep them for deletion later. 200 while (cacheEntry) { 201 const bool isPruneNeeded = m_heapMemoryUsageInBytes > m_heapLimitInBytes || !m_heapLimitInBytes; 202 if (!isPruneNeeded) 203 break; 204 205 // Cache is not used; Remove it. 206 if (!cacheEntry->useCount()) 207 removeFromCacheInternal(cacheEntry, &cacheEntriesToDelete); 208 cacheEntry = cacheEntry->next(); 209 } 210 211 // Remove from cache list as well. 212 removeFromCacheListInternal(cacheEntriesToDelete); 213 } 214} 215 216template<class T, class U, class V> 217void ImageDecodingStore::insertCacheInternal(PassOwnPtr<T> cacheEntry, U* cacheMap, V* identifierMap) 218{ 219 const size_t cacheEntryBytes = cacheEntry->memoryUsageInBytes(); 220 m_heapMemoryUsageInBytes += cacheEntryBytes; 221 222 // m_orderedCacheList is used to support LRU operations to reorder cache 223 // entries quickly. 224 m_orderedCacheList.append(cacheEntry.get()); 225 226 typename U::KeyType key = cacheEntry->cacheKey(); 227 typename V::AddResult result = identifierMap->add(cacheEntry->generator(), typename V::MappedType()); 228 result.storedValue->value.add(key); 229 cacheMap->add(key, cacheEntry); 230 231 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"), "ImageDecodingStoreHeapMemoryUsageBytes", m_heapMemoryUsageInBytes); 232 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"), "ImageDecodingStoreNumOfDecoders", m_decoderCacheMap.size()); 233} 234 235template<class T, class U, class V> 236void ImageDecodingStore::removeFromCacheInternal(const T* cacheEntry, U* cacheMap, V* identifierMap, Vector<OwnPtr<CacheEntry> >* deletionList) 237{ 238 const size_t cacheEntryBytes = cacheEntry->memoryUsageInBytes(); 239 ASSERT(m_heapMemoryUsageInBytes >= cacheEntryBytes); 240 m_heapMemoryUsageInBytes -= cacheEntryBytes; 241 242 // Remove entry from identifier map. 243 typename V::iterator iter = identifierMap->find(cacheEntry->generator()); 244 ASSERT(iter != identifierMap->end()); 245 iter->value.remove(cacheEntry->cacheKey()); 246 if (!iter->value.size()) 247 identifierMap->remove(iter); 248 249 // Remove entry from cache map. 250 deletionList->append(cacheMap->take(cacheEntry->cacheKey())); 251 252 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"), "ImageDecodingStoreHeapMemoryUsageBytes", m_heapMemoryUsageInBytes); 253 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"), "ImageDecodingStoreNumOfDecoders", m_decoderCacheMap.size()); 254} 255 256void ImageDecodingStore::removeFromCacheInternal(const CacheEntry* cacheEntry, Vector<OwnPtr<CacheEntry> >* deletionList) 257{ 258 if (cacheEntry->type() == CacheEntry::TypeDecoder) { 259 removeFromCacheInternal(static_cast<const DecoderCacheEntry*>(cacheEntry), &m_decoderCacheMap, &m_decoderCacheKeyMap, deletionList); 260 } else { 261 ASSERT(false); 262 } 263} 264 265template<class U, class V> 266void ImageDecodingStore::removeCacheIndexedByGeneratorInternal(U* cacheMap, V* identifierMap, const ImageFrameGenerator* generator, Vector<OwnPtr<CacheEntry> >* deletionList) 267{ 268 typename V::iterator iter = identifierMap->find(generator); 269 if (iter == identifierMap->end()) 270 return; 271 272 // Get all cache identifiers associated with generator. 273 Vector<typename U::KeyType> cacheIdentifierList; 274 copyToVector(iter->value, cacheIdentifierList); 275 276 // For each cache identifier find the corresponding CacheEntry and remove it. 277 for (size_t i = 0; i < cacheIdentifierList.size(); ++i) { 278 ASSERT(cacheMap->contains(cacheIdentifierList[i])); 279 const typename U::MappedType::PtrType cacheEntry = cacheMap->get(cacheIdentifierList[i]); 280 ASSERT(!cacheEntry->useCount()); 281 removeFromCacheInternal(cacheEntry, cacheMap, identifierMap, deletionList); 282 } 283} 284 285void ImageDecodingStore::removeFromCacheListInternal(const Vector<OwnPtr<CacheEntry> >& deletionList) 286{ 287 for (size_t i = 0; i < deletionList.size(); ++i) 288 m_orderedCacheList.remove(deletionList[i].get()); 289} 290 291} // namespace blink 292