cid#1607171 Data race condition
[LibreOffice.git] / sd / source / ui / slidesorter / cache / SlsPageCacheManager.cxx
blob6a22b437642fcbc6f4152cbcc74ff216fb32629c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <cache/SlsPageCacheManager.hxx>
22 #include "SlsBitmapCache.hxx"
23 #include <unomodel.hxx>
24 #include <deque>
25 #include <map>
26 #include <memory>
27 #include <unordered_map>
28 #include <utility>
30 #include <comphelper/lok.hxx>
32 namespace {
34 /** Collection of data that is stored for all active preview caches.
36 class CacheDescriptor
38 public:
39 ::sd::slidesorter::cache::PageCacheManager::DocumentKey mpDocument;
40 Size maPreviewSize;
42 CacheDescriptor(
43 ::sd::slidesorter::cache::PageCacheManager::DocumentKey pDocument,
44 const Size& rPreviewSize)
45 :mpDocument(std::move(pDocument)),maPreviewSize(rPreviewSize)
47 /// Test for equality with respect to all members.
48 class Equal {public: bool operator() (
49 const CacheDescriptor& rDescriptor1, const CacheDescriptor& rDescriptor2) const {
50 return rDescriptor1.mpDocument==rDescriptor2.mpDocument
51 && rDescriptor1.maPreviewSize==rDescriptor2.maPreviewSize;
52 } };
53 /// Hash function that takes all members into account.
54 class Hash {public: size_t operator() (const CacheDescriptor& rDescriptor) const {
55 return reinterpret_cast<size_t>(rDescriptor.mpDocument.get()) + rDescriptor.maPreviewSize.Width();
56 } };
59 /** Collection of data that is stored for the inactive, recently used
60 caches.
62 class RecentlyUsedCacheDescriptor
64 public:
65 Size maPreviewSize;
66 std::shared_ptr< ::sd::slidesorter::cache::BitmapCache> mpCache;
68 RecentlyUsedCacheDescriptor(
69 const Size& rPreviewSize,
70 std::shared_ptr< ::sd::slidesorter::cache::BitmapCache> pCache)
71 :maPreviewSize(rPreviewSize),mpCache(std::move(pCache))
75 /** The list of recently used caches is organized as queue. When elements
76 are added the list is shortened to the maximally allowed number of
77 elements by removing the least recently used elements.
79 typedef ::std::deque<RecentlyUsedCacheDescriptor> RecentlyUsedQueue;
81 /** Compare the caches by preview size. Those that match the given size
82 come first, then, regardless of the given size, the largest ones before
83 the smaller ones.
85 class BestFittingCacheComparer
87 public:
88 explicit BestFittingCacheComparer (const Size& rPreferredSize)
89 : maPreferredSize(rPreferredSize)
91 bool operator()(const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement1,
92 const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement2)
94 if (rElement2.first == maPreferredSize)
95 return false;
96 else if (rElement1.first == maPreferredSize)
97 return true;
98 else
99 return (rElement1.first.Width()*rElement1.first.Height()
100 > rElement2.first.Width()*rElement2.first.Height());
103 private:
104 Size maPreferredSize;
107 } // end of anonymous namespace
109 namespace sd::slidesorter::cache {
111 /** Container for the active caches.
113 class PageCacheManager::PageCacheContainer
114 : public std::unordered_map<CacheDescriptor,
115 std::shared_ptr<BitmapCache>,
116 CacheDescriptor::Hash,
117 CacheDescriptor::Equal>
119 public:
120 PageCacheContainer() {}
122 /** Compare entries in the cache container with respect to the cache
123 address only.
125 class CompareWithCache { public:
126 explicit CompareWithCache(std::shared_ptr<BitmapCache> pCache)
127 : mpCache(std::move(pCache)) {}
128 bool operator () (const PageCacheContainer::value_type& rValue) const
129 { return rValue.second == mpCache; }
130 private:
131 std::shared_ptr<BitmapCache> mpCache;
135 /** The recently used caches are stored in one queue for each document.
137 class PageCacheManager::RecentlyUsedPageCaches
139 public:
140 typedef DocumentKey key_type;
141 typedef RecentlyUsedQueue mapped_type;
142 typedef std::map<key_type,mapped_type>::iterator iterator;
143 private:
144 std::map<key_type,mapped_type> maMap;
145 public:
146 RecentlyUsedPageCaches () {};
148 iterator end() { return maMap.end(); }
149 void clear() { maMap.clear(); }
150 iterator find(const key_type& key) { return maMap.find(key); }
151 template<class... Args>
152 std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
155 class PageCacheManager::Deleter
157 public:
158 void operator() (PageCacheManager* pObject) { delete pObject; }
161 //===== PageCacheManager ====================================================
163 std::weak_ptr<PageCacheManager> PageCacheManager::mpInstance;
165 std::shared_ptr<PageCacheManager> PageCacheManager::Instance()
167 std::shared_ptr<PageCacheManager> pInstance;
169 ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex());
171 pInstance = mpInstance.lock();
172 if (pInstance == nullptr)
174 pInstance = std::shared_ptr<PageCacheManager>(
175 new PageCacheManager(),
176 PageCacheManager::Deleter());
177 mpInstance = pInstance;
180 return pInstance;
183 PageCacheManager::PageCacheManager()
184 : mpPageCaches(new PageCacheContainer()),
185 mpRecentlyUsedPageCaches(new RecentlyUsedPageCaches())
189 PageCacheManager::~PageCacheManager()
193 std::shared_ptr<BitmapCache> PageCacheManager::GetCache (
194 const DocumentKey& pDocument,
195 const Size& rPreviewSize)
197 std::shared_ptr<BitmapCache> pResult;
199 // Look for the cache in the list of active caches.
200 CacheDescriptor aKey (pDocument, rPreviewSize);
201 PageCacheContainer::iterator iCache (mpPageCaches->find(aKey));
202 if (iCache != mpPageCaches->end())
203 pResult = iCache->second;
205 // Look for the cache in the list of recently used caches.
206 if (pResult == nullptr)
207 pResult = GetRecentlyUsedCache(pDocument, rPreviewSize);
209 // Create the cache when no suitable one does exist.
210 if (pResult == nullptr)
211 pResult = std::make_shared<BitmapCache>();
213 // The cache may be newly created and thus empty or is old and may
214 // contain previews that are not up-to-date. Recycle previews from
215 // other caches to fill in the holes.
216 Recycle(pResult, pDocument,rPreviewSize);
218 // Put the new (or old) cache into the container.
219 mpPageCaches->emplace(aKey, pResult);
221 return pResult;
224 void PageCacheManager::Recycle (
225 const std::shared_ptr<BitmapCache>& rpCache,
226 const DocumentKey& pDocument,
227 const Size& rPreviewSize)
229 BestFittingPageCaches aCaches;
231 // Add bitmap caches from active caches.
232 for (auto& rActiveCache : *mpPageCaches)
234 if (rActiveCache.first.mpDocument == pDocument)
235 aCaches.emplace_back(
236 rActiveCache.first.maPreviewSize, rActiveCache.second);
239 // Add bitmap caches from recently used caches.
240 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
241 if (iQueue != mpRecentlyUsedPageCaches->end())
243 for (const auto& rRecentCache : iQueue->second)
244 aCaches.emplace_back(
245 rRecentCache.maPreviewSize, rRecentCache.mpCache);
248 ::std::sort(aCaches.begin(), aCaches.end(), BestFittingCacheComparer(rPreviewSize));
250 for (const auto& rBestCache : aCaches)
252 rpCache->Recycle(*rBestCache.second);
256 void PageCacheManager::ReleaseCache (const std::shared_ptr<BitmapCache>& rpCache)
258 PageCacheContainer::iterator iCache (::std::find_if(
259 mpPageCaches->begin(),
260 mpPageCaches->end(),
261 PageCacheContainer::CompareWithCache(rpCache)));
263 if (iCache != mpPageCaches->end())
265 assert(iCache->second == rpCache);
267 PutRecentlyUsedCache(iCache->first.mpDocument,iCache->first.maPreviewSize,rpCache);
269 mpPageCaches->erase(iCache);
273 std::shared_ptr<BitmapCache> PageCacheManager::ChangeSize (
274 const std::shared_ptr<BitmapCache>& rpCache,
275 const Size&,
276 const Size& rNewPreviewSize)
278 std::shared_ptr<BitmapCache> pResult;
280 if (rpCache != nullptr)
282 // Look up the given cache in the list of active caches.
283 PageCacheContainer::iterator iCacheToChange (::std::find_if(
284 mpPageCaches->begin(),
285 mpPageCaches->end(),
286 PageCacheContainer::CompareWithCache(rpCache)));
287 if (iCacheToChange != mpPageCaches->end())
289 assert(iCacheToChange->second == rpCache);
291 // Now, we can change the preview size of the existing one by
292 // removing the cache from the list and re-insert it with the
293 // updated size.
294 const ::sd::slidesorter::cache::PageCacheManager::DocumentKey aKey (
295 iCacheToChange->first.mpDocument);
296 mpPageCaches->erase(iCacheToChange);
297 mpPageCaches->emplace(
298 CacheDescriptor(aKey,rNewPreviewSize),
299 rpCache);
301 pResult = rpCache;
303 // In multi user view this can happen - no issue (reset after switching MasterPage)
304 else if (!comphelper::LibreOfficeKit::isActive())
306 assert(iCacheToChange != mpPageCaches->end());
310 return pResult;
313 bool PageCacheManager::InvalidatePreviewBitmap (
314 const DocumentKey& pDocument,
315 const SdrPage* pKey)
317 bool bHasChanged (false);
319 if (pDocument!=nullptr)
321 // Iterate over all caches that are currently in use and invalidate
322 // the previews in those that belong to the document.
323 for (auto& rCache : *mpPageCaches)
324 if (rCache.first.mpDocument == pDocument)
325 bHasChanged |= rCache.second->InvalidateBitmap(pKey);
327 // Invalidate the previews in the recently used caches belonging to
328 // the given document.
329 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
330 if (iQueue != mpRecentlyUsedPageCaches->end())
332 for (const auto& rCache2 : iQueue->second)
333 bHasChanged |= rCache2.mpCache->InvalidateBitmap(pKey);
337 return bHasChanged;
340 void PageCacheManager::InvalidateAllPreviewBitmaps (const DocumentKey& pDocument)
342 if (pDocument == nullptr)
343 return;
345 // Iterate over all caches that are currently in use and invalidate the
346 // previews in those that belong to the document.
347 for (auto& rCache : *mpPageCaches)
348 if (rCache.first.mpDocument == pDocument)
349 rCache.second->InvalidateCache();
351 // Invalidate the previews in the recently used caches belonging to the
352 // given document.
353 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
354 if (iQueue != mpRecentlyUsedPageCaches->end())
356 for (const auto& rCache2 : iQueue->second)
357 rCache2.mpCache->InvalidateCache();
361 void PageCacheManager::InvalidateAllCaches()
363 // Iterate over all caches that are currently in use and invalidate
364 // them.
365 for (auto& rCache : *mpPageCaches)
366 rCache.second->InvalidateCache();
368 // Remove all recently used caches, there is not much sense in storing
369 // invalidated and unused caches.
370 mpRecentlyUsedPageCaches->clear();
373 void PageCacheManager::ReleasePreviewBitmap (const SdrPage* pPage)
375 for (auto& rCache : *mpPageCaches)
376 rCache.second->ReleaseBitmap(pPage);
379 std::shared_ptr<BitmapCache> PageCacheManager::GetRecentlyUsedCache (
380 const DocumentKey& pDocument,
381 const Size& rPreviewSize)
383 std::shared_ptr<BitmapCache> pCache;
385 // Look for the cache in the list of recently used caches.
386 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
387 if (iQueue != mpRecentlyUsedPageCaches->end())
389 RecentlyUsedQueue::iterator iCache = std::find_if(iQueue->second.begin(), iQueue->second.end(),
390 [&rPreviewSize](const RecentlyUsedCacheDescriptor& rCache) { return rCache.maPreviewSize == rPreviewSize; });
391 if (iCache != iQueue->second.end())
393 pCache = iCache->mpCache;
394 iQueue->second.erase(iCache);
398 return pCache;
401 void PageCacheManager::PutRecentlyUsedCache(
402 DocumentKey const & pDocument,
403 const Size& rPreviewSize,
404 const std::shared_ptr<BitmapCache>& rpCache)
406 // Look up the list of recently used caches for the given document.
407 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
408 if (iQueue == mpRecentlyUsedPageCaches->end())
409 iQueue = mpRecentlyUsedPageCaches->emplace(
410 pDocument, RecentlyUsedQueue()
411 ).first;
413 if (iQueue != mpRecentlyUsedPageCaches->end())
415 iQueue->second.push_front(RecentlyUsedCacheDescriptor(rPreviewSize,rpCache));
416 // Shorten the list of recently used caches to the allowed maximal length.
417 while (iQueue->second.size() > mnMaximalRecentlyCacheCount)
418 iQueue->second.pop_back();
422 } // end of namespace ::sd::slidesorter::cache
424 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */