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 * Permission is hereby granted, free of charge, to any person obtaining a
11 * copy of this software and associated documentation files (the "Software"),
12 * to deal in the Software without restriction, including without limitation
13 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 * and/or sell copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 ****************************************************************************/
28
29 #include "cpl_port.h"
30 #include "gdal_priv.h"
31
32 #include <algorithm>
33 #include <cstddef>
34 #include <new>
35
36 #include "cpl_atomic_ops.h"
37 #include "cpl_error.h"
38 #include "cpl_multiproc.h"
39
40 //! @cond Doxygen_Suppress
41
42 CPL_CVSID("$Id: gdalabstractbandblockcache.cpp 2cef454ab06723a32379690b46581a40eb2e5af2 2019-11-13 16:36:03 +0100 Even Rouault $")
43
44 #ifdef DEBUG_VERBOSE_ABBC
45 static int nAllBandsKeptAlivedBlocks = 0;
46 #endif
47
48 /************************************************************************/
49 /* GDALArrayBandBlockCache() */
50 /************************************************************************/
51
GDALAbstractBandBlockCache(GDALRasterBand * poBandIn)52 GDALAbstractBandBlockCache::GDALAbstractBandBlockCache(
53 GDALRasterBand* poBandIn ) :
54 hSpinLock(CPLCreateLock(LOCK_SPIN)),
55 hCond(CPLCreateCond()),
56 hCondMutex(CPLCreateMutex()),
57 poBand(poBandIn)
58 {
59 if( hCondMutex )
60 CPLReleaseMutex(hCondMutex);
61 }
62
63 /************************************************************************/
64 /* ~GDALAbstractBandBlockCache() */
65 /************************************************************************/
66
~GDALAbstractBandBlockCache()67 GDALAbstractBandBlockCache::~GDALAbstractBandBlockCache()
68 {
69 CPLAssert(nKeepAliveCounter == 0);
70 FreeDanglingBlocks();
71 if( hSpinLock )
72 CPLDestroyLock(hSpinLock);
73 if( hCondMutex )
74 CPLDestroyMutex(hCondMutex);
75 if( hCond )
76 CPLDestroyCond(hCond);
77 }
78
79 /************************************************************************/
80 /* UnreferenceBlockBase() */
81 /* */
82 /* This is called by GDALRasterBlock::Internalize() and */
83 /* FlushCacheBlock() when they remove a block from the linked list */
84 /* but haven't yet flushed it to disk or recovered its pData member*/
85 /* We must be aware that they are blocks in that state, since the */
86 /* band must be kept alive while AddBlockToFreeList() hasn't been */
87 /* called (in case a block is being flushed while the final */
88 /* FlushCache() of the main thread of the dataset is running). */
89 /************************************************************************/
90
UnreferenceBlockBase()91 void GDALAbstractBandBlockCache::UnreferenceBlockBase()
92 {
93 CPLAtomicInc(&nKeepAliveCounter);
94 }
95
96 /************************************************************************/
97 /* AddBlockToFreeList() */
98 /* */
99 /* This is called by GDALRasterBlock::Internalize() and */
100 /* FlushCacheBlock() after they have been finished with a block. */
101 /************************************************************************/
102
AddBlockToFreeList(GDALRasterBlock * poBlock)103 void GDALAbstractBandBlockCache::AddBlockToFreeList( GDALRasterBlock *poBlock )
104 {
105 CPLAssert(poBlock->poPrevious == nullptr);
106 CPLAssert(poBlock->poNext == nullptr);
107 {
108 #ifdef DEBUG_VERBOSE_ABBC
109 CPLAtomicInc(&nAllBandsKeptAlivedBlocks);
110 fprintf(stderr, "AddBlockToFreeList(): nAllBandsKeptAlivedBlocks=%d\n", nAllBandsKeptAlivedBlocks);/*ok*/
111 #endif
112 CPLLockHolderOptionalLockD(hSpinLock);
113 poBlock->poNext = psListBlocksToFree;
114 psListBlocksToFree = poBlock;
115 }
116
117 // If no more blocks in transient state, then warn WaitCompletionPendingTasks()
118 CPLAcquireMutex(hCondMutex, 1000);
119 if( CPLAtomicDec(&nKeepAliveCounter) == 0 )
120 {
121 CPLCondSignal(hCond);
122 }
123 CPLReleaseMutex(hCondMutex);
124 }
125
126 /************************************************************************/
127 /* WaitCompletionPendingTasks() */
128 /************************************************************************/
129
WaitCompletionPendingTasks()130 void GDALAbstractBandBlockCache::WaitCompletionPendingTasks()
131 {
132 #ifdef DEBUG_VERBOSE
133 CPLDebug("GDAL", "WaitCompletionPendingTasks()");
134 #endif
135
136 CPLAcquireMutex(hCondMutex, 1000);
137 while( nKeepAliveCounter != 0 )
138 {
139 CPLDebug( "GDAL", "Waiting for other thread to finish working with our "
140 "blocks" );
141 CPLCondWait(hCond, hCondMutex);
142 }
143 CPLReleaseMutex(hCondMutex);
144 }
145
146 /************************************************************************/
147 /* FreeDanglingBlocks() */
148 /************************************************************************/
149
FreeDanglingBlocks()150 void GDALAbstractBandBlockCache::FreeDanglingBlocks()
151 {
152 GDALRasterBlock* poList;
153 {
154 CPLLockHolderOptionalLockD(hSpinLock);
155 poList = psListBlocksToFree;
156 psListBlocksToFree = nullptr;
157 }
158 while( poList )
159 {
160 #ifdef DEBUG_VERBOSE_ABBC
161 CPLAtomicDec(&nAllBandsKeptAlivedBlocks);
162 fprintf(stderr, "FreeDanglingBlocks(): nAllBandsKeptAlivedBlocks=%d\n", nAllBandsKeptAlivedBlocks);/*ok*/
163 #endif
164 GDALRasterBlock* poNext = poList->poNext;
165 poList->poNext = nullptr;
166 delete poList;
167 poList = poNext;
168 }
169 }
170
171 /************************************************************************/
172 /* CreateBlock() */
173 /************************************************************************/
174
CreateBlock(int nXBlockOff,int nYBlockOff)175 GDALRasterBlock* GDALAbstractBandBlockCache::CreateBlock(int nXBlockOff,
176 int nYBlockOff)
177 {
178 GDALRasterBlock* poBlock;
179 {
180 CPLLockHolderOptionalLockD(hSpinLock);
181 poBlock = psListBlocksToFree;
182 if( poBlock )
183 {
184 #ifdef DEBUG_VERBOSE_ABBC
185 CPLAtomicDec(&nAllBandsKeptAlivedBlocks);
186 fprintf(stderr, "CreateBlock(): nAllBandsKeptAlivedBlocks=%d\n", nAllBandsKeptAlivedBlocks);/*ok*/
187 #endif
188 psListBlocksToFree = poBlock->poNext;
189 }
190 }
191 if( poBlock )
192 poBlock->RecycleFor(nXBlockOff, nYBlockOff);
193 else
194 poBlock = new (std::nothrow) GDALRasterBlock(
195 poBand, nXBlockOff, nYBlockOff );
196 return poBlock;
197 }
198
199 /************************************************************************/
200 /* IncDirtyBlocks() */
201 /************************************************************************/
202
203 /**
204 * \brief Increment/decrement the number of dirty blocks
205 */
206
IncDirtyBlocks(int nInc)207 void GDALAbstractBandBlockCache::IncDirtyBlocks( int nInc )
208 {
209 CPLAtomicAdd(&m_nDirtyBlocks, nInc);
210 }
211
212 /************************************************************************/
213 /* StartDirtyBlockFlushingLog() */
214 /************************************************************************/
215
StartDirtyBlockFlushingLog()216 void GDALAbstractBandBlockCache::StartDirtyBlockFlushingLog()
217 {
218 m_nInitialDirtyBlocksInFlushCache = 0;
219 if( m_nDirtyBlocks > 0 && CPLIsDefaultErrorHandlerAndCatchDebug() )
220 {
221 const char *pszDebug = CPLGetConfigOption("CPL_DEBUG", nullptr);
222 if( pszDebug && (EQUAL(pszDebug, "ON") || EQUAL(pszDebug, "GDAL")) &&
223 CPLGetConfigOption("GDAL_REPORT_DIRTY_BLOCK_FLUSHING", nullptr) == nullptr )
224 {
225 m_nInitialDirtyBlocksInFlushCache = m_nDirtyBlocks;
226 m_nLastTick = -1;
227 }
228 }
229 }
230
231 /************************************************************************/
232 /* UpdateDirtyBlockFlushingLog() */
233 /************************************************************************/
234
UpdateDirtyBlockFlushingLog()235 void GDALAbstractBandBlockCache::UpdateDirtyBlockFlushingLog()
236 {
237 // Poor man progress report for console applications
238 if( m_nInitialDirtyBlocksInFlushCache )
239 {
240 const auto nRemainingDirtyBlocks = m_nDirtyBlocks;
241 const auto nFlushedBlocks =
242 m_nInitialDirtyBlocksInFlushCache - nRemainingDirtyBlocks + 1;
243 const double dfComplete = double(nFlushedBlocks) / m_nInitialDirtyBlocksInFlushCache;
244 const int nThisTick = std::min(40, std::max(0,
245 static_cast<int>(dfComplete * 40.0) ));
246 if( nThisTick > m_nLastTick )
247 {
248 if( m_nLastTick < 0 )
249 {
250 fprintf(stderr, "GDAL: Flushing dirty blocks: "); /*ok*/
251 fflush(stderr); /*ok*/
252 }
253 while( nThisTick > m_nLastTick )
254 {
255 ++m_nLastTick;
256 if( m_nLastTick % 4 == 0 )
257 fprintf( stderr, "%d", (m_nLastTick / 4) * 10 ); /*ok*/
258 else
259 fprintf( stderr, "." ); /*ok*/
260 }
261
262 if( nThisTick == 40 )
263 fprintf( stderr, " - done.\n" ); /*ok*/
264 else
265 fflush( stderr ); /*ok*/
266 }
267 }
268 }
269
270 /************************************************************************/
271 /* EndDirtyBlockFlushingLog() */
272 /************************************************************************/
273
EndDirtyBlockFlushingLog()274 void GDALAbstractBandBlockCache::EndDirtyBlockFlushingLog()
275 {
276 m_nInitialDirtyBlocksInFlushCache = 0;
277 m_nLastTick = -1;
278 }
279
280 //! @endcond
281