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