Warning, file /graphics/krita/libs/image/tiles3/kis_tile_data_pooler.cc was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  *  SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 
0008 #include <stdio.h>
0009 #include "kis_tile_data.h"
0010 #include "kis_tile_data_store.h"
0011 #include "kis_tile_data_store_iterators.h"
0012 #include "kis_debug.h"
0013 #include "kis_tile_data_pooler.h"
0014 #include "kis_image_config.h"
0015 
0016 
0017 const qint32 KisTileDataPooler::MAX_NUM_CLONES = 16;
0018 const qint32 KisTileDataPooler::MAX_TIMEOUT = 60000; // 01m00s
0019 const qint32 KisTileDataPooler::MIN_TIMEOUT = 100; // 00m00.100s
0020 const qint32 KisTileDataPooler::TIMEOUT_FACTOR = 2;
0021 
0022 //#define DEBUG_POOLER
0023 
0024 #ifdef DEBUG_POOLER
0025 #define DEBUG_CLONE_ACTION(td, numClones)                               \
0026     printf("Cloned (%d):\t\t\t\t0x%X (clones: %d, users: %d, refs: %d)\n", \
0027            numClones, td, td->m_clonesStack.size(),                      \
0028            (int)td->m_usersCount, (int)td->m_refCount)
0029 #define DEBUG_SIMPLE_ACTION(action)     \
0030     printf("pooler: %s\n", action)
0031 
0032 #define RUNTIME_SANITY_CHECK(td) do {                                   \
0033         if(td->m_usersCount < td->m_refCount) {                         \
0034             qInfo("**** Suspicious tiledata: 0x%X (clones: %d, users: %d, refs: %d) ****", \
0035                    td, td->m_clonesStack.size(),                         \
0036                    (int)td->m_usersCount, (int)td->m_refCount);         \
0037         }                                                               \
0038         if(td->m_usersCount <= 0) {                                     \
0039             qFatal("pooler: Tiledata 0x%X has zero users counter. Crashing...", td); \
0040         }                                                               \
0041     } while(0)                                                          \
0042 
0043 #define DEBUG_TILE_STATISTICS() debugTileStatistics()
0044 
0045 #define DEBUG_LISTS(mem, beggars, beggarsMem, donors, donorsMem)        \
0046     do {                                                                \
0047     dbgKrita << "--- getLists finished ---";                            \
0048     dbgKrita << "  memoryOccupied:" << mem << "/" << m_memoryLimit;     \
0049     dbgKrita << "  donors:" << donors.size()                            \
0050              << "(mem:" << donorsMem << ")";                            \
0051     dbgKrita << "  beggars:" << beggars.size()                          \
0052              << "(mem:" << beggarsMem << ")";                           \
0053     dbgKrita << "--- ----------------- ---";                            \
0054     } while(0)
0055 
0056 #define DEBUG_ALLOC_CLONE(mem, totalMem)                                \
0057         dbgKrita << "Alloc mem for clones:" << mem                      \
0058                  << "\tMem usage:" << totalMem << "/" << m_memoryLimit
0059 
0060 #define DEBUG_FREE_CLONE(freed, demanded)                               \
0061             dbgKrita << "Freed mem for clones:" << freed                \
0062                      << "/" << qAbs(demanded)
0063 
0064 #else
0065 #define DEBUG_CLONE_ACTION(td, numClones)
0066 #define DEBUG_SIMPLE_ACTION(action)
0067 #define RUNTIME_SANITY_CHECK(td)
0068 #define DEBUG_TILE_STATISTICS()
0069 #define DEBUG_LISTS(mem, beggars, beggarsMem, donors, donorsMem)               \
0070     Q_UNUSED(mem);                                                             \
0071     Q_UNUSED(beggars);                                                         \
0072     Q_UNUSED(beggarsMem);                                                      \
0073     Q_UNUSED(donors);                                                          \
0074     Q_UNUSED(donorsMem);
0075 #define DEBUG_ALLOC_CLONE(mem, totalMem)
0076 #define DEBUG_FREE_CLONE(freed, demanded)
0077 #endif
0078 
0079 
0080 KisTileDataPooler::KisTileDataPooler(KisTileDataStore *store, qint32 memoryLimit)
0081     : QThread()
0082 {
0083     m_shouldExitFlag = 0;
0084     m_store = store;
0085     m_timeout = MIN_TIMEOUT;
0086     m_lastCycleHadWork = false;
0087     m_lastPoolMemoryMetric = 0;
0088     m_lastRealMemoryMetric = 0;
0089     m_lastHistoricalMemoryMetric = 0;
0090 
0091     if(memoryLimit >= 0) {
0092         m_memoryLimit = memoryLimit;
0093     }
0094     else {
0095         m_memoryLimit = MiB_TO_METRIC(KisImageConfig(true).poolLimit());
0096     }
0097 }
0098 
0099 KisTileDataPooler::~KisTileDataPooler()
0100 {
0101 }
0102 
0103 void KisTileDataPooler::kick()
0104 {
0105     m_semaphore.release();
0106 }
0107 
0108 void KisTileDataPooler::terminatePooler()
0109 {
0110     unsigned long exitTimeout = 100;
0111     do {
0112         m_shouldExitFlag = true;
0113         kick();
0114     } while(!wait(exitTimeout));
0115 }
0116 
0117 qint32 KisTileDataPooler::numClonesNeeded(KisTileData *td) const
0118 {
0119     RUNTIME_SANITY_CHECK(td);
0120     qint32 numUsers = td->m_usersCount;
0121     qint32 numPresentClones = td->m_clonesStack.size();
0122     qint32 totalClones = qMin(numUsers - 1, MAX_NUM_CLONES);
0123 
0124     return totalClones - numPresentClones;
0125 }
0126 
0127 void KisTileDataPooler::cloneTileData(KisTileData *td, qint32 numClones) const
0128 {
0129     if (numClones > 0) {
0130         td->blockSwapping();
0131         for (qint32 i = 0; i < numClones; i++) {
0132             td->m_clonesStack.push(new KisTileData(*td, false));
0133         }
0134         td->unblockSwapping();
0135     } else {
0136         qint32 numUnneededClones = qAbs(numClones);
0137         for (qint32 i = 0; i < numUnneededClones; i++) {
0138             KisTileData *clone = 0;
0139 
0140             bool result = td->m_clonesStack.pop(clone);
0141             if(!result) break;
0142 
0143             delete clone;
0144         }
0145     }
0146 
0147     DEBUG_CLONE_ACTION(td, numClones);
0148 }
0149 
0150 void KisTileDataPooler::waitForWork()
0151 {
0152     bool success;
0153 
0154     if (m_lastCycleHadWork)
0155         success = m_semaphore.tryAcquire(1, m_timeout);
0156     else {
0157         m_semaphore.acquire();
0158         success = true;
0159     }
0160 
0161     m_lastCycleHadWork = false;
0162     if (success) {
0163         m_timeout = MIN_TIMEOUT;
0164     } else {
0165         m_timeout *= TIMEOUT_FACTOR;
0166         m_timeout = qMin(m_timeout, MAX_TIMEOUT);
0167     }
0168 }
0169 
0170 void KisTileDataPooler::run()
0171 {
0172     if(!m_memoryLimit) return;
0173 
0174     m_shouldExitFlag = false;
0175 
0176     while (1) {
0177         DEBUG_SIMPLE_ACTION("went to bed... Zzz...");
0178 
0179         waitForWork();
0180 
0181         if (m_shouldExitFlag)
0182             break;
0183 
0184         QThread::msleep(0);
0185         DEBUG_SIMPLE_ACTION("cycle started");
0186 
0187 
0188         KisTileDataStoreReverseIterator *iter = m_store->beginReverseIteration();
0189         QList<KisTileData*> beggars;
0190         QList<KisTileData*> donors;
0191         qint32 memoryOccupied;
0192 
0193         qint32 statRealMemory;
0194         qint32 statHistoricalMemory;
0195 
0196 
0197         getLists(iter, beggars, donors,
0198                  memoryOccupied,
0199                  statRealMemory,
0200                  statHistoricalMemory);
0201 
0202         m_lastCycleHadWork =
0203             processLists(beggars, donors, memoryOccupied);
0204 
0205         m_lastPoolMemoryMetric = memoryOccupied;
0206         m_lastRealMemoryMetric = statRealMemory;
0207         m_lastHistoricalMemoryMetric = statHistoricalMemory;
0208 
0209         m_store->endIteration(iter);
0210 
0211         DEBUG_TILE_STATISTICS();
0212         DEBUG_SIMPLE_ACTION("cycle finished");
0213     }
0214 }
0215 
0216 void KisTileDataPooler::forceUpdateMemoryStats()
0217 {
0218     KIS_SAFE_ASSERT_RECOVER_RETURN(!isRunning());
0219 
0220     KisTileDataStoreReverseIterator *iter = m_store->beginReverseIteration();
0221     QList<KisTileData*> beggars;
0222     QList<KisTileData*> donors;
0223     qint32 memoryOccupied;
0224 
0225     qint32 statRealMemory;
0226     qint32 statHistoricalMemory;
0227 
0228 
0229     getLists(iter, beggars, donors,
0230              memoryOccupied,
0231              statRealMemory,
0232              statHistoricalMemory);
0233 
0234     m_lastPoolMemoryMetric = memoryOccupied;
0235     m_lastRealMemoryMetric = statRealMemory;
0236     m_lastHistoricalMemoryMetric = statHistoricalMemory;
0237 
0238     m_store->endIteration(iter);
0239 }
0240 
0241 qint64 KisTileDataPooler::lastPoolMemoryMetric() const
0242 {
0243     return m_lastPoolMemoryMetric;
0244 }
0245 
0246 qint64 KisTileDataPooler::lastRealMemoryMetric() const
0247 {
0248     return m_lastRealMemoryMetric;
0249 }
0250 
0251 qint64 KisTileDataPooler::lastHistoricalMemoryMetric() const
0252 {
0253     return m_lastHistoricalMemoryMetric;
0254 }
0255 
0256 inline int KisTileDataPooler::clonesMetric(KisTileData *td, int numClones) {
0257     return numClones * td->pixelSize();
0258 }
0259 
0260 inline int KisTileDataPooler::clonesMetric(KisTileData *td) {
0261     return td->m_clonesStack.size() * td->pixelSize();
0262 }
0263 
0264 inline void KisTileDataPooler::tryFreeOrphanedClones(KisTileData *td)
0265 {
0266     qint32 extraClones = -numClonesNeeded(td);
0267 
0268     if(extraClones > 0) {
0269         cloneTileData(td, -extraClones);
0270     }
0271 }
0272 
0273 inline qint32 KisTileDataPooler::needMemory(KisTileData *td)
0274 {
0275     qint32 clonesNeeded = !td->age() ? qMax(0, numClonesNeeded(td)) : 0;
0276     return clonesMetric(td, clonesNeeded);
0277 }
0278 
0279 inline qint32 KisTileDataPooler::canDonorMemory(KisTileData *td)
0280 {
0281     return td->age() && clonesMetric(td);
0282 }
0283 
0284 template<class Iter>
0285 void KisTileDataPooler::getLists(Iter *iter,
0286                                  QList<KisTileData*> &beggars,
0287                                  QList<KisTileData*> &donors,
0288                                  qint32 &memoryOccupied,
0289                                  qint32 &statRealMemory,
0290                                  qint32 &statHistoricalMemory)
0291 {
0292     memoryOccupied = 0;
0293     statRealMemory = 0;
0294     statHistoricalMemory = 0;
0295 
0296     qint32 needMemoryTotal = 0;
0297     qint32 canDonorMemoryTotal = 0;
0298 
0299     qint32 neededMemory;
0300     qint32 donoredMemory;
0301 
0302     KisTileData *item;
0303 
0304     while(iter->hasNext()) {
0305         item = iter->next();
0306 
0307         tryFreeOrphanedClones(item);
0308 
0309         if((neededMemory = needMemory(item))) {
0310             needMemoryTotal += neededMemory;
0311             beggars.append(item);
0312         }
0313         else if((donoredMemory = canDonorMemory(item))) {
0314             canDonorMemoryTotal += donoredMemory;
0315             donors.append(item);
0316         }
0317 
0318         memoryOccupied += clonesMetric(item);
0319 
0320         // statistics gathering
0321         if (item->historical()) {
0322             statHistoricalMemory += item->pixelSize();
0323         } else {
0324             statRealMemory += item->pixelSize();
0325         }
0326     }
0327 
0328     DEBUG_LISTS(memoryOccupied,
0329                 beggars, needMemoryTotal,
0330                 donors, canDonorMemoryTotal);
0331 }
0332 
0333 qint32 KisTileDataPooler::tryGetMemory(QList<KisTileData*> &donors,
0334                                        qint32 memoryMetric)
0335 {
0336     qint32 memoryFreed = 0;
0337 
0338     QMutableListIterator<KisTileData*> iter(donors);
0339     iter.toBack();
0340 
0341     while(iter.hasPrevious() && memoryFreed < memoryMetric) {
0342         KisTileData *item = iter.previous();
0343 
0344         qint32 numClones = item->m_clonesStack.size();
0345         cloneTileData(item, -numClones);
0346         memoryFreed += clonesMetric(item, numClones);
0347 
0348         iter.remove();
0349     }
0350 
0351     return memoryFreed;
0352 }
0353 
0354 bool KisTileDataPooler::processLists(QList<KisTileData*> &beggars,
0355                                      QList<KisTileData*> &donors,
0356                                      qint32 &memoryOccupied)
0357 {
0358     bool hadWork = false;
0359 
0360 
0361     Q_FOREACH (KisTileData *item, beggars) {
0362         qint32 clonesNeeded = numClonesNeeded(item);
0363         qint32 clonesMemory = clonesMetric(item, clonesNeeded);
0364 
0365         qint32 memoryLeft =
0366             m_memoryLimit - (memoryOccupied + clonesMemory);
0367 
0368         if(memoryLeft < 0) {
0369             qint32 freedMemory = tryGetMemory(donors, -memoryLeft);
0370             memoryOccupied -= freedMemory;
0371 
0372             DEBUG_FREE_CLONE(freedMemory, memoryLeft);
0373 
0374             if(m_memoryLimit < memoryOccupied + clonesMemory)
0375                 break;
0376         }
0377 
0378         cloneTileData(item, clonesNeeded);
0379         DEBUG_ALLOC_CLONE(clonesMemory, memoryOccupied);
0380 
0381         memoryOccupied += clonesMemory;
0382         hadWork = true;
0383     }
0384 
0385     return hadWork;
0386 }
0387 
0388 void KisTileDataPooler::debugTileStatistics()
0389 {
0390     /**
0391      * Assume we are called from the inside of the loop.
0392      * This means m_store is already locked
0393      */
0394 
0395     qint64 preallocatedTiles=0;
0396 
0397     KisTileDataStoreIterator *iter = m_store->beginIteration();
0398     KisTileData *item;
0399 
0400     while(iter->hasNext()) {
0401         item = iter->next();
0402         preallocatedTiles += item->m_clonesStack.size();
0403     }
0404 
0405     m_store->endIteration(iter);
0406 
0407     dbgKrita << "Tiles statistics:\t total:" << m_store->numTiles() << "\t preallocated:"<< preallocatedTiles;
0408 }
0409 
0410 void KisTileDataPooler::testingRereadConfig()
0411 {
0412     m_memoryLimit = MiB_TO_METRIC(KisImageConfig(true).poolLimit());
0413 }