File indexing completed on 2024-05-26 04:28:04

0001 /*
0002  *  SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
0003  *            (c) 2009 Dmitry  Kazakov <dimula73@gmail.com>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 
0009 #include "kis_tile_data.h"
0010 #include "kis_tile_data_store.h"
0011 #include "kis_tile.h"
0012 #include "kis_memento_manager.h"
0013 #include "kis_debug.h"
0014 
0015 
0016 void KisTile::init(qint32 col, qint32 row,
0017                    KisTileData *defaultTileData, KisMementoManager* mm)
0018 {
0019     m_col = col;
0020     m_row = row;
0021     m_lockCounter = 0;
0022 
0023     m_extent = QRect(m_col * KisTileData::WIDTH, m_row * KisTileData::HEIGHT,
0024                      KisTileData::WIDTH, KisTileData::HEIGHT);
0025 
0026     m_tileData = defaultTileData;
0027     m_tileData->acquire();
0028 
0029     if (mm) {
0030         mm->registerTileChange(this);
0031     }
0032     m_mementoManager.storeRelease(mm);
0033 }
0034 
0035 KisTile::KisTile(qint32 col, qint32 row,
0036                  KisTileData *defaultTileData, KisMementoManager* mm)
0037 {
0038     init(col, row, defaultTileData, mm);
0039 }
0040 
0041 KisTile::KisTile(const KisTile& rhs, qint32 col, qint32 row, KisMementoManager* mm)
0042         : KisShared()
0043 {
0044     init(col, row, rhs.tileData(), mm);
0045 }
0046 
0047 KisTile::KisTile(const KisTile& rhs, KisMementoManager* mm)
0048         : KisShared()
0049 {
0050     init(rhs.col(), rhs.row(), rhs.tileData(), mm);
0051 }
0052 
0053 KisTile::KisTile(const KisTile& rhs)
0054         : KisShared()
0055 {
0056     init(rhs.col(), rhs.row(), rhs.tileData(), rhs.m_mementoManager);
0057 }
0058 
0059 KisTile::~KisTile()
0060 {
0061 #ifdef DEAD_TILES_SANITY_CHECK
0062     KIS_ASSERT(!m_lockCounter);
0063 
0064     /**
0065      * We should have been disconnected from the memento manager in
0066      * notifyDetachedFromDataManager() or notifyDeadWithoutDetaching(),
0067      * otherwise there is a bug
0068      */
0069 
0070     if (m_mementoManager) {
0071         qDebug() << this << ppVar(m_sanityNumCOWHappened);
0072         qDebug() << this << ppVar(m_sanityHasBeenDetached);
0073         qDebug() << this << ppVar(m_sanityMMHasBeenInitializedManually);
0074         qDebug() << this << ppVar(m_sanityIsDead);
0075         KIS_ASSERT(0 && "m_mementoManager is still initialized during destruction");
0076     }
0077 #endif
0078 
0079     m_tileData->release();
0080 }
0081 
0082 void KisTile::notifyDetachedFromDataManager()
0083 {
0084 #ifdef DEAD_TILES_SANITY_CHECK
0085     sanityCheckIsNotLockedForWrite();
0086 #endif
0087 
0088     if (m_mementoManager.loadAcquire()) {
0089         KisMementoManager *manager = m_mementoManager;
0090         m_mementoManager.storeRelease(0);
0091         manager->registerTileDeleted(this);
0092     }
0093 
0094 #ifdef DEAD_TILES_SANITY_CHECK
0095     m_sanityHasBeenDetached.ref();
0096 #endif
0097 }
0098 
0099 void KisTile::notifyDeadWithoutDetaching()
0100 {
0101 #ifdef DEAD_TILES_SANITY_CHECK
0102     sanityCheckIsNotLockedForWrite();
0103 #endif
0104 
0105     m_mementoManager.storeRelease(0);
0106 
0107 #ifdef DEAD_TILES_SANITY_CHECK
0108     m_sanityIsDead.ref();
0109 #endif
0110 }
0111 
0112 void KisTile::notifyAttachedToDataManager(KisMementoManager *mm)
0113 {
0114 #ifdef DEAD_TILES_SANITY_CHECK
0115     sanityCheckIsNotDestroyedYet();
0116 #endif
0117 
0118     // TODO: check if we really need locking here
0119     if (!m_mementoManager.loadAcquire()) {
0120         QMutexLocker locker(&m_COWMutex);
0121 
0122         if (!m_mementoManager.loadAcquire()) {
0123 
0124             if (mm) {
0125                 mm->registerTileChange(this);
0126             }
0127             m_mementoManager.storeRelease(mm);
0128 
0129 #ifdef DEAD_TILES_SANITY_CHECK
0130             m_sanityMMHasBeenInitializedManually.ref();
0131 #endif
0132         }
0133     }
0134 
0135 #ifdef DEAD_TILES_SANITY_CHECK
0136     sanityCheckIsNotDestroyedYet();
0137 #endif
0138 }
0139 
0140 //#define DEBUG_TILE_LOCKING
0141 //#define DEBUG_TILE_COWING
0142 
0143 #ifdef DEBUG_TILE_LOCKING
0144 #define DEBUG_LOG_ACTION(action)                                        \
0145     printf("### %s \ttile:\t0x%llX (%d, %d) (0x%llX) ###\n", action, (quintptr)this, m_col, m_row, (quintptr)m_tileData)
0146 #else
0147 #define DEBUG_LOG_ACTION(action)
0148 #endif
0149 
0150 #ifdef DEBUG_TILE_COWING
0151 #define DEBUG_COWING(newTD)                                             \
0152     printf("### COW done \ttile:\t0x%X (%d, %d) (0x%X -> 0x%X) [mm: 0x%X] ###\n", (quintptr)this, m_col, m_row, (quintptr)m_tileData, (quintptr)newTD, m_mementoManager);
0153 #else
0154 #define DEBUG_COWING(newTD)
0155 #endif
0156 
0157 inline void KisTile::blockSwapping() const
0158 {
0159     /**
0160      * We need to hold a special barrier lock here to ensure
0161      * m_tileData->blockSwapping() has finished executing
0162      * before anyone started reading the tile data. That is
0163      * why we can not use atomic operations here.
0164      */
0165 
0166     QMutexLocker locker(&m_swapBarrierLock);
0167     Q_ASSERT(m_lockCounter >= 0);
0168 
0169     if(!m_lockCounter++)
0170         m_tileData->blockSwapping();
0171 
0172     Q_ASSERT(data());
0173 }
0174 
0175 inline void KisTile::unblockSwapping() const
0176 {
0177     QMutexLocker locker(&m_swapBarrierLock);
0178     Q_ASSERT(m_lockCounter > 0);
0179 
0180     if(--m_lockCounter == 0) {
0181         m_tileData->unblockSwapping();
0182 
0183         if(!m_oldTileData.isEmpty()) {
0184             Q_FOREACH (KisTileData *td, m_oldTileData) {
0185                 td->unblockSwapping();
0186                 td->release();
0187             }
0188             m_oldTileData.clear();
0189         }
0190     }
0191 }
0192 
0193 inline void KisTile::safeReleaseOldTileData(KisTileData *td)
0194 {
0195     QMutexLocker locker(&m_swapBarrierLock);
0196     Q_ASSERT(m_lockCounter >= 0);
0197 
0198     if(m_lockCounter > 0) {
0199         m_oldTileData.push(td);
0200     }
0201     else {
0202         td->unblockSwapping();
0203         td->release();
0204     }
0205 }
0206 
0207 void KisTile::lockForRead() const
0208 {
0209 #ifdef DEAD_TILES_SANITY_CHECK
0210     m_sanityLockedForRead.ref();
0211 #endif
0212 
0213     DEBUG_LOG_ACTION("lock [R]");
0214     blockSwapping();
0215 }
0216 
0217 
0218 #define lazyCopying() (m_tileData->m_usersCount>1)
0219 
0220 void KisTile::lockForWrite()
0221 {
0222 #ifdef DEAD_TILES_SANITY_CHECK
0223     m_sanityLockedForWrite.ref();
0224 #endif
0225 
0226     blockSwapping();
0227 
0228     /* We are doing COW here */
0229     if (lazyCopying()) {
0230         m_COWMutex.lock();
0231 
0232         /**
0233          * Everything could have happened before we took
0234          * the mutex, so let's check again...
0235          */
0236 
0237         if (lazyCopying()) {
0238 
0239             KisTileData *tileData = m_tileData->clone();
0240             tileData->acquire();
0241             tileData->blockSwapping();
0242             KisTileData *oldTileData = m_tileData;
0243             m_tileData = tileData;
0244             safeReleaseOldTileData(oldTileData);
0245 
0246             DEBUG_COWING(tileData);
0247 
0248             KisMementoManager *mm = m_mementoManager.load();
0249             if (mm) {
0250                 mm->registerTileChange(this);
0251             }
0252         }
0253         m_COWMutex.unlock();
0254 
0255 #ifdef DEAD_TILES_SANITY_CHECK
0256         m_sanityNumCOWHappened.ref();
0257 #endif
0258     }
0259 
0260     DEBUG_LOG_ACTION("lock [W]");
0261 }
0262 
0263 void KisTile::unlockForWrite()
0264 {
0265     unblockSwapping();
0266     DEBUG_LOG_ACTION("unlock [W]");
0267 
0268 #ifdef DEAD_TILES_SANITY_CHECK
0269     m_sanityLockedForWrite.deref();
0270     KIS_ASSERT(m_sanityLockedForWrite.loadAcquire() >= 0);
0271 #endif
0272 }
0273 
0274 void KisTile::unlockForRead() const
0275 {
0276     unblockSwapping();
0277     DEBUG_LOG_ACTION("unlock [R]");
0278 
0279 #ifdef DEAD_TILES_SANITY_CHECK
0280     m_sanityLockedForRead.deref();
0281     KIS_ASSERT(m_sanityLockedForRead.loadAcquire() >= 0);
0282 #endif
0283 }
0284 
0285 
0286 #include <stdio.h>
0287 void KisTile::debugPrintInfo()
0288 {
0289     dbgTiles << "------\n"
0290                 "Tile:\t\t\t" << this
0291                 << "\n   data:\t" << m_tileData
0292                 << "\n   next:\t" <<  m_nextTile.data();
0293 
0294 }
0295 
0296 void KisTile::debugDumpTile()
0297 {
0298     lockForRead();
0299     quint8 *data = this->data();
0300 
0301     for (int i = 0; i < KisTileData::HEIGHT; i++) {
0302         for (int j = 0; j < KisTileData::WIDTH; j++) {
0303             dbgTiles << data[(i*KisTileData::WIDTH+j)*pixelSize()];
0304         }
0305     }
0306     unlockForRead();
0307 }
0308 
0309 #ifdef DEAD_TILES_SANITY_CHECK
0310 
0311 void KisTile::sanityCheckIsNotDestroyedYet()
0312 {
0313     if (m_lockCounter) {
0314         qDebug() << this << ppVar(m_sanityLockedForRead);
0315         qDebug() << this << ppVar(m_sanityLockedForWrite);
0316         qDebug() << this << ppVar(m_lockCounter);
0317 
0318         KIS_ASSERT(!m_lockCounter || !m_sanityLockedForWrite && "sanityCheckIsNotDestroyedYet() failed");
0319     }
0320 }
0321 
0322 void KisTile::sanityCheckIsNotLockedForWrite()
0323 {
0324     if (m_sanityHasBeenDetached.loadAcquire()) {
0325         qDebug() << this << ppVar(m_sanityNumCOWHappened);
0326         qDebug() << this << ppVar(m_sanityHasBeenDetached);
0327         qDebug() << this << ppVar(m_sanityMMHasBeenInitializedManually);
0328         qDebug() << this << ppVar(m_sanityIsDead);
0329         KIS_ASSERT(0 && "sanityCheckIsNotLockedForWrite() failed");
0330     }
0331 }
0332 
0333 #endif