Line data Source code
1 : /****************************************************************************** 2 : * 3 : * Project: GDAL Core 4 : * Purpose: Store cached blocks 5 : * Author: Even Rouault, <even dot rouault at spatialys dot org> 6 : * 7 : ****************************************************************************** 8 : * Copyright (c) 2015, Even Rouault <even dot rouault at spatialys dot org> 9 : * 10 : * SPDX-License-Identifier: MIT 11 : ****************************************************************************/ 12 : 13 : #include "cpl_port.h" 14 : #include "gdal_priv.h" 15 : 16 : #include <algorithm> 17 : #include <cstddef> 18 : #include <new> 19 : 20 : #include "cpl_atomic_ops.h" 21 : #include "cpl_error.h" 22 : #include "cpl_multiproc.h" 23 : 24 : //! @cond Doxygen_Suppress 25 : 26 : #ifdef DEBUG_VERBOSE_ABBC 27 : static int nAllBandsKeptAlivedBlocks = 0; 28 : #endif 29 : 30 : /************************************************************************/ 31 : /* GDALArrayBandBlockCache() */ 32 : /************************************************************************/ 33 : 34 33763 : GDALAbstractBandBlockCache::GDALAbstractBandBlockCache(GDALRasterBand *poBandIn) 35 101263 : : hSpinLock(CPLCreateLock(LOCK_SPIN)), hCond(CPLCreateCond()), 36 33763 : hCondMutex(CPLCreateMutex()), poBand(poBandIn) 37 : { 38 33764 : if (hCondMutex) 39 33764 : CPLReleaseMutex(hCondMutex); 40 33764 : } 41 : 42 : /************************************************************************/ 43 : /* ~GDALAbstractBandBlockCache() */ 44 : /************************************************************************/ 45 : 46 67526 : GDALAbstractBandBlockCache::~GDALAbstractBandBlockCache() 47 : { 48 33762 : CPLAssert(nKeepAliveCounter == 0); 49 33762 : FreeDanglingBlocks(); 50 33764 : if (hSpinLock) 51 33764 : CPLDestroyLock(hSpinLock); 52 33764 : if (hCondMutex) 53 33764 : CPLDestroyMutex(hCondMutex); 54 33764 : if (hCond) 55 33764 : CPLDestroyCond(hCond); 56 33764 : } 57 : 58 : /************************************************************************/ 59 : /* UnreferenceBlockBase() */ 60 : /* */ 61 : /* This is called by GDALRasterBlock::Internalize() and */ 62 : /* FlushCacheBlock() when they remove a block from the linked list */ 63 : /* but haven't yet flushed it to disk or recovered its pData member*/ 64 : /* We must be aware that they are blocks in that state, since the */ 65 : /* band must be kept alive while AddBlockToFreeList() hasn't been */ 66 : /* called (in case a block is being flushed while the final */ 67 : /* FlushCache() of the main thread of the dataset is running). */ 68 : /************************************************************************/ 69 : 70 29595 : void GDALAbstractBandBlockCache::UnreferenceBlockBase() 71 : { 72 29595 : CPLAtomicInc(&nKeepAliveCounter); 73 29595 : } 74 : 75 : /************************************************************************/ 76 : /* AddBlockToFreeList() */ 77 : /* */ 78 : /* This is called by GDALRasterBlock::Internalize() and */ 79 : /* FlushCacheBlock() after they have been finished with a block. */ 80 : /************************************************************************/ 81 : 82 29595 : void GDALAbstractBandBlockCache::AddBlockToFreeList(GDALRasterBlock *poBlock) 83 : { 84 29595 : CPLAssert(poBlock->poPrevious == nullptr); 85 29595 : CPLAssert(poBlock->poNext == nullptr); 86 : { 87 : #ifdef DEBUG_VERBOSE_ABBC 88 : CPLAtomicInc(&nAllBandsKeptAlivedBlocks); 89 : fprintf(/*ok*/ stderr, 90 : "AddBlockToFreeList(): nAllBandsKeptAlivedBlocks=%d\n", 91 : nAllBandsKeptAlivedBlocks); 92 : #endif 93 29595 : CPLLockHolderOptionalLockD(hSpinLock); 94 29595 : poBlock->poNext = psListBlocksToFree; 95 29595 : psListBlocksToFree = poBlock; 96 : } 97 : 98 : // If no more blocks in transient state, then warn 99 : // WaitCompletionPendingTasks() 100 29595 : CPLAcquireMutex(hCondMutex, 1000); 101 29595 : if (CPLAtomicDec(&nKeepAliveCounter) == 0) 102 : { 103 29550 : CPLCondSignal(hCond); 104 : } 105 29595 : CPLReleaseMutex(hCondMutex); 106 29595 : } 107 : 108 : /************************************************************************/ 109 : /* WaitCompletionPendingTasks() */ 110 : /************************************************************************/ 111 : 112 671792 : void GDALAbstractBandBlockCache::WaitCompletionPendingTasks() 113 : { 114 : #ifdef DEBUG_VERBOSE 115 : CPLDebug("GDAL", "WaitCompletionPendingTasks()"); 116 : #endif 117 : 118 671792 : CPLAcquireMutex(hCondMutex, 1000); 119 671793 : while (nKeepAliveCounter != 0) 120 : { 121 3 : CPLDebug("GDAL", "Waiting for other thread to finish working with our " 122 : "blocks"); 123 3 : CPLCondWait(hCond, hCondMutex); 124 : } 125 671790 : CPLReleaseMutex(hCondMutex); 126 671792 : } 127 : 128 : /************************************************************************/ 129 : /* FreeDanglingBlocks() */ 130 : /************************************************************************/ 131 : 132 3364720 : void GDALAbstractBandBlockCache::FreeDanglingBlocks() 133 : { 134 : GDALRasterBlock *poList; 135 : { 136 3364720 : CPLLockHolderOptionalLockD(hSpinLock); 137 3364720 : poList = psListBlocksToFree; 138 3364720 : psListBlocksToFree = nullptr; 139 : } 140 3383920 : while (poList) 141 : { 142 : #ifdef DEBUG_VERBOSE_ABBC 143 : CPLAtomicDec(&nAllBandsKeptAlivedBlocks); 144 : fprintf(/*ok*/ stderr, 145 : "FreeDanglingBlocks(): nAllBandsKeptAlivedBlocks=%d\n", 146 : nAllBandsKeptAlivedBlocks); 147 : #endif 148 19222 : GDALRasterBlock *poNext = poList->poNext; 149 19222 : poList->poNext = nullptr; 150 19222 : delete poList; 151 19213 : poList = poNext; 152 : } 153 3364700 : } 154 : 155 : /************************************************************************/ 156 : /* CreateBlock() */ 157 : /************************************************************************/ 158 : 159 3148170 : GDALRasterBlock *GDALAbstractBandBlockCache::CreateBlock(int nXBlockOff, 160 : int nYBlockOff) 161 : { 162 : GDALRasterBlock *poBlock; 163 : { 164 6296430 : CPLLockHolderOptionalLockD(hSpinLock); 165 3148250 : poBlock = psListBlocksToFree; 166 3148250 : if (poBlock) 167 : { 168 : #ifdef DEBUG_VERBOSE_ABBC 169 : CPLAtomicDec(&nAllBandsKeptAlivedBlocks); 170 : fprintf(/*ok*/ stderr, 171 : "CreateBlock(): nAllBandsKeptAlivedBlocks=%d\n", 172 : nAllBandsKeptAlivedBlocks); 173 : #endif 174 10373 : psListBlocksToFree = poBlock->poNext; 175 : } 176 : } 177 3148330 : if (poBlock) 178 10373 : poBlock->RecycleFor(nXBlockOff, nYBlockOff); 179 : else 180 3137920 : poBlock = 181 3137960 : new (std::nothrow) GDALRasterBlock(poBand, nXBlockOff, nYBlockOff); 182 3148280 : return poBlock; 183 : } 184 : 185 : /************************************************************************/ 186 : /* IncDirtyBlocks() */ 187 : /************************************************************************/ 188 : 189 : /** 190 : * \brief Increment/decrement the number of dirty blocks 191 : */ 192 : 193 515146 : void GDALAbstractBandBlockCache::IncDirtyBlocks(int nInc) 194 : { 195 515146 : CPLAtomicAdd(&m_nDirtyBlocks, nInc); 196 515145 : } 197 : 198 : /************************************************************************/ 199 : /* StartDirtyBlockFlushingLog() */ 200 : /************************************************************************/ 201 : 202 182500 : void GDALAbstractBandBlockCache::StartDirtyBlockFlushingLog() 203 : { 204 182500 : m_nInitialDirtyBlocksInFlushCache = 0; 205 182500 : if (m_nDirtyBlocks > 0 && CPLIsDefaultErrorHandlerAndCatchDebug()) 206 : { 207 1865 : if (CPLIsDebugEnabled() && 208 5 : CPLGetConfigOption("GDAL_REPORT_DIRTY_BLOCK_FLUSHING", nullptr) == 209 : nullptr) 210 : { 211 5 : m_nInitialDirtyBlocksInFlushCache = m_nDirtyBlocks; 212 5 : m_nLastTick = -1; 213 : } 214 : } 215 182500 : } 216 : 217 : /************************************************************************/ 218 : /* UpdateDirtyBlockFlushingLog() */ 219 : /************************************************************************/ 220 : 221 175939 : void GDALAbstractBandBlockCache::UpdateDirtyBlockFlushingLog() 222 : { 223 : // Poor man progress report for console applications 224 175939 : if (m_nInitialDirtyBlocksInFlushCache) 225 : { 226 1601 : const auto nRemainingDirtyBlocks = m_nDirtyBlocks; 227 1601 : const auto nFlushedBlocks = 228 1601 : m_nInitialDirtyBlocksInFlushCache - nRemainingDirtyBlocks + 1; 229 1601 : const double dfComplete = 230 1601 : double(nFlushedBlocks) / m_nInitialDirtyBlocksInFlushCache; 231 : const int nThisTick = 232 1601 : std::min(40, std::max(0, static_cast<int>(dfComplete * 40.0))); 233 1601 : if (nThisTick > m_nLastTick) 234 : { 235 165 : if (m_nLastTick < 0) 236 : { 237 5 : fprintf(stderr, "GDAL: Flushing dirty blocks: "); /*ok*/ 238 5 : fflush(stderr); /*ok*/ 239 : } 240 370 : while (nThisTick > m_nLastTick) 241 : { 242 205 : ++m_nLastTick; 243 205 : if (m_nLastTick % 4 == 0) 244 55 : fprintf(stderr, "%d", (m_nLastTick / 4) * 10); /*ok*/ 245 : else 246 150 : fprintf(stderr, "."); /*ok*/ 247 : } 248 : 249 165 : if (nThisTick == 40) 250 5 : fprintf(stderr, " - done.\n"); /*ok*/ 251 : else 252 160 : fflush(stderr); /*ok*/ 253 : } 254 : } 255 175939 : } 256 : 257 : /************************************************************************/ 258 : /* EndDirtyBlockFlushingLog() */ 259 : /************************************************************************/ 260 : 261 182500 : void GDALAbstractBandBlockCache::EndDirtyBlockFlushingLog() 262 : { 263 182500 : m_nInitialDirtyBlocksInFlushCache = 0; 264 182500 : m_nLastTick = -1; 265 182500 : } 266 : 267 : //! @endcond