Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_tile_data_pooler.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7
8#include <stdio.h>
9#include "kis_tile_data.h"
10#include "kis_tile_data_store.h"
12#include "kis_debug.h"
14#include "kis_image_config.h"
15
16
17const qint32 KisTileDataPooler::MAX_NUM_CLONES = 16;
18const qint32 KisTileDataPooler::MAX_TIMEOUT = 60000; // 01m00s
19const qint32 KisTileDataPooler::MIN_TIMEOUT = 100; // 00m00.100s
21
22//#define DEBUG_POOLER
23
24#ifdef DEBUG_POOLER
25#define DEBUG_CLONE_ACTION(td, numClones) \
26 printf("Cloned (%d):\t\t\t\t0x%X (clones: %d, users: %d, refs: %d)\n", \
27 numClones, td, td->m_clonesStack.size(), \
28 (int)td->m_usersCount, (int)td->m_refCount)
29#define DEBUG_SIMPLE_ACTION(action) \
30 printf("pooler: %s\n", action)
31
32#define RUNTIME_SANITY_CHECK(td) do { \
33 if(td->m_usersCount < td->m_refCount) { \
34 qInfo("**** Suspicious tiledata: 0x%X (clones: %d, users: %d, refs: %d) ****", \
35 td, td->m_clonesStack.size(), \
36 (int)td->m_usersCount, (int)td->m_refCount); \
37 } \
38 if(td->m_usersCount <= 0) { \
39 qFatal("pooler: Tiledata 0x%X has zero users counter. Crashing...", td); \
40 } \
41 } while(0) \
42
43#define DEBUG_TILE_STATISTICS() debugTileStatistics()
44
45#define DEBUG_LISTS(mem, beggars, beggarsMem, donors, donorsMem) \
46 do { \
47 dbgKrita << "--- getLists finished ---"; \
48 dbgKrita << " memoryOccupied:" << mem << "/" << m_memoryLimit; \
49 dbgKrita << " donors:" << donors.size() \
50 << "(mem:" << donorsMem << ")"; \
51 dbgKrita << " beggars:" << beggars.size() \
52 << "(mem:" << beggarsMem << ")"; \
53 dbgKrita << "--- ----------------- ---"; \
54 } while(0)
55
56#define DEBUG_ALLOC_CLONE(mem, totalMem) \
57 dbgKrita << "Alloc mem for clones:" << mem \
58 << "\tMem usage:" << totalMem << "/" << m_memoryLimit
59
60#define DEBUG_FREE_CLONE(freed, demanded) \
61 dbgKrita << "Freed mem for clones:" << freed \
62 << "/" << qAbs(demanded)
63
64#else
65#define DEBUG_CLONE_ACTION(td, numClones)
66#define DEBUG_SIMPLE_ACTION(action)
67#define RUNTIME_SANITY_CHECK(td)
68#define DEBUG_TILE_STATISTICS()
69#define DEBUG_LISTS(mem, beggars, beggarsMem, donors, donorsMem) \
70 Q_UNUSED(mem); \
71 Q_UNUSED(beggars); \
72 Q_UNUSED(beggarsMem); \
73 Q_UNUSED(donors); \
74 Q_UNUSED(donorsMem);
75#define DEBUG_ALLOC_CLONE(mem, totalMem)
76#define DEBUG_FREE_CLONE(freed, demanded)
77#endif
78
79
81 : QThread()
82{
84 m_store = store;
86 m_lastCycleHadWork = false;
90
91 if(memoryLimit >= 0) {
92 m_memoryLimit = memoryLimit;
93 }
94 else {
95 m_memoryLimit = MiB_TO_METRIC(KisImageConfig(true).poolLimit());
96 }
97}
98
102
104{
105 m_semaphore.release();
106}
107
109{
110 unsigned long exitTimeout = 100;
111 do {
112 m_shouldExitFlag = true;
113 kick();
114 } while(!wait(exitTimeout));
115}
116
118{
120 qint32 numUsers = td->m_usersCount;
121 qint32 numPresentClones = td->m_clonesStack.size();
122 qint32 totalClones = qMin(numUsers - 1, MAX_NUM_CLONES);
123
124 return totalClones - numPresentClones;
125}
126
127void KisTileDataPooler::cloneTileData(KisTileData *td, qint32 numClones) const
128{
129 if (numClones > 0) {
130 td->blockSwapping();
131 for (qint32 i = 0; i < numClones; i++) {
132 td->m_clonesStack.push(new KisTileData(*td, false));
133 }
134 td->unblockSwapping();
135 } else {
136 qint32 numUnneededClones = qAbs(numClones);
137 for (qint32 i = 0; i < numUnneededClones; i++) {
138 KisTileData *clone = 0;
139
140 bool result = td->m_clonesStack.pop(clone);
141 if(!result) break;
142
143 delete clone;
144 }
145 }
146
147 DEBUG_CLONE_ACTION(td, numClones);
148}
149
151{
152 bool success;
153
155 success = m_semaphore.tryAcquire(1, m_timeout);
156 else {
157 m_semaphore.acquire();
158 success = true;
159 }
160
161 m_lastCycleHadWork = false;
162 if (success) {
164 } else {
167 }
168}
169
171{
172 if(!m_memoryLimit) return;
173
174 m_shouldExitFlag = false;
175
176 while (1) {
177 DEBUG_SIMPLE_ACTION("went to bed... Zzz...");
178
179 waitForWork();
180
182 break;
183
184 QThread::msleep(0);
185 DEBUG_SIMPLE_ACTION("cycle started");
186
187
189 QList<KisTileData*> beggars;
190 QList<KisTileData*> donors;
191 qint32 memoryOccupied;
192
193 qint32 statRealMemory;
194 qint32 statHistoricalMemory;
195
196
197 getLists(iter, beggars, donors,
198 memoryOccupied,
199 statRealMemory,
200 statHistoricalMemory);
201
203 processLists(beggars, donors, memoryOccupied);
204
205 m_lastPoolMemoryMetric = memoryOccupied;
206 m_lastRealMemoryMetric = statRealMemory;
207 m_lastHistoricalMemoryMetric = statHistoricalMemory;
208
209 m_store->endIteration(iter);
210
212 DEBUG_SIMPLE_ACTION("cycle finished");
213 }
214}
215
217{
218 KIS_SAFE_ASSERT_RECOVER_RETURN(!isRunning());
219
221 QList<KisTileData*> beggars;
222 QList<KisTileData*> donors;
223 qint32 memoryOccupied;
224
225 qint32 statRealMemory;
226 qint32 statHistoricalMemory;
227
228
229 getLists(iter, beggars, donors,
230 memoryOccupied,
231 statRealMemory,
232 statHistoricalMemory);
233
234 m_lastPoolMemoryMetric = memoryOccupied;
235 m_lastRealMemoryMetric = statRealMemory;
236 m_lastHistoricalMemoryMetric = statHistoricalMemory;
237
238 m_store->endIteration(iter);
239}
240
245
250
255
256inline int KisTileDataPooler::clonesMetric(KisTileData *td, int numClones) {
257 return numClones * td->pixelSize();
258}
259
261 return td->m_clonesStack.size() * td->pixelSize();
262}
263
265{
266 qint32 extraClones = -numClonesNeeded(td);
267
268 if(extraClones > 0) {
269 cloneTileData(td, -extraClones);
270 }
271}
272
274{
275 qint32 clonesNeeded = !td->age() ? qMax(0, numClonesNeeded(td)) : 0;
276 return clonesMetric(td, clonesNeeded);
277}
278
280{
281 return td->age() && clonesMetric(td);
282}
283
284template<class Iter>
286 QList<KisTileData*> &beggars,
287 QList<KisTileData*> &donors,
288 qint32 &memoryOccupied,
289 qint32 &statRealMemory,
290 qint32 &statHistoricalMemory)
291{
292 memoryOccupied = 0;
293 statRealMemory = 0;
294 statHistoricalMemory = 0;
295
296 qint32 needMemoryTotal = 0;
297 qint32 canDonorMemoryTotal = 0;
298
299 qint32 neededMemory;
300 qint32 donoredMemory;
301
302 KisTileData *item;
303
304 while(iter->hasNext()) {
305 item = iter->next();
306
308
309 if((neededMemory = needMemory(item))) {
310 needMemoryTotal += neededMemory;
311 beggars.append(item);
312 }
313 else if((donoredMemory = canDonorMemory(item))) {
314 canDonorMemoryTotal += donoredMemory;
315 donors.append(item);
316 }
317
318 memoryOccupied += clonesMetric(item);
319
320 // statistics gathering
321 if (item->historical()) {
322 statHistoricalMemory += item->pixelSize();
323 } else {
324 statRealMemory += item->pixelSize();
325 }
326 }
327
328 DEBUG_LISTS(memoryOccupied,
329 beggars, needMemoryTotal,
330 donors, canDonorMemoryTotal);
331}
332
334 qint32 memoryMetric)
335{
336 qint32 memoryFreed = 0;
337
338 QMutableListIterator<KisTileData*> iter(donors);
339 iter.toBack();
340
341 while(iter.hasPrevious() && memoryFreed < memoryMetric) {
342 KisTileData *item = iter.previous();
343
344 qint32 numClones = item->m_clonesStack.size();
345 cloneTileData(item, -numClones);
346 memoryFreed += clonesMetric(item, numClones);
347
348 iter.remove();
349 }
350
351 return memoryFreed;
352}
353
355 QList<KisTileData*> &donors,
356 qint32 &memoryOccupied)
357{
358 bool hadWork = false;
359
360
361 Q_FOREACH (KisTileData *item, beggars) {
362 qint32 clonesNeeded = numClonesNeeded(item);
363 qint32 clonesMemory = clonesMetric(item, clonesNeeded);
364
365 qint32 memoryLeft =
366 m_memoryLimit - (memoryOccupied + clonesMemory);
367
368 if(memoryLeft < 0) {
369 qint32 freedMemory = tryGetMemory(donors, -memoryLeft);
370 memoryOccupied -= freedMemory;
371
372 DEBUG_FREE_CLONE(freedMemory, memoryLeft);
373
374 if(m_memoryLimit < memoryOccupied + clonesMemory)
375 break;
376 }
377
378 cloneTileData(item, clonesNeeded);
379 DEBUG_ALLOC_CLONE(clonesMemory, memoryOccupied);
380
381 memoryOccupied += clonesMemory;
382 hadWork = true;
383 }
384
385 return hadWork;
386}
387
389{
395 qint64 preallocatedTiles=0;
396
398 KisTileData *item;
399
400 while(iter->hasNext()) {
401 item = iter->next();
402 preallocatedTiles += item->m_clonesStack.size();
403 }
404
405 m_store->endIteration(iter);
406
407 dbgKrita << "Tiles statistics:\t total:" << m_store->numTiles() << "\t preallocated:"<< preallocatedTiles;
408}
409
static const qint32 MAX_NUM_CLONES
qint64 lastRealMemoryMetric() const
bool processLists(QList< KisTileData * > &beggars, QList< KisTileData * > &donors, qint32 &memoryOccupied)
qint32 numClonesNeeded(KisTileData *td) const
qint32 canDonorMemory(KisTileData *td)
static const qint32 TIMEOUT_FACTOR
void tryFreeOrphanedClones(KisTileData *td)
qint64 lastHistoricalMemoryMetric() const
qint32 needMemory(KisTileData *td)
KisTileDataStore * m_store
static const qint32 MIN_TIMEOUT
qint64 lastPoolMemoryMetric() const
KisTileDataPooler(KisTileDataStore *store, qint32 memoryLimit=-1)
static const qint32 MAX_TIMEOUT
void getLists(Iter *iter, QList< KisTileData * > &beggars, QList< KisTileData * > &donors, qint32 &memoryOccupied, qint32 &statRealMemory, qint32 &statHistoricalMemory)
qint32 tryGetMemory(QList< KisTileData * > &donors, qint32 memoryMetric)
void cloneTileData(KisTileData *td, qint32 numClones) const
int clonesMetric(KisTileData *td, int numClones)
void endIteration(KisTileDataStoreIterator *iterator)
KisTileDataStoreIterator * beginIteration()
qint32 numTiles() const
KisTileDataStoreReverseIterator * beginReverseIteration()
void unblockSwapping()
quint32 pixelSize() const
void blockSwapping()
KisTileDataCache m_clonesStack
int age() const
bool historical() const
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define dbgKrita
Definition kis_debug.h:45
#define DEBUG_ALLOC_CLONE(mem, totalMem)
#define DEBUG_FREE_CLONE(freed, demanded)
#define DEBUG_TILE_STATISTICS()
#define DEBUG_SIMPLE_ACTION(action)
#define RUNTIME_SANITY_CHECK(td)
#define DEBUG_CLONE_ACTION(td, numClones)
#define DEBUG_LISTS(mem, beggars, beggarsMem, donors, donorsMem)
T MiB_TO_METRIC(T value)