LibreOffice Module vcl (master)  1
Manager.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <graphic/Manager.hxx>
21 #include <impgraph.hxx>
22 #include <sal/log.hxx>
23 
24 #include <officecfg/Office/Common.hxx>
25 #include <unotools/configmgr.hxx>
26 
27 using namespace css;
28 
29 namespace vcl::graphic
30 {
31 namespace
32 {
33 void setupConfigurationValuesIfPossible(sal_Int64& rMemoryLimit,
34  std::chrono::seconds& rAllowedIdleTime, bool& bSwapEnabled)
35 {
37  return;
38 
39  try
40  {
41  using officecfg::Office::Common::Cache;
42 
43  rMemoryLimit = Cache::GraphicManager::GraphicMemoryLimit::get();
44  rAllowedIdleTime
45  = std::chrono::seconds(Cache::GraphicManager::GraphicAllowedIdleTime::get());
46  bSwapEnabled = Cache::GraphicManager::GraphicSwappingEnabled::get();
47  }
48  catch (...)
49  {
50  }
51 }
52 }
53 
54 Manager& Manager::get()
55 {
56  static Manager gStaticManager;
57  return gStaticManager;
58 }
59 
60 Manager::Manager()
61  : mnAllowedIdleTime(10)
62  , mbSwapEnabled(true)
63  , mbReducingGraphicMemory(false)
64  , mnMemoryLimit(300000000)
65  , mnUsedSize(0)
66  , maSwapOutTimer("graphic::Manager maSwapOutTimer")
67 {
68  setupConfigurationValuesIfPossible(mnMemoryLimit, mnAllowedIdleTime, mbSwapEnabled);
69 
70  if (mbSwapEnabled)
71  {
72  maSwapOutTimer.SetInvokeHandler(LINK(this, Manager, SwapOutTimerHandler));
75  }
76 }
77 
78 void Manager::loopGraphicsAndSwapOut(std::unique_lock<std::mutex>& rGuard)
79 {
80  // make a copy of m_pImpGraphicList because if we swap out a svg, the svg
81  // filter may create more temp Graphics which are auto-added to
82  // m_pImpGraphicList invalidating a loop over m_pImpGraphicList, e.g.
83  // reexport of tdf118346-1.odg
85 
86  for (ImpGraphic* pEachImpGraphic : aImpGraphicList)
87  {
88  if (mnUsedSize < sal_Int64(mnMemoryLimit * 0.7))
89  return;
90 
91  if (pEachImpGraphic->isSwappedOut())
92  continue;
93 
94  sal_Int64 nCurrentGraphicSize = getGraphicSizeBytes(pEachImpGraphic);
95  if (nCurrentGraphicSize > 100000)
96  {
97  if (!pEachImpGraphic->mpContext)
98  {
99  auto aCurrent = std::chrono::high_resolution_clock::now();
100  auto aDeltaTime = aCurrent - pEachImpGraphic->maLastUsed;
101  auto aSeconds = std::chrono::duration_cast<std::chrono::seconds>(aDeltaTime);
102 
103  if (aSeconds > mnAllowedIdleTime)
104  {
105  // unlock because svgio can call back into us
106  rGuard.unlock();
107  pEachImpGraphic->swapOut();
108  rGuard.lock();
109  }
110  }
111  }
112  }
113 }
114 
115 void Manager::reduceGraphicMemory(std::unique_lock<std::mutex>& rGuard)
116 {
117  // maMutex is locked in callers
118 
119  if (!mbSwapEnabled)
120  return;
121 
123  return;
124 
125  // avoid recursive reduceGraphicMemory on reexport of tdf118346-1.odg to odg
127  return;
129 
130  loopGraphicsAndSwapOut(rGuard);
131 
132  sal_Int64 calculatedSize = 0;
133  for (ImpGraphic* pEachImpGraphic : m_pImpGraphicList)
134  {
135  if (!pEachImpGraphic->isSwappedOut())
136  {
137  calculatedSize += getGraphicSizeBytes(pEachImpGraphic);
138  }
139  }
140 
141  if (calculatedSize != mnUsedSize)
142  {
143  assert(rGuard.owns_lock() && rGuard.mutex() == &maMutex);
144  // coverity[missing_lock: FALSE] - as above assert
145  mnUsedSize = calculatedSize;
146  }
147 
148  mbReducingGraphicMemory = false;
149 }
150 
151 sal_Int64 Manager::getGraphicSizeBytes(const ImpGraphic* pImpGraphic)
152 {
153  if (!pImpGraphic->isAvailable())
154  return 0;
155  return pImpGraphic->getSizeBytes();
156 }
157 
158 IMPL_LINK(Manager, SwapOutTimerHandler, Timer*, pTimer, void)
159 {
160  std::unique_lock aGuard(maMutex);
161 
162  pTimer->Stop();
163  reduceGraphicMemory(aGuard);
164  pTimer->Start();
165 }
166 
167 void Manager::registerGraphic(const std::shared_ptr<ImpGraphic>& pImpGraphic)
168 {
169  std::unique_lock aGuard(maMutex);
170 
171  // make some space first
173  reduceGraphicMemory(aGuard);
174 
175  // Insert and update the used size (bytes)
176  assert(aGuard.owns_lock() && aGuard.mutex() == &maMutex);
177  // coverity[missing_lock: FALSE] - as above assert
178  mnUsedSize += getGraphicSizeBytes(pImpGraphic.get());
179  m_pImpGraphicList.insert(pImpGraphic.get());
180 
181  // calculate size of the graphic set
182  sal_Int64 calculatedSize = 0;
183  for (ImpGraphic* pEachImpGraphic : m_pImpGraphicList)
184  {
185  if (!pEachImpGraphic->isSwappedOut())
186  {
187  calculatedSize += getGraphicSizeBytes(pEachImpGraphic);
188  }
189  }
190 
191  if (calculatedSize != mnUsedSize)
192  {
193  SAL_INFO_IF(calculatedSize != mnUsedSize, "vcl.gdi",
194  "Calculated size mismatch. Variable size is '"
195  << mnUsedSize << "' but calculated size is '" << calculatedSize << "'");
196  mnUsedSize = calculatedSize;
197  }
198 }
199 
201 {
202  std::scoped_lock aGuard(maMutex);
203 
204  mnUsedSize -= getGraphicSizeBytes(pImpGraphic);
205  m_pImpGraphicList.erase(pImpGraphic);
206 }
207 
208 std::shared_ptr<ImpGraphic> Manager::copy(std::shared_ptr<ImpGraphic> const& rImpGraphicPtr)
209 {
210  auto pReturn = std::make_shared<ImpGraphic>(*rImpGraphicPtr);
211  registerGraphic(pReturn);
212  return pReturn;
213 }
214 
215 std::shared_ptr<ImpGraphic> Manager::newInstance()
216 {
217  auto pReturn = std::make_shared<ImpGraphic>();
218  registerGraphic(pReturn);
219  return pReturn;
220 }
221 
222 std::shared_ptr<ImpGraphic> Manager::newInstance(std::shared_ptr<GfxLink> const& rGfxLink,
223  sal_Int32 nPageIndex)
224 {
225  auto pReturn = std::make_shared<ImpGraphic>(rGfxLink, nPageIndex);
226  registerGraphic(pReturn);
227  return pReturn;
228 }
229 
230 std::shared_ptr<ImpGraphic> Manager::newInstance(const BitmapEx& rBitmapEx)
231 {
232  auto pReturn = std::make_shared<ImpGraphic>(rBitmapEx);
233  registerGraphic(pReturn);
234  return pReturn;
235 }
236 
237 std::shared_ptr<ImpGraphic> Manager::newInstance(const Animation& rAnimation)
238 {
239  auto pReturn = std::make_shared<ImpGraphic>(rAnimation);
240  registerGraphic(pReturn);
241  return pReturn;
242 }
243 
244 std::shared_ptr<ImpGraphic>
245 Manager::newInstance(const std::shared_ptr<VectorGraphicData>& rVectorGraphicDataPtr)
246 {
247  auto pReturn = std::make_shared<ImpGraphic>(rVectorGraphicDataPtr);
248  registerGraphic(pReturn);
249  return pReturn;
250 }
251 
252 std::shared_ptr<ImpGraphic> Manager::newInstance(const GDIMetaFile& rMetaFile)
253 {
254  auto pReturn = std::make_shared<ImpGraphic>(rMetaFile);
255  registerGraphic(pReturn);
256  return pReturn;
257 }
258 
259 std::shared_ptr<ImpGraphic> Manager::newInstance(const GraphicExternalLink& rGraphicLink)
260 {
261  auto pReturn = std::make_shared<ImpGraphic>(rGraphicLink);
262  registerGraphic(pReturn);
263  return pReturn;
264 }
265 
266 void Manager::swappedIn(const ImpGraphic* pImpGraphic, sal_Int64 nSizeBytes)
267 {
268  std::scoped_lock aGuard(maMutex);
269  if (pImpGraphic)
270  {
271  mnUsedSize += nSizeBytes;
272  }
273 }
274 
275 void Manager::swappedOut(const ImpGraphic* pImpGraphic, sal_Int64 nSizeBytes)
276 {
277  std::scoped_lock aGuard(maMutex);
278  if (pImpGraphic)
279  {
280  mnUsedSize -= nSizeBytes;
281  }
282 }
283 
284 void Manager::changeExisting(const ImpGraphic* pImpGraphic, sal_Int64 nOldSizeBytes)
285 {
286  std::scoped_lock aGuard(maMutex);
287 
288  mnUsedSize -= nOldSizeBytes;
289  mnUsedSize += getGraphicSizeBytes(pImpGraphic);
290 }
291 } // end vcl::graphic
292 
293 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
o3tl::sorted_vector< ImpGraphic * > m_pImpGraphicList
Definition: Manager.hxx:35
std::mutex maMutex
Definition: Manager.hxx:34
static sal_Int64 getGraphicSizeBytes(const ImpGraphic *pImpGraphic)
Definition: Manager.cxx:151
void unregisterGraphic(ImpGraphic *pImpGraphic)
Definition: Manager.cxx:200
#define SAL_INFO_IF(condition, area, stream)
std::mutex maMutex
void registerGraphic(const std::shared_ptr< ImpGraphic > &rImpGraphic)
Definition: Manager.cxx:167
std::shared_ptr< ImpGraphic > copy(std::shared_ptr< ImpGraphic > const &pImpGraphic)
Definition: Manager.cxx:208
bool mbReducingGraphicMemory
Definition: Manager.hxx:38
void swappedIn(const ImpGraphic *pImpGraphic, sal_Int64 nSizeBytes)
Definition: Manager.cxx:266
static bool IsFuzzing()
IMPL_LINK(Manager, SwapOutTimerHandler, Timer *, pTimer, void)
Definition: Manager.cxx:158
void loopGraphicsAndSwapOut(std::unique_lock< std::mutex > &rGuard)
Definition: Manager.cxx:78
std::chrono::seconds mnAllowedIdleTime
Definition: Manager.hxx:36
sal_Int64 mnUsedSize
Definition: Manager.hxx:40
void reduceGraphicMemory(std::unique_lock< std::mutex > &rGuard)
Definition: Manager.cxx:115
std::shared_ptr< ImpGraphic > newInstance()
Definition: Manager.cxx:215
virtual void Start(bool bStartTimer=true) override
Schedules the task for execution.
Definition: timer.cxx:83
void changeExisting(const ImpGraphic *pImpGraphic, sal_Int64 nOldSize)
Definition: Manager.cxx:284
void SetTimeout(sal_uInt64 nTimeoutMs)
Definition: timer.cxx:90
sal_uLong getSizeBytes() const
Definition: impgraph.cxx:962
void SetInvokeHandler(const Link< Timer *, void > &rLink)
Definition: timer.hxx:56
bool isAvailable() const
Definition: impgraph.cxx:513
sal_Int64 mnMemoryLimit
Definition: Manager.hxx:39
Definition: timer.hxx:26
std::pair< const_iterator, bool > insert(Value &&x)
void swappedOut(const ImpGraphic *pImpGraphic, sal_Int64 nSizeBytes)
Definition: Manager.cxx:275
size_type erase(const Value &x)
bool m_bDetectedRangeSegmentation false