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 38228 : GDALAbstractBandBlockCache::GDALAbstractBandBlockCache(GDALRasterBand *poBandIn) 35 114674 : : hSpinLock(CPLCreateLock(LOCK_SPIN)), hCond(CPLCreateCond()), 36 38228 : hCondMutex(CPLCreateMutex()), poBand(poBandIn) 37 : { 38 38228 : if (hCondMutex) 39 38228 : CPLReleaseMutex(hCondMutex); 40 38228 : } 41 : 42 : /************************************************************************/ 43 : /* ~GDALAbstractBandBlockCache() */ 44 : /************************************************************************/ 45 : 46 76456 : GDALAbstractBandBlockCache::~GDALAbstractBandBlockCache() 47 : { 48 38228 : CPLAssert(nKeepAliveCounter == 0); 49 38228 : FreeDanglingBlocks(); 50 38228 : if (hSpinLock) 51 38228 : CPLDestroyLock(hSpinLock); 52 38228 : if (hCondMutex) 53 38228 : CPLDestroyMutex(hCondMutex); 54 38228 : if (hCond) 55 38228 : CPLDestroyCond(hCond); 56 38228 : } 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 29624 : void GDALAbstractBandBlockCache::UnreferenceBlockBase() 71 : { 72 29624 : CPLAtomicInc(&nKeepAliveCounter); 73 29624 : } 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 29624 : void GDALAbstractBandBlockCache::AddBlockToFreeList(GDALRasterBlock *poBlock) 83 : { 84 29624 : CPLAssert(poBlock->poPrevious == nullptr); 85 29624 : 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 29624 : CPLLockHolderOptionalLockD(hSpinLock); 94 29624 : poBlock->poNext = psListBlocksToFree; 95 29624 : psListBlocksToFree = poBlock; 96 : } 97 : 98 : // If no more blocks in transient state, then warn 99 : // WaitCompletionPendingTasks() 100 29624 : CPLAcquireMutex(hCondMutex, 1000); 101 29624 : if (CPLAtomicDec(&nKeepAliveCounter) == 0) 102 : { 103 29579 : CPLCondSignal(hCond); 104 : } 105 29624 : CPLReleaseMutex(hCondMutex); 106 29624 : } 107 : 108 : /************************************************************************/ 109 : /* WaitCompletionPendingTasks() */ 110 : /************************************************************************/ 111 : 112 1017150 : void GDALAbstractBandBlockCache::WaitCompletionPendingTasks() 113 : { 114 : #ifdef DEBUG_VERBOSE 115 : CPLDebug("GDAL", "WaitCompletionPendingTasks()"); 116 : #endif 117 : 118 1017150 : CPLAcquireMutex(hCondMutex, 1000); 119 1017160 : 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 1017150 : CPLReleaseMutex(hCondMutex); 126 1017150 : } 127 : 128 : /************************************************************************/ 129 : /* FreeDanglingBlocks() */ 130 : /************************************************************************/ 131 : 132 3402280 : void GDALAbstractBandBlockCache::FreeDanglingBlocks() 133 : { 134 : GDALRasterBlock *poList; 135 : { 136 3402280 : CPLLockHolderOptionalLockD(hSpinLock); 137 3402280 : poList = psListBlocksToFree; 138 3402280 : psListBlocksToFree = nullptr; 139 : } 140 3421280 : 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 19007 : GDALRasterBlock *poNext = poList->poNext; 149 19007 : poList->poNext = nullptr; 150 19007 : delete poList; 151 19004 : poList = poNext; 152 : } 153 3402280 : } 154 : 155 : /************************************************************************/ 156 : /* CreateBlock() */ 157 : /************************************************************************/ 158 : 159 3163340 : GDALRasterBlock *GDALAbstractBandBlockCache::CreateBlock(int nXBlockOff, 160 : int nYBlockOff) 161 : { 162 : GDALRasterBlock *poBlock; 163 : { 164 6326670 : CPLLockHolderOptionalLockD(hSpinLock); 165 3163330 : poBlock = psListBlocksToFree; 166 3163330 : 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 10617 : psListBlocksToFree = poBlock->poNext; 175 : } 176 : } 177 3163330 : if (poBlock) 178 10617 : poBlock->RecycleFor(nXBlockOff, nYBlockOff); 179 : else 180 3152710 : poBlock = 181 3152720 : new (std::nothrow) GDALRasterBlock(poBand, nXBlockOff, nYBlockOff); 182 3163320 : return poBlock; 183 : } 184 : 185 : /************************************************************************/ 186 : /* IncDirtyBlocks() */ 187 : /************************************************************************/ 188 : 189 : /** 190 : * \brief Increment/decrement the number of dirty blocks 191 : */ 192 : 193 552607 : void GDALAbstractBandBlockCache::IncDirtyBlocks(int nInc) 194 : { 195 552607 : CPLAtomicAdd(&m_nDirtyBlocks, nInc); 196 552606 : } 197 : 198 : /************************************************************************/ 199 : /* StartDirtyBlockFlushingLog() */ 200 : /************************************************************************/ 201 : 202 200711 : void GDALAbstractBandBlockCache::StartDirtyBlockFlushingLog() 203 : { 204 200711 : m_nInitialDirtyBlocksInFlushCache = 0; 205 200711 : if (m_nDirtyBlocks > 0 && CPLIsDefaultErrorHandlerAndCatchDebug()) 206 : { 207 2004 : if (CPLIsDebugEnabled() && 208 4 : CPLGetConfigOption("GDAL_REPORT_DIRTY_BLOCK_FLUSHING", nullptr) == 209 : nullptr) 210 : { 211 4 : m_nInitialDirtyBlocksInFlushCache = m_nDirtyBlocks; 212 4 : m_nLastTick = -1; 213 : } 214 : } 215 200711 : } 216 : 217 : /************************************************************************/ 218 : /* UpdateDirtyBlockFlushingLog() */ 219 : /************************************************************************/ 220 : 221 193968 : void GDALAbstractBandBlockCache::UpdateDirtyBlockFlushingLog() 222 : { 223 : // Poor man progress report for console applications 224 193968 : if (m_nInitialDirtyBlocksInFlushCache) 225 : { 226 1600 : const auto nRemainingDirtyBlocks = m_nDirtyBlocks; 227 1600 : const auto nFlushedBlocks = 228 1600 : m_nInitialDirtyBlocksInFlushCache - nRemainingDirtyBlocks + 1; 229 1600 : const double dfComplete = 230 1600 : double(nFlushedBlocks) / m_nInitialDirtyBlocksInFlushCache; 231 : const int nThisTick = 232 1600 : std::min(40, std::max(0, static_cast<int>(dfComplete * 40.0))); 233 1600 : if (nThisTick > m_nLastTick) 234 : { 235 164 : if (m_nLastTick < 0) 236 : { 237 4 : fprintf(stderr, "GDAL: Flushing dirty blocks: "); /*ok*/ 238 4 : fflush(stderr); /*ok*/ 239 : } 240 328 : while (nThisTick > m_nLastTick) 241 : { 242 164 : ++m_nLastTick; 243 164 : if (m_nLastTick % 4 == 0) 244 44 : fprintf(stderr, "%d", (m_nLastTick / 4) * 10); /*ok*/ 245 : else 246 120 : fprintf(stderr, "."); /*ok*/ 247 : } 248 : 249 164 : if (nThisTick == 40) 250 4 : fprintf(stderr, " - done.\n"); /*ok*/ 251 : else 252 160 : fflush(stderr); /*ok*/ 253 : } 254 : } 255 193968 : } 256 : 257 : /************************************************************************/ 258 : /* EndDirtyBlockFlushingLog() */ 259 : /************************************************************************/ 260 : 261 200718 : void GDALAbstractBandBlockCache::EndDirtyBlockFlushingLog() 262 : { 263 200718 : m_nInitialDirtyBlocksInFlushCache = 0; 264 200718 : m_nLastTick = -1; 265 200718 : } 266 : 267 : //! @endcond