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 238505 : GDALAbstractBandBlockCache::GDALAbstractBandBlockCache(GDALRasterBand *poBandIn) 37 715446 : : hSpinLock(CPLCreateLock(LOCK_SPIN)), hCond(CPLCreateCond()), 38 238505 : hCondMutex(CPLCreateMutex()), poBand(poBandIn) 39 : { 40 238508 : if (hCondMutex) 41 238507 : CPLReleaseMutex(hCondMutex); 42 238510 : } 43 : 44 : /************************************************************************/ 45 : /* ~GDALAbstractBandBlockCache() */ 46 : /************************************************************************/ 47 : 48 477019 : GDALAbstractBandBlockCache::~GDALAbstractBandBlockCache() 49 : { 50 238509 : CPLAssert(nKeepAliveCounter == 0); 51 238509 : FreeDanglingBlocks(); 52 238507 : if (hSpinLock) 53 238507 : CPLDestroyLock(hSpinLock); 54 238510 : if (hCondMutex) 55 238510 : CPLDestroyMutex(hCondMutex); 56 238510 : if (hCond) 57 238510 : CPLDestroyCond(hCond); 58 238510 : } 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 29635 : void GDALAbstractBandBlockCache::UnreferenceBlockBase() 73 : { 74 29635 : CPLAtomicInc(&nKeepAliveCounter); 75 29635 : } 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 29634 : void GDALAbstractBandBlockCache::AddBlockToFreeList(GDALRasterBlock *poBlock) 85 : { 86 29634 : CPLAssert(poBlock->poPrevious == nullptr); 87 29634 : 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 29634 : CPLLockHolderOptionalLockD(hSpinLock); 96 29634 : poBlock->poNext = psListBlocksToFree; 97 29634 : psListBlocksToFree = poBlock; 98 : } 99 : 100 : // If no more blocks in transient state, then warn 101 : // WaitCompletionPendingTasks() 102 29633 : CPLAcquireMutex(hCondMutex, 1000); 103 29634 : if (CPLAtomicDec(&nKeepAliveCounter) == 0) 104 : { 105 29426 : CPLCondSignal(hCond); 106 : } 107 29633 : CPLReleaseMutex(hCondMutex); 108 29634 : } 109 : 110 : /************************************************************************/ 111 : /* WaitCompletionPendingTasks() */ 112 : /************************************************************************/ 113 : 114 1816220 : void GDALAbstractBandBlockCache::WaitCompletionPendingTasks() 115 : { 116 : #ifdef DEBUG_VERBOSE 117 : CPLDebug("GDAL", "WaitCompletionPendingTasks()"); 118 : #endif 119 : 120 1816220 : CPLAcquireMutex(hCondMutex, 1000); 121 1816230 : 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 1816220 : CPLReleaseMutex(hCondMutex); 128 1816230 : } 129 : 130 : /************************************************************************/ 131 : /* FreeDanglingBlocks() */ 132 : /************************************************************************/ 133 : 134 4606070 : void GDALAbstractBandBlockCache::FreeDanglingBlocks() 135 : { 136 : GDALRasterBlock *poList; 137 : { 138 4606070 : CPLLockHolderOptionalLockD(hSpinLock); 139 4606140 : poList = psListBlocksToFree; 140 4606140 : psListBlocksToFree = nullptr; 141 : } 142 4624240 : 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 18169 : GDALRasterBlock *poNext = poList->poNext; 151 18169 : poList->poNext = nullptr; 152 18169 : delete poList; 153 18118 : poList = poNext; 154 : } 155 4606080 : } 156 : 157 : /************************************************************************/ 158 : /* CreateBlock() */ 159 : /************************************************************************/ 160 : 161 3368500 : GDALRasterBlock *GDALAbstractBandBlockCache::CreateBlock(int nXBlockOff, 162 : int nYBlockOff) 163 : { 164 : GDALRasterBlock *poBlock; 165 : { 166 6736950 : CPLLockHolderOptionalLockD(hSpinLock); 167 3368450 : poBlock = psListBlocksToFree; 168 3368450 : 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 11466 : psListBlocksToFree = poBlock->poNext; 177 : } 178 : } 179 3368420 : if (poBlock) 180 11466 : poBlock->RecycleFor(nXBlockOff, nYBlockOff); 181 : else 182 3357010 : poBlock = 183 3356960 : new (std::nothrow) GDALRasterBlock(poBand, nXBlockOff, nYBlockOff); 184 3368430 : return poBlock; 185 : } 186 : 187 : /************************************************************************/ 188 : /* IncDirtyBlocks() */ 189 : /************************************************************************/ 190 : 191 : /** 192 : * \brief Increment/decrement the number of dirty blocks 193 : */ 194 : 195 796480 : void GDALAbstractBandBlockCache::IncDirtyBlocks(int nInc) 196 : { 197 796480 : CPLAtomicAdd(&m_nDirtyBlocks, nInc); 198 796482 : } 199 : 200 : /************************************************************************/ 201 : /* StartDirtyBlockFlushingLog() */ 202 : /************************************************************************/ 203 : 204 999147 : void GDALAbstractBandBlockCache::StartDirtyBlockFlushingLog() 205 : { 206 999147 : m_nInitialDirtyBlocksInFlushCache = 0; 207 999147 : 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 999147 : } 218 : 219 : /************************************************************************/ 220 : /* UpdateDirtyBlockFlushingLog() */ 221 : /************************************************************************/ 222 : 223 217363 : void GDALAbstractBandBlockCache::UpdateDirtyBlockFlushingLog() 224 : { 225 : // Poor man progress report for console applications 226 217363 : 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 217363 : } 258 : 259 : /************************************************************************/ 260 : /* EndDirtyBlockFlushingLog() */ 261 : /************************************************************************/ 262 : 263 999151 : void GDALAbstractBandBlockCache::EndDirtyBlockFlushingLog() 264 : { 265 999151 : m_nInitialDirtyBlocksInFlushCache = 0; 266 999151 : m_nLastTick = -1; 267 999151 : } 268 : 269 : //! @endcond