File indexing completed on 2024-12-22 04:10:31
0001 /* 0002 * SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include <QtGlobal> 0008 #include "kis_memento_manager.h" 0009 #include "kis_memento.h" 0010 0011 0012 //#define DEBUG_MM 0013 0014 #ifdef DEBUG_MM 0015 #define DEBUG_LOG_TILE_ACTION(action, tile, col, row) \ 0016 printf("### MementoManager (0x%X): %s " \ 0017 "\ttile:\t0x%X (%d, %d) ###\n", (quintptr)this, action, \ 0018 (quintptr)tile, col, row) 0019 0020 #define DEBUG_LOG_SIMPLE_ACTION(action) \ 0021 printf("### MementoManager (0x%X): %s\n", (quintptr)this, action) 0022 0023 #define DEBUG_DUMP_MESSAGE(action) do { \ 0024 printf("\n### MementoManager (0x%X): %s \t\t##########\n", \ 0025 (quintptr)this, action); \ 0026 debugPrintInfo(); \ 0027 printf("##################################################################\n\n"); \ 0028 } while(0) 0029 #else 0030 0031 #define DEBUG_LOG_TILE_ACTION(action, tile, col, row) 0032 #define DEBUG_LOG_SIMPLE_ACTION(action) 0033 #define DEBUG_DUMP_MESSAGE(action) 0034 #endif 0035 0036 0037 /** 0038 * The class is supposed to store the changes of the paint device 0039 * it is associated with. The history of changes is presented in form 0040 * of transactions (revisions). If you purge the history of one 0041 * transaction (revision) with purgeHistory() we won't be able to undo 0042 * the changes made by this transactions. 0043 * 0044 * The Memento Manager can be in two states: 0045 * - Named Transaction is in progress - it means the caller 0046 * has explicitly requested creation of a new transaction. 0047 * The handle for the transaction is stored on a side of 0048 * the caller. And the history will be automatically purged 0049 * when the handler dies. 0050 * - Anonymous Transaction is in progress - the caller isn't 0051 * bothered about transactions at all. We pretend as we do 0052 * not support any versioning and do not have any historical 0053 * information. The history of such transactions is not purged 0054 * automatically, but it is free'd when younger named transaction 0055 * is purged. 0056 */ 0057 0058 #define blockRegistration() (m_registrationBlocked = true) 0059 #define unblockRegistration() (m_registrationBlocked = false) 0060 #define registrationBlocked() (m_registrationBlocked) 0061 0062 #define namedTransactionInProgress() ((bool)m_currentMemento) 0063 0064 KisMementoManager::KisMementoManager() 0065 : m_index(0), 0066 m_headsHashTable(0), 0067 m_registrationBlocked(false) 0068 { 0069 /** 0070 * Tile change/delete registration is enabled for all 0071 * devices by default. It can't be delayed. 0072 */ 0073 } 0074 0075 KisMementoManager::KisMementoManager(const KisMementoManager& rhs) 0076 : m_index(rhs.m_index, 0), 0077 m_revisions(rhs.m_revisions), 0078 m_cancelledRevisions(rhs.m_cancelledRevisions), 0079 m_headsHashTable(rhs.m_headsHashTable, 0), 0080 m_currentMemento(rhs.m_currentMemento), 0081 m_registrationBlocked(rhs.m_registrationBlocked) 0082 { 0083 Q_ASSERT_X(!m_registrationBlocked, 0084 "KisMementoManager", "(impossible happened) " 0085 "The device has been copied while registration was blocked"); 0086 } 0087 0088 KisMementoManager::~KisMementoManager() 0089 { 0090 // Nothing to be done here. Happily... 0091 // Everything is done by QList and KisSharedPtr... 0092 DEBUG_LOG_SIMPLE_ACTION("died\n"); 0093 } 0094 0095 /** 0096 * NOTE: We don't assume that the registerTileChange/Delete 0097 * can be called once a commit only. Reverse can happen when we 0098 * do sequential clears of the device. In such a case the tiles 0099 * will be removed and added several times during a commit. 0100 * 0101 * TODO: There is an 'uncomfortable' state for the tile possible 0102 * 1) Imagine we have a clear device 0103 * 2) Then we painted something in a tile 0104 * 3) It registered itself using registerTileChange() 0105 * 4) Then we called clear() and getMemento() [==commit()] 0106 * 5) The tile will be registered as deleted and successfully 0107 * committed to a revision. That means the states of the memento 0108 * manager at stages 1 and 5 do not coincide. 0109 * This will not lead to any memory leaks or bugs seen, it just 0110 * not good from a theoretical perspective. 0111 */ 0112 0113 void KisMementoManager::registerTileChange(KisTile *tile) 0114 { 0115 if (registrationBlocked()) return; 0116 0117 DEBUG_LOG_TILE_ACTION("reg. [C]", tile, tile->col(), tile->row()); 0118 0119 KisMementoItemSP mi = m_index.getExistingTile(tile->col(), tile->row()); 0120 0121 if(!mi) { 0122 mi = new KisMementoItem(); 0123 mi->changeTile(tile); 0124 m_index.addTile(mi); 0125 0126 if(namedTransactionInProgress()) { 0127 m_currentMemento->updateExtent(mi->col(), mi->row(), &m_currentMementoExtentLock); 0128 } 0129 } 0130 else { 0131 mi->reset(); 0132 mi->changeTile(tile); 0133 } 0134 } 0135 0136 void KisMementoManager::registerTileDeleted(KisTile *tile) 0137 { 0138 if (registrationBlocked()) return; 0139 0140 DEBUG_LOG_TILE_ACTION("reg. [D]", tile, tile->col(), tile->row()); 0141 0142 KisMementoItemSP mi = m_index.getExistingTile(tile->col(), tile->row()); 0143 0144 if(!mi) { 0145 mi = new KisMementoItem(); 0146 0147 KisTileData *defaultTileData = m_headsHashTable.refAndFetchDefaultTileData(); 0148 mi->deleteTile(tile, defaultTileData); 0149 defaultTileData->deref(); 0150 0151 m_index.addTile(mi); 0152 0153 if(namedTransactionInProgress()) { 0154 m_currentMemento->updateExtent(mi->col(), mi->row(), &m_currentMementoExtentLock); 0155 } 0156 } 0157 else { 0158 mi->reset(); 0159 0160 KisTileData *defaultTileData = m_headsHashTable.refAndFetchDefaultTileData(); 0161 mi->deleteTile(tile, defaultTileData); 0162 defaultTileData->deref(); 0163 } 0164 } 0165 0166 void KisMementoManager::commit() 0167 { 0168 if (m_index.isEmpty()) { 0169 if(namedTransactionInProgress()) { 0170 //warnTiles << "Named Transaction is empty"; 0171 /** 0172 * We still need to continue commit, because 0173 * a named transaction may be reverted by the user 0174 */ 0175 } 0176 else { 0177 m_currentMemento = 0; 0178 return; 0179 } 0180 } 0181 0182 KisMementoItemList revisionList; 0183 KisMementoItemSP mi; 0184 KisMementoItemSP parentMI; 0185 bool newTile; 0186 0187 KisMementoItemHashTableIterator iter(&m_index); 0188 while ((mi = iter.tile())) { 0189 parentMI = m_headsHashTable.getTileLazy(mi->col(), mi->row(), newTile); 0190 0191 mi->setParent(parentMI); 0192 mi->commit(); 0193 revisionList.append(mi); 0194 0195 m_headsHashTable.deleteTile(mi->col(), mi->row()); 0196 0197 iter.moveCurrentToHashTable(&m_headsHashTable); 0198 //iter.next(); // previous line does this for us 0199 } 0200 0201 KisHistoryItem hItem; 0202 hItem.itemList = revisionList; 0203 hItem.memento = m_currentMemento.data(); 0204 m_revisions.append(hItem); 0205 0206 m_currentMemento = 0; 0207 KIS_ASSERT(m_index.isEmpty()); 0208 0209 DEBUG_DUMP_MESSAGE("COMMIT_DONE"); 0210 0211 // Waking up pooler to prepare copies for us 0212 KisTileDataStore::instance()->kickPooler(); 0213 } 0214 0215 KisTileSP KisMementoManager::getCommittedTile(qint32 col, qint32 row, bool &existingTile) 0216 { 0217 /** 0218 * Our getOldTile mechanism is supposed to return current 0219 * tile, if the history is disabled. So we return zero if 0220 * no named transaction is in progress. 0221 */ 0222 if(!namedTransactionInProgress()) 0223 return KisTileSP(); 0224 0225 KisMementoItemSP mi = m_headsHashTable.getReadOnlyTileLazy(col, row, existingTile); 0226 return mi->tile(0); 0227 } 0228 0229 KisMementoSP KisMementoManager::getMemento() 0230 { 0231 /** 0232 * We do not allow nested transactions 0233 */ 0234 KIS_SAFE_ASSERT_RECOVER_NOOP(!namedTransactionInProgress()); 0235 0236 /** 0237 * The following assert is useful for testing if some code creates a 0238 * transaction on a device with "inconsistent history". We cannot keep 0239 * this sanity check enabled all the time, because in some places 0240 * (e.g. projection in KisAsyncMerger) such usecase is considered legit. 0241 * But in places with "consistent history", e.g. in layer's paint 0242 * device, such usage will cause undo corruption. 0243 */ 0244 // KIS_SAFE_ASSERT_RECOVER_NOOP(m_index.isEmpty()); 0245 0246 // Clear redo() information 0247 m_cancelledRevisions.clear(); 0248 0249 commit(); 0250 m_currentMemento = new KisMemento(this); 0251 0252 DEBUG_LOG_SIMPLE_ACTION("GET_MEMENTO_DONE"); 0253 0254 return m_currentMemento; 0255 } 0256 0257 KisMementoSP KisMementoManager::currentMemento() { 0258 return m_currentMemento; 0259 } 0260 0261 #define forEachReversed(iter, list) \ 0262 for(iter=list.end(); iter-- != list.begin();) 0263 0264 0265 void KisMementoManager::rollback(KisTileHashTable *ht, KisMementoSP memento) 0266 { 0267 commit(); 0268 0269 if (! m_revisions.size()) return; 0270 0271 KisHistoryItem changeList = m_revisions.takeLast(); 0272 0273 // SANITY CHECK: the transaction's memento must be in sync with 0274 // the revisions list we have locally 0275 KIS_SAFE_ASSERT_RECOVER_NOOP(changeList.memento == memento); 0276 0277 KisMementoItemSP mi; 0278 KisMementoItemSP parentMI; 0279 KisMementoItemList::iterator iter; 0280 0281 blockRegistration(); 0282 forEachReversed(iter, changeList.itemList) { 0283 mi=*iter; 0284 parentMI = mi->parent(); 0285 0286 if (mi->type() == KisMementoItem::CHANGED) 0287 ht->deleteTile(mi->col(), mi->row()); 0288 if (parentMI->type() == KisMementoItem::CHANGED) 0289 ht->addTile(parentMI->tile(this)); 0290 0291 m_headsHashTable.deleteTile(parentMI->col(), parentMI->row()); 0292 m_headsHashTable.addTile(parentMI); 0293 0294 // This is not necessary 0295 //mi->setParent(0); 0296 } 0297 /** 0298 * NOTE: tricky hack alert. 0299 * We have just deleted some tiles from the original hash table. 0300 * And they accurately reported to us about their death. Should 0301 * have reported... But we have prevented their registration with 0302 * explicitly blocking the process. So all the dead tiles are 0303 * going to /dev/null :) 0304 * 0305 * PS: It could cause some race condition... But we insist on 0306 * serialization of rollback()/rollforward() requests. There is 0307 * not much sense in calling rollback() concurrently. 0308 */ 0309 unblockRegistration(); 0310 0311 // We have just emulated a commit so: 0312 m_currentMemento = 0; 0313 KIS_ASSERT(!namedTransactionInProgress()); 0314 0315 m_cancelledRevisions.prepend(changeList); 0316 DEBUG_DUMP_MESSAGE("UNDONE"); 0317 0318 // Waking up pooler to prepare copies for us 0319 KisTileDataStore::instance()->kickPooler(); 0320 } 0321 0322 void KisMementoManager::rollforward(KisTileHashTable *ht, KisMementoSP memento) 0323 { 0324 KIS_SAFE_ASSERT_RECOVER_RETURN(m_index.isEmpty()); 0325 0326 if (!m_cancelledRevisions.size()) return; 0327 0328 KisHistoryItem changeList = m_cancelledRevisions.takeFirst(); 0329 0330 // SANITY CHECK: the transaction's memento must be in sync with 0331 // the revisions list we have locally 0332 KIS_SAFE_ASSERT_RECOVER_NOOP(changeList.memento == memento); 0333 0334 KisMementoItemSP mi; 0335 0336 blockRegistration(); 0337 Q_FOREACH (mi, changeList.itemList) { 0338 if (mi->parent()->type() == KisMementoItem::CHANGED) 0339 ht->deleteTile(mi->col(), mi->row()); 0340 if (mi->type() == KisMementoItem::CHANGED) 0341 ht->addTile(mi->tile(this)); 0342 0343 m_index.addTile(mi); 0344 } 0345 // see comment in rollback() 0346 0347 m_currentMemento = changeList.memento; 0348 commit(); 0349 unblockRegistration(); 0350 DEBUG_DUMP_MESSAGE("REDONE"); 0351 } 0352 0353 void KisMementoManager::purgeHistory(KisMementoSP oldestMemento) 0354 { 0355 if (m_currentMemento == oldestMemento) { 0356 commit(); 0357 } 0358 0359 qint32 revisionIndex = findRevisionByMemento(oldestMemento); 0360 if (revisionIndex < 0) return; 0361 0362 for(; revisionIndex > 0; revisionIndex--) { 0363 resetRevisionHistory(m_revisions.first().itemList); 0364 m_revisions.removeFirst(); 0365 } 0366 0367 KIS_ASSERT(m_revisions.first().memento == oldestMemento); 0368 resetRevisionHistory(m_revisions.first().itemList); 0369 0370 DEBUG_DUMP_MESSAGE("PURGE_HISTORY"); 0371 } 0372 0373 qint32 KisMementoManager::findRevisionByMemento(KisMementoSP memento) const 0374 { 0375 qint32 index = -1; 0376 for(qint32 i = 0; i < m_revisions.size(); i++) { 0377 if (m_revisions[i].memento == memento) { 0378 index = i; 0379 break; 0380 } 0381 } 0382 return index; 0383 } 0384 0385 void KisMementoManager::resetRevisionHistory(KisMementoItemList list) 0386 { 0387 KisMementoItemSP parentMI; 0388 KisMementoItemSP mi; 0389 0390 Q_FOREACH (mi, list) { 0391 parentMI = mi->parent(); 0392 if(!parentMI) continue; 0393 0394 while (parentMI->parent()) { 0395 parentMI = parentMI->parent(); 0396 } 0397 mi->setParent(parentMI); 0398 } 0399 } 0400 0401 void KisMementoManager::setDefaultTileData(KisTileData *defaultTileData) 0402 { 0403 m_headsHashTable.setDefaultTileData(defaultTileData); 0404 m_index.setDefaultTileData(defaultTileData); 0405 } 0406 0407 void KisMementoManager::debugPrintInfo() 0408 { 0409 printf("KisMementoManager stats:\n"); 0410 printf("Index list\n"); 0411 KisMementoItemSP mi; 0412 KisMementoItemHashTableIteratorConst iter(&m_index); 0413 0414 while ((mi = iter.tile())) { 0415 mi->debugPrintInfo(); 0416 iter.next(); 0417 } 0418 0419 printf("Revisions list:\n"); 0420 qint32 i = 0; 0421 Q_FOREACH (const KisHistoryItem &changeList, m_revisions) { 0422 printf("--- revision #%d ---\n", i++); 0423 Q_FOREACH (mi, changeList.itemList) { 0424 mi->debugPrintInfo(); 0425 } 0426 } 0427 0428 printf("\nCancelled revisions list:\n"); 0429 i = 0; 0430 Q_FOREACH (const KisHistoryItem &changeList, m_cancelledRevisions) { 0431 printf("--- revision #%d ---\n", m_revisions.size() + i++); 0432 Q_FOREACH (mi, changeList.itemList) { 0433 mi->debugPrintInfo(); 0434 } 0435 } 0436 0437 printf("----------------\n"); 0438 m_headsHashTable.debugPrintInfo(); 0439 }