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