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