1 /*
2 * Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include <QSemaphore>
20
21 #include "tiles3/swap/kis_tile_data_swapper.h"
22 #include "tiles3/swap/kis_tile_data_swapper_p.h"
23 #include "tiles3/kis_tile_data.h"
24 #include "tiles3/kis_tile_data_store.h"
25 #include "tiles3/kis_tile_data_store_iterators.h"
26 #include "kis_debug.h"
27
28 #define SEC 1000
29
30 const qint32 KisTileDataSwapper::TIMEOUT = -1;
31 const qint32 KisTileDataSwapper::DELAY = 0.7 * SEC;
32
33 //#define DEBUG_SWAPPER
34
35 #ifdef DEBUG_SWAPPER
36 #define DEBUG_ACTION(action) dbgKrita << action
37 #define DEBUG_VALUE(value) dbgKrita << "\t" << ppVar(value)
38 #else
39 #define DEBUG_ACTION(action)
40 #define DEBUG_VALUE(value)
41 #endif
42
43 class SoftSwapStrategy;
44 class AggressiveSwapStrategy;
45
46
47 struct Q_DECL_HIDDEN KisTileDataSwapper::Private
48 {
49 public:
50 QSemaphore semaphore;
51 QAtomicInt shouldExitFlag;
52 KisTileDataStore *store;
53 KisStoreLimits limits;
54 QMutex cycleLock;
55 };
56
KisTileDataSwapper(KisTileDataStore * store)57 KisTileDataSwapper::KisTileDataSwapper(KisTileDataStore *store)
58 : QThread(),
59 m_d(new Private())
60 {
61 m_d->shouldExitFlag = 0;
62 m_d->store = store;
63 }
64
~KisTileDataSwapper()65 KisTileDataSwapper::~KisTileDataSwapper()
66 {
67 delete m_d;
68 }
69
kick()70 void KisTileDataSwapper::kick()
71 {
72 m_d->semaphore.release();
73 }
74
terminateSwapper()75 void KisTileDataSwapper::terminateSwapper()
76 {
77 unsigned long exitTimeout = 100;
78 do {
79 m_d->shouldExitFlag = true;
80 kick();
81 } while(!wait(exitTimeout));
82 }
83
waitForWork()84 void KisTileDataSwapper::waitForWork()
85 {
86 m_d->semaphore.tryAcquire(1, TIMEOUT);
87 }
88
run()89 void KisTileDataSwapper::run()
90 {
91 while (1) {
92 waitForWork();
93
94 if (m_d->shouldExitFlag)
95 return;
96
97 QThread::msleep(DELAY);
98
99 doJob();
100 }
101 }
102
checkFreeMemory()103 void KisTileDataSwapper::checkFreeMemory()
104 {
105 // dbgKrita <<"check memory: high limit -" << m_d->limits.emergencyThreshold() <<"in mem -" << m_d->store->numTilesInMemory();
106 if(m_d->store->memoryMetric() > m_d->limits.emergencyThreshold())
107 doJob();
108 }
109
doJob()110 void KisTileDataSwapper::doJob()
111 {
112 /**
113 * In emergency case usual threads have access
114 * to this function as well
115 */
116 QMutexLocker locker(&m_d->cycleLock);
117
118 qint32 memoryMetric = m_d->store->memoryMetric();
119
120 DEBUG_ACTION("Started swap cycle");
121 DEBUG_VALUE(m_d->store->numTiles());
122 DEBUG_VALUE(m_d->store->numTilesInMemory());
123 DEBUG_VALUE(memoryMetric);
124
125 DEBUG_VALUE(m_d->limits.softLimitThreshold());
126 DEBUG_VALUE(m_d->limits.hardLimitThreshold());
127
128
129 if(memoryMetric > m_d->limits.softLimitThreshold()) {
130 qint32 softFree = memoryMetric - m_d->limits.softLimit();
131 DEBUG_VALUE(softFree);
132 DEBUG_ACTION("\t pass0");
133 memoryMetric -= pass<SoftSwapStrategy>(softFree);
134 DEBUG_VALUE(memoryMetric);
135
136 if(memoryMetric > m_d->limits.hardLimitThreshold()) {
137 qint32 hardFree = memoryMetric - m_d->limits.hardLimit();
138 DEBUG_VALUE(hardFree);
139 DEBUG_ACTION("\t pass1");
140 memoryMetric -= pass<AggressiveSwapStrategy>(hardFree);
141 DEBUG_VALUE(memoryMetric);
142 }
143 }
144 }
145
146
147 class SoftSwapStrategy
148 {
149 public:
150 typedef KisTileDataStoreIterator iterator;
151
beginIteration(KisTileDataStore * store)152 static inline iterator* beginIteration(KisTileDataStore *store) {
153 return store->beginIteration();
154 }
155
endIteration(KisTileDataStore * store,iterator * iter)156 static inline void endIteration(KisTileDataStore *store, iterator *iter) {
157 store->endIteration(iter);
158 }
159
isInteresting(KisTileData * td)160 static inline bool isInteresting(KisTileData *td) {
161 // We are working with mementoed tiles only...
162 return td->historical();
163 }
164
swapOutFirst(KisTileData * td)165 static inline bool swapOutFirst(KisTileData *td) {
166 return td->age() > 0;
167 }
168 };
169
170 class AggressiveSwapStrategy
171 {
172 public:
173 typedef KisTileDataStoreClockIterator iterator;
174
beginIteration(KisTileDataStore * store)175 static inline iterator* beginIteration(KisTileDataStore *store) {
176 return store->beginClockIteration();
177 }
178
endIteration(KisTileDataStore * store,iterator * iter)179 static inline void endIteration(KisTileDataStore *store, iterator *iter) {
180 store->endIteration(iter);
181 }
182
isInteresting(KisTileData * td)183 static inline bool isInteresting(KisTileData *td) {
184 // Add some aggression...
185 Q_UNUSED(td);
186 return true; // >:)
187 }
188
swapOutFirst(KisTileData * td)189 static inline bool swapOutFirst(KisTileData *td) {
190 return td->age() > 0;
191 }
192 };
193
194
195 template<class strategy>
pass(qint64 needToFreeMetric)196 qint64 KisTileDataSwapper::pass(qint64 needToFreeMetric)
197 {
198 qint64 freedMetric = 0;
199 QList<KisTileData*> additionalCandidates;
200
201 typename strategy::iterator *iter =
202 strategy::beginIteration(m_d->store);
203
204 KisTileData *item = 0;
205
206 while (iter->hasNext()) {
207 item = iter->next();
208
209 if (freedMetric >= needToFreeMetric) break;
210
211 if (!strategy::isInteresting(item)) continue;
212
213 if (strategy::swapOutFirst(item)) {
214 if (iter->trySwapOut(item)) {
215 freedMetric += item->pixelSize();
216 }
217 }
218 else {
219 item->markOld();
220 additionalCandidates.append(item);
221 }
222
223 }
224
225 Q_FOREACH (item, additionalCandidates) {
226 if (freedMetric >= needToFreeMetric) break;
227
228 if (iter->trySwapOut(item)) {
229 freedMetric += item->pixelSize();
230 }
231 }
232
233 strategy::endIteration(m_d->store, iter);
234
235 return freedMetric;
236 }
237
testingRereadConfig()238 void KisTileDataSwapper::testingRereadConfig()
239 {
240 m_d->limits = KisStoreLimits();
241 }
242