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>
26
27using namespace css;
28
29namespace vcl::graphic
30{
31namespace
32{
33void 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
55{
56 static Manager gStaticManager;
57 return gStaticManager;
58}
59
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
78void 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
115void 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
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
149}
150
151sal_Int64 Manager::getGraphicSizeBytes(const ImpGraphic* pImpGraphic)
152{
153 if (!pImpGraphic->isAvailable())
154 return 0;
155 return pImpGraphic->getSizeBytes();
156}
157
158IMPL_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
167void 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
208std::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
215std::shared_ptr<ImpGraphic> Manager::newInstance()
216{
217 auto pReturn = std::make_shared<ImpGraphic>();
218 registerGraphic(pReturn);
219 return pReturn;
220}
221
222std::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
230std::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
237std::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
244std::shared_ptr<ImpGraphic>
245Manager::newInstance(const std::shared_ptr<VectorGraphicData>& rVectorGraphicDataPtr)
246{
247 auto pReturn = std::make_shared<ImpGraphic>(rVectorGraphicDataPtr);
248 registerGraphic(pReturn);
249 return pReturn;
250}
251
252std::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
259std::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
266void 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
275void 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
284void 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: */
std::mutex maMutex
sal_uLong getSizeBytes() const
Definition: impgraph.cxx:963
bool isAvailable() const
Definition: impgraph.cxx:514
Definition: timer.hxx:27
void SetTimeout(sal_uInt64 nTimeoutMs)
Definition: timer.cxx:90
void SetInvokeHandler(const Link< Timer *, void > &rLink)
Definition: timer.hxx:56
virtual void Start(bool bStartTimer=true) override
Schedules the task for execution.
Definition: timer.cxx:83
size_type erase(const Value &x)
std::pair< const_iterator, bool > insert(Value &&x)
static bool IsFuzzing()
void changeExisting(const ImpGraphic *pImpGraphic, sal_Int64 nOldSize)
Definition: Manager.cxx:284
sal_Int64 mnMemoryLimit
Definition: Manager.hxx:39
o3tl::sorted_vector< ImpGraphic * > m_pImpGraphicList
Definition: Manager.hxx:35
std::shared_ptr< ImpGraphic > newInstance()
Definition: Manager.cxx:215
std::shared_ptr< ImpGraphic > copy(std::shared_ptr< ImpGraphic > const &pImpGraphic)
Definition: Manager.cxx:208
bool mbReducingGraphicMemory
Definition: Manager.hxx:38
static Manager & get()
Definition: Manager.cxx:54
void loopGraphicsAndSwapOut(std::unique_lock< std::mutex > &rGuard)
Definition: Manager.cxx:78
void reduceGraphicMemory(std::unique_lock< std::mutex > &rGuard)
Definition: Manager.cxx:115
void swappedOut(const ImpGraphic *pImpGraphic, sal_Int64 nSizeBytes)
Definition: Manager.cxx:275
static sal_Int64 getGraphicSizeBytes(const ImpGraphic *pImpGraphic)
Definition: Manager.cxx:151
void registerGraphic(const std::shared_ptr< ImpGraphic > &rImpGraphic)
Definition: Manager.cxx:167
void unregisterGraphic(ImpGraphic *pImpGraphic)
Definition: Manager.cxx:200
void swappedIn(const ImpGraphic *pImpGraphic, sal_Int64 nSizeBytes)
Definition: Manager.cxx:266
sal_Int64 mnUsedSize
Definition: Manager.hxx:40
std::mutex maMutex
Definition: Manager.hxx:34
std::chrono::seconds mnAllowedIdleTime
Definition: Manager.hxx:36
#define SAL_INFO_IF(condition, area, stream)
IMPL_LINK(Manager, SwapOutTimerHandler, Timer *, pTimer, void)
Definition: Manager.cxx:158