File indexing completed on 2024-12-22 04:10:33
0001 /* 0002 * SPDX-FileCopyrightText: 2004 C. Boemann <cbo@boemann.dk> 0003 * SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include <QtGlobal> 0009 #include "kis_debug.h" 0010 #include "kis_global.h" 0011 0012 //#define SHARED_TILES_SANITY_CHECK 0013 0014 0015 template<class T> 0016 KisTileHashTableTraits<T>::KisTileHashTableTraits(KisMementoManager *mm) 0017 : m_lock(QReadWriteLock::NonRecursive) 0018 { 0019 m_hashTable = new TileTypeSP [TABLE_SIZE]; 0020 Q_CHECK_PTR(m_hashTable); 0021 0022 m_numTiles = 0; 0023 m_defaultTileData = 0; 0024 m_mementoManager = mm; 0025 } 0026 0027 template<class T> 0028 KisTileHashTableTraits<T>::KisTileHashTableTraits(const KisTileHashTableTraits<T> &ht, 0029 KisMementoManager *mm) 0030 : m_lock(QReadWriteLock::NonRecursive) 0031 { 0032 QReadLocker locker(&ht.m_lock); 0033 0034 m_mementoManager = mm; 0035 m_defaultTileData = 0; 0036 setDefaultTileDataImp(ht.m_defaultTileData); 0037 0038 m_hashTable = new TileTypeSP [TABLE_SIZE]; 0039 Q_CHECK_PTR(m_hashTable); 0040 0041 0042 TileTypeSP foreignTile; 0043 TileTypeSP nativeTile; 0044 TileTypeSP nativeTileHead; 0045 for (qint32 i = 0; i < TABLE_SIZE; i++) { 0046 nativeTileHead = 0; 0047 0048 foreignTile = ht.m_hashTable[i]; 0049 while (foreignTile) { 0050 nativeTile = TileTypeSP(new TileType(*foreignTile, m_mementoManager)); 0051 nativeTile->setNext(nativeTileHead); 0052 nativeTileHead = nativeTile; 0053 0054 foreignTile = foreignTile->next(); 0055 } 0056 0057 m_hashTable[i] = nativeTileHead; 0058 } 0059 m_numTiles = ht.m_numTiles; 0060 } 0061 0062 template<class T> 0063 KisTileHashTableTraits<T>::~KisTileHashTableTraits() 0064 { 0065 clear(); 0066 delete[] m_hashTable; 0067 setDefaultTileDataImp(0); 0068 } 0069 0070 template<class T> 0071 quint32 KisTileHashTableTraits<T>::calculateHash(qint32 col, qint32 row) 0072 { 0073 return ((row << 5) + (col & 0x1F)) & 0x3FF; 0074 } 0075 0076 template<class T> 0077 typename KisTileHashTableTraits<T>::TileTypeSP 0078 KisTileHashTableTraits<T>::getTileMinefieldWalk(qint32 col, qint32 row, qint32 idx) 0079 { 0080 // WARNING: this function is here only for educational purposes! Don't 0081 // use it! It causes race condition in a shared pointer copy-ctor 0082 // when accessing m_hashTable! 0083 0084 /** 0085 * This is a special method for dangerous and unsafe access to 0086 * the tiles table. Thanks to the fact that our shared pointers 0087 * are thread safe, we can iterate through the linked list without 0088 * having any locks help. In the worst case, we will miss the needed 0089 * tile. In that case, the higher level code will do the proper 0090 * locking and do the second try with all the needed locks held. 0091 */ 0092 0093 TileTypeSP headTile = m_hashTable[idx]; 0094 TileTypeSP tile = headTile; 0095 0096 for (; tile; tile = tile->next()) { 0097 if (tile->col() == col && 0098 tile->row() == row) { 0099 0100 if (m_hashTable[idx] != headTile) { 0101 tile.clear(); 0102 } 0103 0104 break; 0105 } 0106 } 0107 0108 return tile; 0109 } 0110 0111 template<class T> 0112 typename KisTileHashTableTraits<T>::TileTypeSP 0113 KisTileHashTableTraits<T>::getTile(qint32 col, qint32 row, qint32 idx) 0114 { 0115 TileTypeSP tile = m_hashTable[idx]; 0116 0117 for (; tile; tile = tile->next()) { 0118 if (tile->col() == col && 0119 tile->row() == row) { 0120 0121 return tile; 0122 } 0123 } 0124 0125 return TileTypeSP(); 0126 } 0127 0128 template<class T> 0129 void KisTileHashTableTraits<T>::linkTile(TileTypeSP tile, qint32 idx) 0130 { 0131 TileTypeSP firstTile = m_hashTable[idx]; 0132 0133 #ifdef SHARED_TILES_SANITY_CHECK 0134 Q_ASSERT_X(!tile->next(), "KisTileHashTableTraits<T>::linkTile", 0135 "A tile can't be shared by several hash tables, sorry."); 0136 #endif 0137 0138 tile->setNext(firstTile); 0139 m_hashTable[idx] = tile; 0140 m_numTiles++; 0141 } 0142 0143 template<class T> 0144 bool KisTileHashTableTraits<T>::unlinkTile(qint32 col, qint32 row, qint32 idx) 0145 { 0146 TileTypeSP tile = m_hashTable[idx]; 0147 TileTypeSP prevTile; 0148 0149 for (; tile; tile = tile->next()) { 0150 if (tile->col() == col && 0151 tile->row() == row) { 0152 0153 if (prevTile) 0154 prevTile->setNext(tile->next()); 0155 else 0156 /* optimize here*/ 0157 m_hashTable[idx] = tile->next(); 0158 0159 /** 0160 * The shared pointer may still be accessed by someone, so 0161 * we need to disconnects the tile from memento manager 0162 * explicitly 0163 */ 0164 tile->setNext(TileTypeSP()); 0165 tile->notifyDetachedFromDataManager(); 0166 tile.clear(); 0167 0168 m_numTiles--; 0169 return true; 0170 } 0171 prevTile = tile; 0172 } 0173 0174 return false; 0175 } 0176 0177 template<class T> 0178 inline void KisTileHashTableTraits<T>::setDefaultTileDataImp(KisTileData *defaultTileData) 0179 { 0180 if (m_defaultTileData) { 0181 m_defaultTileData->release(); 0182 m_defaultTileData = 0; 0183 } 0184 0185 if (defaultTileData) { 0186 defaultTileData->acquire(); 0187 m_defaultTileData = defaultTileData; 0188 } 0189 } 0190 0191 template<class T> 0192 inline KisTileData* KisTileHashTableTraits<T>::defaultTileDataImp() const 0193 { 0194 return m_defaultTileData; 0195 } 0196 0197 0198 template<class T> 0199 bool KisTileHashTableTraits<T>::tileExists(qint32 col, qint32 row) 0200 { 0201 return this->getExistingTile(col, row); 0202 } 0203 0204 template<class T> 0205 typename KisTileHashTableTraits<T>::TileTypeSP 0206 KisTileHashTableTraits<T>::getExistingTile(qint32 col, qint32 row) 0207 { 0208 const qint32 idx = calculateHash(col, row); 0209 0210 // NOTE: minefield walk is disabled due to supposed unsafety, 0211 // see bug 391270 0212 0213 QReadLocker locker(&m_lock); 0214 return getTile(col, row, idx); 0215 } 0216 0217 template<class T> 0218 typename KisTileHashTableTraits<T>::TileTypeSP 0219 KisTileHashTableTraits<T>::getTileLazy(qint32 col, qint32 row, 0220 bool& newTile) 0221 { 0222 const qint32 idx = calculateHash(col, row); 0223 0224 // NOTE: minefield walk is disabled due to supposed unsafety, 0225 // see bug 391270 0226 0227 newTile = false; 0228 TileTypeSP tile; 0229 0230 { 0231 QReadLocker locker(&m_lock); 0232 tile = getTile(col, row, idx); 0233 } 0234 0235 if (!tile) { 0236 QWriteLocker locker(&m_lock); 0237 tile = new TileType(col, row, m_defaultTileData, m_mementoManager); 0238 linkTile(tile, idx); 0239 newTile = true; 0240 } 0241 0242 return tile; 0243 } 0244 0245 template<class T> 0246 typename KisTileHashTableTraits<T>::TileTypeSP 0247 KisTileHashTableTraits<T>::getReadOnlyTileLazy(qint32 col, qint32 row, bool &existingTile) 0248 { 0249 const qint32 idx = calculateHash(col, row); 0250 0251 // NOTE: minefield walk is disabled due to supposed unsafety, 0252 // see bug 391270 0253 0254 QReadLocker locker(&m_lock); 0255 0256 TileTypeSP tile = getTile(col, row, idx); 0257 existingTile = tile; 0258 0259 if (!existingTile) { 0260 tile = new TileType(col, row, m_defaultTileData, 0); 0261 } 0262 0263 return tile; 0264 } 0265 0266 template<class T> 0267 void KisTileHashTableTraits<T>::addTile(TileTypeSP tile) 0268 { 0269 const qint32 idx = calculateHash(tile->col(), tile->row()); 0270 0271 QWriteLocker locker(&m_lock); 0272 linkTile(tile, idx); 0273 } 0274 0275 template<class T> 0276 bool KisTileHashTableTraits<T>::deleteTile(qint32 col, qint32 row) 0277 { 0278 const qint32 idx = calculateHash(col, row); 0279 0280 QWriteLocker locker(&m_lock); 0281 return unlinkTile(col, row, idx); 0282 } 0283 0284 template<class T> 0285 bool KisTileHashTableTraits<T>::deleteTile(TileTypeSP tile) 0286 { 0287 return deleteTile(tile->col(), tile->row()); 0288 } 0289 0290 template<class T> 0291 void KisTileHashTableTraits<T>::clear() 0292 { 0293 QWriteLocker locker(&m_lock); 0294 TileTypeSP tile = TileTypeSP(); 0295 qint32 i; 0296 0297 for (i = 0; i < TABLE_SIZE; i++) { 0298 tile = m_hashTable[i]; 0299 0300 while (tile) { 0301 TileTypeSP tmp = tile; 0302 tile = tile->next(); 0303 0304 /** 0305 * About disconnection of tiles see a comment in unlinkTile() 0306 */ 0307 0308 tmp->setNext(TileTypeSP()); 0309 tmp->notifyDetachedFromDataManager(); 0310 tmp = 0; 0311 0312 m_numTiles--; 0313 } 0314 0315 m_hashTable[i] = 0; 0316 } 0317 0318 Q_ASSERT(!m_numTiles); 0319 } 0320 0321 template<class T> 0322 void KisTileHashTableTraits<T>::setDefaultTileData(KisTileData *defaultTileData) 0323 { 0324 QWriteLocker locker(&m_lock); 0325 setDefaultTileDataImp(defaultTileData); 0326 } 0327 0328 template<class T> 0329 KisTileData* KisTileHashTableTraits<T>::defaultTileData() const 0330 { 0331 QWriteLocker locker(&m_lock); 0332 return defaultTileDataImp(); 0333 } 0334 0335 template <class T> 0336 inline KisTileData* KisTileHashTableTraits<T>::refAndFetchDefaultTileData() const 0337 { 0338 QWriteLocker locker(&m_lock); 0339 KisTileData *result = defaultTileDataImp(); 0340 result->ref(); 0341 return result; 0342 } 0343 0344 0345 /*************** Debugging stuff ***************/ 0346 0347 template<class T> 0348 void KisTileHashTableTraits<T>::debugPrintInfo() 0349 { 0350 if (!m_numTiles) return; 0351 0352 qInfo() << "==========================\n" 0353 << "TileHashTable:" 0354 << "\n def. data:\t\t" << m_defaultTileData 0355 << "\n numTiles:\t\t" << m_numTiles; 0356 debugListLengthDistribution(); 0357 qInfo() << "==========================\n"; 0358 } 0359 0360 template<class T> 0361 qint32 KisTileHashTableTraits<T>::debugChainLen(qint32 idx) 0362 { 0363 qint32 len = 0; 0364 for (TileTypeSP it = m_hashTable[idx]; it; it = it->next(), len++) ; 0365 return len; 0366 } 0367 0368 template<class T> 0369 void KisTileHashTableTraits<T>::debugMaxListLength(qint32 &min, qint32 &max) 0370 { 0371 TileTypeSP tile; 0372 qint32 maxLen = 0; 0373 qint32 minLen = m_numTiles; 0374 qint32 tmp = 0; 0375 0376 for (qint32 i = 0; i < TABLE_SIZE; i++) { 0377 tmp = debugChainLen(i); 0378 if (tmp > maxLen) 0379 maxLen = tmp; 0380 if (tmp < minLen) 0381 minLen = tmp; 0382 } 0383 0384 min = minLen; 0385 max = maxLen; 0386 } 0387 0388 template<class T> 0389 void KisTileHashTableTraits<T>::debugListLengthDistribution() 0390 { 0391 qint32 min, max; 0392 qint32 arraySize; 0393 qint32 tmp; 0394 0395 debugMaxListLength(min, max); 0396 arraySize = max - min + 1; 0397 0398 qint32 *array = new qint32[arraySize]; 0399 memset(array, 0, sizeof(qint32)*arraySize); 0400 0401 for (qint32 i = 0; i < TABLE_SIZE; i++) { 0402 tmp = debugChainLen(i); 0403 array[tmp-min]++; 0404 } 0405 0406 qInfo() << QString(" minChain: %1\n").arg(min); 0407 qInfo() << QString(" maxChain: %1\n").arg(max); 0408 0409 qInfo() << " Chain size distribution:"; 0410 for (qint32 i = 0; i < arraySize; i++) 0411 qInfo() << QString(" %1: %2").arg(i + min).arg(array[i]); 0412 0413 delete[] array; 0414 } 0415 0416 template<class T> 0417 void KisTileHashTableTraits<T>::sanityChecksumCheck() 0418 { 0419 /** 0420 * We assume that the lock should have already been taken 0421 * by the code that was going to change the table 0422 */ 0423 Q_ASSERT(!m_lock.tryLockForWrite()); 0424 0425 TileTypeSP tile = 0; 0426 qint32 exactNumTiles = 0; 0427 0428 for (qint32 i = 0; i < TABLE_SIZE; i++) { 0429 tile = m_hashTable[i]; 0430 while (tile) { 0431 exactNumTiles++; 0432 tile = tile->next(); 0433 } 0434 } 0435 0436 if (exactNumTiles != m_numTiles) { 0437 dbgKrita << "Sanity check failed!"; 0438 dbgKrita << ppVar(exactNumTiles); 0439 dbgKrita << ppVar(m_numTiles); 0440 dbgKrita << "Wrong tiles checksum!"; 0441 Q_ASSERT(0); // not fatalKrita for a backtrace support 0442 } 0443 }