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