diff --git a/libs/image/tiles3/kis_tile_data_store.cc b/libs/image/tiles3/kis_tile_data_store.cc index f3313094b6..9a3efc7181 100644 --- a/libs/image/tiles3/kis_tile_data_store.cc +++ b/libs/image/tiles3/kis_tile_data_store.cc @@ -1,397 +1,371 @@ /* * Copyright (c) 2009 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // to disable assert when the leak tracker is active #include "config-memory-leak-tracker.h" #include #include "kis_tile_data_store.h" #include "kis_tile_data.h" #include "kis_debug.h" #include "kis_tile_data_store_iterators.h" Q_GLOBAL_STATIC(KisTileDataStore, s_instance) //#define DEBUG_PRECLONE #ifdef DEBUG_PRECLONE #include #define DEBUG_PRECLONE_ACTION(action, oldTD, newTD) \ printf("!!! %s:\t\t\t 0x%X -> 0x%X \t\t!!!\n", \ action, (quintptr)oldTD, (quintptr) newTD) #define DEBUG_FREE_ACTION(td) \ printf("Tile data free'd \t(0x%X)\n", td) #else #define DEBUG_PRECLONE_ACTION(action, oldTD, newTD) #define DEBUG_FREE_ACTION(td) #endif #ifdef DEBUG_HIT_MISS qint64 __preclone_miss = 0; qint64 __preclone_hit = 0; qint64 __preclone_miss_user_count = 0; qint64 __preclone_miss_age = 0; #define DEBUG_COUNT_PRECLONE_HIT(td) __preclone_hit++ #define DEBUG_COUNT_PRECLONE_MISS(td) __preclone_miss++; __preclone_miss_user_count+=td->numUsers(); __preclone_miss_age+=td->age() #define DEBUG_REPORT_PRECLONE_EFFICIENCY() \ dbgKrita << "Hits:" << __preclone_hit \ << "of" << __preclone_hit + __preclone_miss \ << "(" \ << qreal(__preclone_hit) / (__preclone_hit + __preclone_miss) \ << ")" \ << "miss users" << qreal(__preclone_miss_user_count) / __preclone_miss \ << "miss age" << qreal(__preclone_miss_age) / __preclone_miss #else #define DEBUG_COUNT_PRECLONE_HIT(td) #define DEBUG_COUNT_PRECLONE_MISS(td) #define DEBUG_REPORT_PRECLONE_EFFICIENCY() #endif KisTileDataStore::KisTileDataStore() : m_pooler(this), m_swapper(this), m_numTiles(0), m_memoryMetric(0), m_counter(1), m_clockIndex(1) { -// m_clockIterator = m_tileDataList.end(); m_pooler.start(); m_swapper.start(); } KisTileDataStore::~KisTileDataStore() { m_pooler.terminatePooler(); m_swapper.terminateSwapper(); if (numTiles() > 0) { errKrita << "Warning: some tiles have leaked:"; errKrita << "\tTiles in memory:" << numTilesInMemory() << "\n" << "\tTotal tiles:" << numTiles(); } } KisTileDataStore* KisTileDataStore::instance() { return s_instance; } KisTileDataStore::MemoryStatistics KisTileDataStore::memoryStatistics() { // in case the pooler is disabled, we should force it // to update the stats if (!m_pooler.isRunning()) { m_pooler.forceUpdateMemoryStats(); } -// QMutexLocker lock(&m_listLock); QReadLocker lock(&m_iteratorLock); MemoryStatistics stats; const qint64 metricCoeff = KisTileData::WIDTH * KisTileData::HEIGHT; stats.realMemorySize = m_pooler.lastRealMemoryMetric() * metricCoeff; stats.historicalMemorySize = m_pooler.lastHistoricalMemoryMetric() * metricCoeff; stats.poolSize = m_pooler.lastPoolMemoryMetric() * metricCoeff; stats.totalMemorySize = memoryMetric() * metricCoeff + stats.poolSize; stats.swapSize = m_swappedStore.totalMemoryMetric() * metricCoeff; return stats; } inline void KisTileDataStore::registerTileDataImp(KisTileData *td) { -// td->m_listIterator = m_tileDataList.insert(m_tileDataList.end(), td); -// m_numTiles++; -// m_memoryMetric += td->pixelSize(); int index = m_counter.fetchAndAddOrdered(1); td->m_tileNumber = index; m_tileDataMap.assign(index, td); m_numTiles.ref(); m_memoryMetric += td->pixelSize(); } void KisTileDataStore::registerTileData(KisTileData *td) { -// QMutexLocker lock(&m_listLock); QReadLocker lock(&m_iteratorLock); registerTileDataImp(td); } inline void KisTileDataStore::unregisterTileDataImp(KisTileData *td) { -// KisTileDataListIterator tempIterator = td->m_listIterator; - -// if(m_clockIterator == tempIterator) { -// m_clockIterator = tempIterator + 1; -// } - -// td->m_listIterator = m_tileDataList.end(); -// m_tileDataList.erase(tempIterator); -// m_numTiles--; -// m_memoryMetric -= td->pixelSize(); if (m_clockIndex == td->m_tileNumber) { do { m_clockIndex.ref(); } while (!m_tileDataMap.get(m_clockIndex.loadAcquire()) && m_clockIndex < m_counter); } int index = td->m_tileNumber; td->m_tileNumber = -1; m_tileDataMap.erase(index); m_numTiles.deref(); m_memoryMetric -= td->pixelSize(); } void KisTileDataStore::unregisterTileData(KisTileData *td) { -// QMutexLocker lock(&m_listLock); QReadLocker lock(&m_iteratorLock); unregisterTileDataImp(td); } KisTileData *KisTileDataStore::allocTileData(qint32 pixelSize, const quint8 *defPixel) { KisTileData *td = new KisTileData(pixelSize, defPixel, this); registerTileData(td); return td; } KisTileData *KisTileDataStore::duplicateTileData(KisTileData *rhs) { KisTileData *td = 0; if (rhs->m_clonesStack.pop(td)) { DEBUG_PRECLONE_ACTION("+ Pre-clone HIT", rhs, td); DEBUG_COUNT_PRECLONE_HIT(rhs); } else { rhs->blockSwapping(); td = new KisTileData(*rhs); rhs->unblockSwapping(); DEBUG_PRECLONE_ACTION("- Pre-clone #MISS#", rhs, td); DEBUG_COUNT_PRECLONE_MISS(rhs); } registerTileData(td); return td; } void KisTileDataStore::freeTileData(KisTileData *td) { Q_ASSERT(td->m_store == this); DEBUG_FREE_ACTION(td); -// m_listLock.lock(); m_iteratorLock.lockForRead(); td->m_swapLock.lockForWrite(); if (!td->data()) { m_swappedStore.forgetTileData(td); } else { unregisterTileDataImp(td); } td->m_swapLock.unlock(); m_iteratorLock.unlock(); -// m_listLock.unlock(); delete td; } void KisTileDataStore::ensureTileDataLoaded(KisTileData *td) { // dbgKrita << "#### SWAP MISS! ####" << td << ppVar(td->mementoed()) << ppVar(td->age()) << ppVar(td->numUsers()); checkFreeMemory(); td->m_swapLock.lockForRead(); while (!td->data()) { td->m_swapLock.unlock(); /** * The order of this heavy locking is very important. * Change it only in case, you really know what you are doing. */ -// m_listLock.lock(); m_iteratorLock.lockForRead(); /** * If someone has managed to load the td from swap, then, most * probably, they have already taken the swap lock. This may * lead to a deadlock, because COW mechanism breaks lock * ordering rules in duplicateTileData() (it takes m_listLock * while the swap lock is held). In our case it is enough just * to check whether the other thread has already fetched the * data. Please notice that we do not take both of the locks * while checking this, because holding m_listLock is * enough. Nothing can happen to the tile while we hold * m_listLock. */ if (!td->data()) { td->m_swapLock.lockForWrite(); m_swappedStore.swapInTileData(td); registerTileDataImp(td); td->m_swapLock.unlock(); } m_iteratorLock.unlock(); -// m_listLock.unlock(); /** * <-- In theory, livelock is possible here... */ td->m_swapLock.lockForRead(); } } bool KisTileDataStore::trySwapTileData(KisTileData *td) { /** * This function is called with m_listLock acquired */ bool result = false; if (!td->m_swapLock.tryLockForWrite()) return result; if (td->data()) { unregisterTileDataImp(td); if (m_swappedStore.trySwapOutTileData(td)) { result = true; } else { result = false; registerTileDataImp(td); } } td->m_swapLock.unlock(); return result; } KisTileDataStoreIterator* KisTileDataStore::beginIteration() { -// m_listLock.lock(); -// return new KisTileDataStoreIterator(m_tileDataList, this); m_iteratorLock.lockForWrite(); return new KisTileDataStoreIterator(m_tileDataMap, this); } void KisTileDataStore::endIteration(KisTileDataStoreIterator* iterator) { delete iterator; m_iteratorLock.unlock(); -// m_listLock.unlock(); } KisTileDataStoreReverseIterator* KisTileDataStore::beginReverseIteration() { -// m_listLock.lock(); -// return new KisTileDataStoreReverseIterator(m_tileDataList, this); m_iteratorLock.lockForWrite(); return new KisTileDataStoreReverseIterator(m_tileDataMap, this); } void KisTileDataStore::endIteration(KisTileDataStoreReverseIterator* iterator) { delete iterator; m_iteratorLock.unlock(); -// m_listLock.unlock(); DEBUG_REPORT_PRECLONE_EFFICIENCY(); } KisTileDataStoreClockIterator* KisTileDataStore::beginClockIteration() { -// m_listLock.lock(); -// return new KisTileDataStoreClockIterator(m_clockIterator, m_tileDataList, this); m_iteratorLock.lockForWrite(); return new KisTileDataStoreClockIterator(m_tileDataMap, m_clockIndex.loadAcquire(), this); } void KisTileDataStore::endIteration(KisTileDataStoreClockIterator* iterator) { m_clockIndex = iterator->getFinalPosition(); delete iterator; m_iteratorLock.unlock(); -// m_listLock.unlock(); } void KisTileDataStore::debugPrintList() { -// KisTileData *item; -// Q_FOREACH (item, m_tileDataList) { -// dbgTiles << "-------------------------\n" -// << "TileData:\t\t\t" << item -// << "\n refCount:\t" << item->m_refCount; -// } + ConcurrentMap::Iterator iter(m_tileDataMap); + KisTileData *item = 0; + + while (iter.isValid()) { + item = iter.getValue(); + dbgTiles << "-------------------------\n" + << "TileData:\t\t\t" << item + << "\n refCount:\t" << item->m_refCount; + } } void KisTileDataStore::debugSwapAll() { KisTileDataStoreIterator* iter = beginIteration(); KisTileData *item; while (iter->hasNext()) { item = iter->next(); iter->trySwapOut(item); } endIteration(iter); // dbgKrita << "Number of tiles:" << numTiles(); // dbgKrita << "Tiles in memory:" << numTilesInMemory(); // m_swappedStore.debugStatistics(); } void KisTileDataStore::debugClear() { -// QMutexLocker lock(&m_listLock); + QWriteLocker l(&m_iteratorLock); + ConcurrentMap::Iterator iter(m_tileDataMap); -// Q_FOREACH (KisTileData *item, m_tileDataList) { -// delete item; -// } - -// m_tileDataList.clear(); -// m_clockIterator = m_tileDataList.end(); + while (iter.isValid()) { + delete iter.getValue(); + iter.next(); + } -// m_numTiles = 0; -// m_memoryMetric = 0; + m_counter = 1; + m_clockIndex = 1; + m_numTiles = 0; + m_memoryMetric = 0; } void KisTileDataStore::testingRereadConfig() { m_pooler.testingRereadConfig(); m_swapper.testingRereadConfig(); kickPooler(); } void KisTileDataStore::testingSuspendPooler() { m_pooler.terminatePooler(); } void KisTileDataStore::testingResumePooler() { m_pooler.start(); } diff --git a/libs/image/tiles3/kis_tile_data_store.h b/libs/image/tiles3/kis_tile_data_store.h index 88b7ac3034..e7768d0988 100644 --- a/libs/image/tiles3/kis_tile_data_store.h +++ b/libs/image/tiles3/kis_tile_data_store.h @@ -1,200 +1,190 @@ /* * Copyright (c) 2009 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TILE_DATA_STORE_H_ #define KIS_TILE_DATA_STORE_H_ #include "kritaimage_export.h" #include #include "kis_tile_data_interface.h" #include "kis_tile_data_pooler.h" #include "swap/kis_tile_data_swapper.h" #include "swap/kis_swapped_data_store.h" #include "3rdparty/lock_free_map/concurrent_map.h" class KisTileDataStoreIterator; class KisTileDataStoreReverseIterator; class KisTileDataStoreClockIterator; /** * Stores tileData objects. When needed compresses them and swaps. */ class KRITAIMAGE_EXPORT KisTileDataStore { public: KisTileDataStore(); ~KisTileDataStore(); static KisTileDataStore* instance(); void debugPrintList(); struct MemoryStatistics { qint64 totalMemorySize; qint64 realMemorySize; qint64 historicalMemorySize; qint64 poolSize; qint64 swapSize; }; MemoryStatistics memoryStatistics(); /** * Returns total number of tiles present: in memory * or in a swap file */ inline qint32 numTiles() const { -// return m_numTiles + m_swappedStore.numTiles(); return m_numTiles.loadAcquire() + m_swappedStore.numTiles(); } /** * Returns the number of tiles present in memory only */ inline qint32 numTilesInMemory() const { -// return m_numTiles; return m_numTiles.loadAcquire(); } inline void checkFreeMemory() { m_swapper.checkFreeMemory(); } /** * \see m_memoryMetric */ inline qint64 memoryMetric() const { -// return m_memoryMetric; return m_memoryMetric.loadAcquire(); } KisTileDataStoreIterator* beginIteration(); void endIteration(KisTileDataStoreIterator* iterator); KisTileDataStoreReverseIterator* beginReverseIteration(); void endIteration(KisTileDataStoreReverseIterator* iterator); KisTileDataStoreClockIterator* beginClockIteration(); void endIteration(KisTileDataStoreClockIterator* iterator); inline KisTileData* createDefaultTileData(qint32 pixelSize, const quint8 *defPixel) { return allocTileData(pixelSize, defPixel); } // Called by The Memento Manager after every commit inline void kickPooler() { m_pooler.kick(); //FIXME: maybe, rename a function? m_swapper.kick(); } /** * Try swap out the tile data. * It may fail in case the tile is being accessed * at the same moment of time. */ bool trySwapTileData(KisTileData *td); /** * WARN: The following three method are only for usage * in KisTileData. Do not call them directly! */ KisTileData *duplicateTileData(KisTileData *rhs); void freeTileData(KisTileData *td); /** * Ensures that the tile data is totally present in memory * and it's swapping is blocked by holding td->m_swapLock * in a read mode. * PRECONDITIONS: td->m_swapLock is *unlocked* * m_listRWLock is *unlocked* * POSTCONDITIONS: td->m_data is in memory and * td->m_swapLock is locked * m_listRWLock is unlocked */ void ensureTileDataLoaded(KisTileData *td); private: KisTileData *allocTileData(qint32 pixelSize, const quint8 *defPixel); void registerTileData(KisTileData *td); void unregisterTileData(KisTileData *td); inline void registerTileDataImp(KisTileData *td); inline void unregisterTileDataImp(KisTileData *td); void freeRegisteredTiles(); friend class DeadlockyThread; friend class KisLowMemoryTests; void debugSwapAll(); void debugClear(); friend class KisTiledDataManagerTest; void testingSuspendPooler(); void testingResumePooler(); friend class KisLowMemoryBenchmark; void testingRereadConfig(); private: KisTileDataPooler m_pooler; KisTileDataSwapper m_swapper; friend class KisTileDataStoreTest; friend class KisTileDataPoolerTest; KisSwappedDataStore m_swappedStore; -// KisTileDataListIterator m_clockIterator; - -// QMutex m_listLock; -// KisTileDataList m_tileDataList; -// qint32 m_numTiles; - /** * This metric is used for computing the volume * of memory occupied by tile data objects. * metric = num_bytes / (KisTileData::WIDTH * KisTileData::HEIGHT) */ -// qint64 m_memoryMetric; QAtomicInt m_numTiles; QAtomicInt m_memoryMetric; QAtomicInt m_counter; QAtomicInt m_clockIndex; ConcurrentMap m_tileDataMap; QReadWriteLock m_iteratorLock; }; template inline T MiB_TO_METRIC(T value) { unsigned long long __MiB = 1ULL << 20; return value * (__MiB / (KisTileData::WIDTH * KisTileData::HEIGHT)); } #endif /* KIS_TILE_DATA_STORE_H_ */ diff --git a/libs/image/tiles3/kis_tile_data_store_iterators.h b/libs/image/tiles3/kis_tile_data_store_iterators.h index 1f11ba03d9..f0cf1d8421 100644 --- a/libs/image/tiles3/kis_tile_data_store_iterators.h +++ b/libs/image/tiles3/kis_tile_data_store_iterators.h @@ -1,324 +1,163 @@ /* * Copyright (c) 2010 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TILE_DATA_STORE_ITERATORS_H_ #define KIS_TILE_DATA_STORE_ITERATORS_H_ #include "kis_tile_data.h" #include "kis_debug.h" /** * KisTileDataStoreIterator, * KisTileDataStoreReverseIterator, * KisTileDataStoreClockIterator * - are general iterators for the contents of KisTileDataStore. * The store starts holding a lock when returns one of such * iterators, so no one will be able to change the list while * you are iterating. * * But be careful! You can't change the list while iterating either, * because it can invalidate the iterator. This is a general rule. */ class KisTileDataStoreIterator { -//public: -// KisTileDataStoreIterator(KisTileDataList &list, KisTileDataStore *store) -// : m_list(list), -// m_store(store) -// { -// m_iterator = m_list.begin(); -// m_end = m_list.end(); -// } - -// inline KisTileData* peekNext() { -// return *m_iterator; -// } - -// inline KisTileData* next() { -// return *(m_iterator++); -// } - -// inline bool hasNext() const { -// return m_iterator != m_end; -// } - -// inline bool trySwapOut(KisTileData *td) { -// if(td->m_listIterator == m_iterator) -// m_iterator++; - -// return m_store->trySwapTileData(td); -// } - -//private: -// KisTileDataList &m_list; -// KisTileDataListIterator m_iterator; -// KisTileDataListIterator m_end; -// KisTileDataStore *m_store; public: KisTileDataStoreIterator(ConcurrentMap &map, KisTileDataStore *store) : m_map(map), m_store(store) { m_iterator.setMap(m_map); } inline KisTileData* peekNext() { return m_iterator.getValue(); } inline KisTileData* next() { KisTileData *current = m_iterator.getValue(); m_iterator.next(); return current; } inline bool hasNext() const { return m_iterator.isValid(); } inline bool trySwapOut(KisTileData *td) { if (td == m_iterator.getValue()) { m_iterator.next(); } return m_store->trySwapTileData(td); } private: ConcurrentMap &m_map; ConcurrentMap::Iterator m_iterator; KisTileDataStore *m_store; }; -class KisTileDataStoreReverseIterator +class KisTileDataStoreReverseIterator : public KisTileDataStoreIterator { -//public: -// KisTileDataStoreReverseIterator(KisTileDataList &list, KisTileDataStore *store) -// : m_list(list), -// m_store(store) -// { -// m_iterator = m_list.end(); -// m_begin = m_list.begin(); -// } - -// inline KisTileData* peekNext() { -// return *(m_iterator-1); -// } - -// inline KisTileData* next() { -// return *(--m_iterator); -// } - -// inline bool hasNext() const { -// return m_iterator != m_begin; -// } - -// inline bool trySwapOut(KisTileData *td) { -// if(td->m_listIterator == m_iterator) -// m_iterator++; - -// return m_store->trySwapTileData(td); -// } - -//private: -// KisTileDataList &m_list; -// KisTileDataListIterator m_iterator; -// KisTileDataListIterator m_begin; -// KisTileDataStore *m_store; public: KisTileDataStoreReverseIterator(ConcurrentMap &map, KisTileDataStore *store) - : m_map(map), - m_store(store) - { - m_iterator.setMap(m_map); - } - - inline KisTileData* peekNext() - { - return m_iterator.getValue(); - } - - inline KisTileData* next() - { - KisTileData *current = m_iterator.getValue(); - m_iterator.next(); - return current; - } - - inline bool hasNext() const - { - return m_iterator.isValid(); - } - - inline bool trySwapOut(KisTileData *td) + : KisTileDataStoreIterator(map, store) { - if (td == m_iterator.getValue()) { - m_iterator.next(); - } - - return m_store->trySwapTileData(td); } - -private: - ConcurrentMap &m_map; - ConcurrentMap::Iterator m_iterator; - KisTileDataStore *m_store; }; class KisTileDataStoreClockIterator { -//public: -// KisTileDataStoreClockIterator(KisTileDataListIterator startItem, KisTileDataList &list, KisTileDataStore *store) -// : m_list(list), -// m_store(store) -// { -// m_end = m_list.end(); - -// if(startItem == m_list.begin() || -// startItem == m_end) { -// m_iterator = m_list.begin(); -// m_startItem = m_end; -// m_endReached = true; -// } -// else { -// m_startItem = startItem; -// m_iterator = startItem; -// m_endReached = false; -// } -// } - -// inline KisTileData* peekNext() { -// if(m_iterator == m_end) { -// m_iterator = m_list.begin(); -// m_endReached = true; -// } - -// return *m_iterator; -// } - -// inline KisTileData* next() { -// if(m_iterator == m_end) { -// m_iterator = m_list.begin(); -// m_endReached = true; -// } - -// return *(m_iterator++); -// } - -// inline bool hasNext() const { -// return !(m_endReached && m_iterator == m_startItem); -// } - -// inline bool trySwapOut(KisTileData *td) { -// if(td->m_listIterator == m_iterator) -// m_iterator++; - -// return m_store->trySwapTileData(td); -// } - -//private: -// friend class KisTileDataStore; -// inline KisTileDataListIterator getFinalPosition() { -// return m_iterator; -// } - -//private: -// KisTileDataList &m_list; -// bool m_endReached; -// KisTileDataListIterator m_iterator; -// KisTileDataListIterator m_startItem; -// KisTileDataListIterator m_end; -// KisTileDataStore *m_store; public: KisTileDataStoreClockIterator(ConcurrentMap &map, int startIndex, KisTileDataStore *store) : m_map(map), m_store(store) { m_iterator.setMap(m_map); m_startItem = m_map.get(startIndex); if (m_iterator.getValue() == m_startItem || !m_startItem) { m_endReached = true; } else { while (m_iterator.getValue() != m_startItem) { m_iterator.next(); } m_endReached = false; } } inline KisTileData* peekNext() { if (!m_iterator.isValid()) { m_iterator.setMap(m_map); m_endReached = true; } return m_iterator.getValue(); } inline KisTileData* next() { if (!m_iterator.isValid()) { m_iterator.setMap(m_map); m_endReached = true; } KisTileData *current = m_iterator.getValue(); m_iterator.next(); return current; } inline bool hasNext() const { return !(m_endReached && m_iterator.getValue() == m_startItem); } inline bool trySwapOut(KisTileData *td) { if (td == m_iterator.getValue()) { m_iterator.next(); } return m_store->trySwapTileData(td); } private: friend class KisTileDataStore; inline int getFinalPosition() { return m_iterator.getValue()->m_tileNumber; } private: ConcurrentMap &m_map; ConcurrentMap::Iterator m_iterator; KisTileData *m_startItem; bool m_endReached; KisTileDataStore *m_store; }; #endif /* KIS_TILE_DATA_STORE_ITERATORS_H_ */