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 : #include "gdal_abstractbandblockcache.h" 25 : 26 : //! @cond Doxygen_Suppress 27 : 28 : #ifdef DEBUG_VERBOSE_ABBC 29 : static int nAllBandsKeptAlivedBlocks = 0; 30 : #endif 31 : 32 : /************************************************************************/ 33 : /* GDALArrayBandBlockCache() */ 34 : /************************************************************************/ 35 : 36 238545 : GDALAbstractBandBlockCache::GDALAbstractBandBlockCache(GDALRasterBand *poBandIn) 37 715624 : : hSpinLock(CPLCreateLock(LOCK_SPIN)), hCond(CPLCreateCond()), 38 238545 : hCondMutex(CPLCreateMutex()), poBand(poBandIn) 39 : { 40 238547 : if (hCondMutex) 41 238547 : CPLReleaseMutex(hCondMutex); 42 238547 : } 43 : 44 : /************************************************************************/ 45 : /* ~GDALAbstractBandBlockCache() */ 46 : /************************************************************************/ 47 : 48 477089 : GDALAbstractBandBlockCache::~GDALAbstractBandBlockCache() 49 : { 50 238544 : CPLAssert(nKeepAliveCounter == 0); 51 238544 : FreeDanglingBlocks(); 52 238546 : if (hSpinLock) 53 238546 : CPLDestroyLock(hSpinLock); 54 238547 : if (hCondMutex) 55 238547 : CPLDestroyMutex(hCondMutex); 56 238547 : if (hCond) 57 238547 : CPLDestroyCond(hCond); 58 238545 : } 59 : 60 : /************************************************************************/ 61 : /* UnreferenceBlockBase() */ 62 : /* */ 63 : /* This is called by GDALRasterBlock::Internalize() and */ 64 : /* FlushCacheBlock() when they remove a block from the linked list */ 65 : /* but haven't yet flushed it to disk or recovered its pData member*/ 66 : /* We must be aware that they are blocks in that state, since the */ 67 : /* band must be kept alive while AddBlockToFreeList() hasn't been */ 68 : /* called (in case a block is being flushed while the final */ 69 : /* FlushCache() of the main thread of the dataset is running). */ 70 : /************************************************************************/ 71 : 72 29648 : void GDALAbstractBandBlockCache::UnreferenceBlockBase() 73 : { 74 29648 : CPLAtomicInc(&nKeepAliveCounter); 75 29648 : } 76 : 77 : /************************************************************************/ 78 : /* AddBlockToFreeList() */ 79 : /* */ 80 : /* This is called by GDALRasterBlock::Internalize() and */ 81 : /* FlushCacheBlock() after they have been finished with a block. */ 82 : /************************************************************************/ 83 : 84 29639 : void GDALAbstractBandBlockCache::AddBlockToFreeList(GDALRasterBlock *poBlock) 85 : { 86 29639 : CPLAssert(poBlock->poPrevious == nullptr); 87 29639 : CPLAssert(poBlock->poNext == nullptr); 88 : { 89 : #ifdef DEBUG_VERBOSE_ABBC 90 : CPLAtomicInc(&nAllBandsKeptAlivedBlocks); 91 : fprintf(/*ok*/ stderr, 92 : "AddBlockToFreeList(): nAllBandsKeptAlivedBlocks=%d\n", 93 : nAllBandsKeptAlivedBlocks); 94 : #endif 95 29639 : CPLLockHolderOptionalLockD(hSpinLock); 96 29629 : poBlock->poNext = psListBlocksToFree; 97 29629 : psListBlocksToFree = poBlock; 98 : } 99 : 100 : // If no more blocks in transient state, then warn 101 : // WaitCompletionPendingTasks() 102 29626 : CPLAcquireMutex(hCondMutex, 1000); 103 29630 : if (CPLAtomicDec(&nKeepAliveCounter) == 0) 104 : { 105 29590 : CPLCondSignal(hCond); 106 : } 107 29624 : CPLReleaseMutex(hCondMutex); 108 29632 : } 109 : 110 : /************************************************************************/ 111 : /* WaitCompletionPendingTasks() */ 112 : /************************************************************************/ 113 : 114 1816340 : void GDALAbstractBandBlockCache::WaitCompletionPendingTasks() 115 : { 116 : #ifdef DEBUG_VERBOSE 117 : CPLDebug("GDAL", "WaitCompletionPendingTasks()"); 118 : #endif 119 : 120 1816340 : CPLAcquireMutex(hCondMutex, 1000); 121 1816350 : while (nKeepAliveCounter != 0) 122 : { 123 4 : CPLDebug("GDAL", "Waiting for other thread to finish working with our " 124 : "blocks"); 125 4 : CPLCondWait(hCond, hCondMutex); 126 : } 127 1816350 : CPLReleaseMutex(hCondMutex); 128 1816340 : } 129 : 130 : /************************************************************************/ 131 : /* FreeDanglingBlocks() */ 132 : /************************************************************************/ 133 : 134 4606570 : void GDALAbstractBandBlockCache::FreeDanglingBlocks() 135 : { 136 : GDALRasterBlock *poList; 137 : { 138 4606570 : CPLLockHolderOptionalLockD(hSpinLock); 139 4606580 : poList = psListBlocksToFree; 140 4606580 : psListBlocksToFree = nullptr; 141 : } 142 4625580 : while (poList) 143 : { 144 : #ifdef DEBUG_VERBOSE_ABBC 145 : CPLAtomicDec(&nAllBandsKeptAlivedBlocks); 146 : fprintf(/*ok*/ stderr, 147 : "FreeDanglingBlocks(): nAllBandsKeptAlivedBlocks=%d\n", 148 : nAllBandsKeptAlivedBlocks); 149 : #endif 150 19014 : GDALRasterBlock *poNext = poList->poNext; 151 19014 : poList->poNext = nullptr; 152 19014 : delete poList; 153 19002 : poList = poNext; 154 : } 155 4606570 : } 156 : 157 : /************************************************************************/ 158 : /* CreateBlock() */ 159 : /************************************************************************/ 160 : 161 3368650 : GDALRasterBlock *GDALAbstractBandBlockCache::CreateBlock(int nXBlockOff, 162 : int nYBlockOff) 163 : { 164 : GDALRasterBlock *poBlock; 165 : { 166 6737320 : CPLLockHolderOptionalLockD(hSpinLock); 167 3368670 : poBlock = psListBlocksToFree; 168 3368670 : if (poBlock) 169 : { 170 : #ifdef DEBUG_VERBOSE_ABBC 171 : CPLAtomicDec(&nAllBandsKeptAlivedBlocks); 172 : fprintf(/*ok*/ stderr, 173 : "CreateBlock(): nAllBandsKeptAlivedBlocks=%d\n", 174 : nAllBandsKeptAlivedBlocks); 175 : #endif 176 10620 : psListBlocksToFree = poBlock->poNext; 177 : } 178 : } 179 3368670 : if (poBlock) 180 10620 : poBlock->RecycleFor(nXBlockOff, nYBlockOff); 181 : else 182 3358030 : poBlock = 183 3358050 : new (std::nothrow) GDALRasterBlock(poBand, nXBlockOff, nYBlockOff); 184 3368620 : return poBlock; 185 : } 186 : 187 : /************************************************************************/ 188 : /* IncDirtyBlocks() */ 189 : /************************************************************************/ 190 : 191 : /** 192 : * \brief Increment/decrement the number of dirty blocks 193 : */ 194 : 195 796513 : void GDALAbstractBandBlockCache::IncDirtyBlocks(int nInc) 196 : { 197 796513 : CPLAtomicAdd(&m_nDirtyBlocks, nInc); 198 796522 : } 199 : 200 : /************************************************************************/ 201 : /* StartDirtyBlockFlushingLog() */ 202 : /************************************************************************/ 203 : 204 999270 : void GDALAbstractBandBlockCache::StartDirtyBlockFlushingLog() 205 : { 206 999270 : m_nInitialDirtyBlocksInFlushCache = 0; 207 999270 : if (m_nDirtyBlocks > 0 && CPLIsDefaultErrorHandlerAndCatchDebug()) 208 : { 209 2437 : if (CPLIsDebugEnabled() && 210 4 : CPLGetConfigOption("GDAL_REPORT_DIRTY_BLOCK_FLUSHING", nullptr) == 211 : nullptr) 212 : { 213 4 : m_nInitialDirtyBlocksInFlushCache = m_nDirtyBlocks; 214 4 : m_nLastTick = -1; 215 : } 216 : } 217 999270 : } 218 : 219 : /************************************************************************/ 220 : /* UpdateDirtyBlockFlushingLog() */ 221 : /************************************************************************/ 222 : 223 217382 : void GDALAbstractBandBlockCache::UpdateDirtyBlockFlushingLog() 224 : { 225 : // Poor man progress report for console applications 226 217382 : if (m_nInitialDirtyBlocksInFlushCache) 227 : { 228 1600 : const auto nRemainingDirtyBlocks = m_nDirtyBlocks; 229 1600 : const auto nFlushedBlocks = 230 1600 : m_nInitialDirtyBlocksInFlushCache - nRemainingDirtyBlocks + 1; 231 1600 : const double dfComplete = 232 1600 : double(nFlushedBlocks) / m_nInitialDirtyBlocksInFlushCache; 233 : const int nThisTick = 234 1600 : std::min(40, std::max(0, static_cast<int>(dfComplete * 40.0))); 235 1600 : if (nThisTick > m_nLastTick) 236 : { 237 164 : if (m_nLastTick < 0) 238 : { 239 4 : fprintf(stderr, "GDAL: Flushing dirty blocks: "); /*ok*/ 240 4 : fflush(stderr); /*ok*/ 241 : } 242 328 : while (nThisTick > m_nLastTick) 243 : { 244 164 : ++m_nLastTick; 245 164 : if (m_nLastTick % 4 == 0) 246 44 : fprintf(stderr, "%d", (m_nLastTick / 4) * 10); /*ok*/ 247 : else 248 120 : fprintf(stderr, "."); /*ok*/ 249 : } 250 : 251 164 : if (nThisTick == 40) 252 4 : fprintf(stderr, " - done.\n"); /*ok*/ 253 : else 254 160 : fflush(stderr); /*ok*/ 255 : } 256 : } 257 217382 : } 258 : 259 : /************************************************************************/ 260 : /* EndDirtyBlockFlushingLog() */ 261 : /************************************************************************/ 262 : 263 999272 : void GDALAbstractBandBlockCache::EndDirtyBlockFlushingLog() 264 : { 265 999272 : m_nInitialDirtyBlocksInFlushCache = 0; 266 999272 : m_nLastTick = -1; 267 999272 : } 268 : 269 : //! @endcond