1 /******************************************************************************
2  *
3  * Project:  GDAL Core
4  * Purpose:  Implementation of GDALRasterBlock class and related global
5  *           raster block cache management.
6  * Author:   Frank Warmerdam, warmerdam@pobox.com
7  *
8  **********************************************************************
9  * Copyright (c) 1998, Frank Warmerdam <warmerdam@pobox.com>
10  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ****************************************************************************/
30 
31 #include "cpl_port.h"
32 #include "gdal.h"
33 #include "gdal_priv.h"
34 
35 #include <algorithm>
36 #include <climits>
37 #include <cstring>
38 
39 #include "cpl_atomic_ops.h"
40 #include "cpl_conv.h"
41 #include "cpl_error.h"
42 #include "cpl_multiproc.h"
43 #include "cpl_string.h"
44 #include "cpl_vsi.h"
45 
46 CPL_CVSID("$Id: gdalrasterblock.cpp b9374618c144dc2ac20c6cad2eb7404b0f6e7cb4 2021-08-20 15:31:28 +0200 Even Rouault $")
47 
48 static bool bCacheMaxInitialized = false;
49 // Will later be overridden by the default 5% if GDAL_CACHEMAX not defined.
50 static GIntBig nCacheMax = 40 * 1024 * 1024;
51 static volatile GIntBig nCacheUsed = 0;
52 
53 static GDALRasterBlock *poOldest = nullptr;  // Tail.
54 static GDALRasterBlock *poNewest = nullptr;  // Head.
55 
56 static int nDisableDirtyBlockFlushCounter = 0;
57 
58 #if 0
59 static CPLMutex *hRBLock = nullptr;
60 #define INITIALIZE_LOCK CPLMutexHolderD( &hRBLock )
61 #define TAKE_LOCK       CPLMutexHolderOptionalLockD( hRBLock )
62 #define DESTROY_LOCK    CPLDestroyMutex( hRBLock )
63 #else
64 
65 static CPLLock* hRBLock = nullptr;
66 static bool bDebugContention = false;
67 static bool bSleepsForBockCacheDebug = false;
GetLockType()68 static CPLLockType GetLockType()
69 {
70     static int nLockType = -1;
71     if( nLockType < 0 )
72     {
73         const char* pszLockType =
74             CPLGetConfigOption("GDAL_RB_LOCK_TYPE", "ADAPTIVE");
75         if( EQUAL(pszLockType, "ADAPTIVE") )
76             nLockType = LOCK_ADAPTIVE_MUTEX;
77         else if( EQUAL(pszLockType, "RECURSIVE") )
78             nLockType = LOCK_RECURSIVE_MUTEX;
79         else if( EQUAL(pszLockType, "SPIN") )
80             nLockType = LOCK_SPIN;
81         else
82         {
83             CPLError(
84                 CE_Warning, CPLE_NotSupported,
85                 "GDAL_RB_LOCK_TYPE=%s not supported. Falling back to ADAPTIVE",
86                 pszLockType);
87             nLockType = LOCK_ADAPTIVE_MUTEX;
88         }
89         bDebugContention = CPLTestBool(
90             CPLGetConfigOption("GDAL_RB_LOCK_DEBUG_CONTENTION", "NO"));
91     }
92     return static_cast<CPLLockType>(nLockType);
93 }
94 
95 #define INITIALIZE_LOCK         CPLLockHolderD( &hRBLock, GetLockType() ); \
96                                 CPLLockSetDebugPerf(hRBLock, bDebugContention)
97 #define TAKE_LOCK               CPLLockHolderOptionalLockD( hRBLock )
98 #define DESTROY_LOCK            CPLDestroyLock( hRBLock )
99 
100 #endif
101 
102 //#define ENABLE_DEBUG
103 
104 /************************************************************************/
105 /*                          GDALSetCacheMax()                           */
106 /************************************************************************/
107 
108 /**
109  * \brief Set maximum cache memory.
110  *
111  * This function sets the maximum amount of memory that GDAL is permitted
112  * to use for GDALRasterBlock caching. The unit of the value is bytes.
113  *
114  * The maximum value is 2GB, due to the use of a signed 32 bit integer.
115  * Use GDALSetCacheMax64() to be able to set a higher value.
116  *
117  * @param nNewSizeInBytes the maximum number of bytes for caching.
118  */
119 
GDALSetCacheMax(int nNewSizeInBytes)120 void CPL_STDCALL GDALSetCacheMax( int nNewSizeInBytes )
121 
122 {
123     GDALSetCacheMax64(nNewSizeInBytes);
124 }
125 
126 /************************************************************************/
127 /*                        GDALSetCacheMax64()                           */
128 /************************************************************************/
129 
130 /**
131  * \brief Set maximum cache memory.
132  *
133  * This function sets the maximum amount of memory that GDAL is permitted
134  * to use for GDALRasterBlock caching. The unit of the value is bytes.
135  *
136  * Note: On 32 bit platforms, the maximum amount of memory that can be addressed
137  * by a process might be 2 GB or 3 GB, depending on the operating system
138  * capabilities. This function will not make any attempt to check the
139  * consistency of the passed value with the effective capabilities of the OS.
140  *
141  * @param nNewSizeInBytes the maximum number of bytes for caching.
142  *
143  * @since GDAL 1.8.0
144  */
145 
GDALSetCacheMax64(GIntBig nNewSizeInBytes)146 void CPL_STDCALL GDALSetCacheMax64( GIntBig nNewSizeInBytes )
147 
148 {
149 #if 0
150     if( nNewSizeInBytes == 12346789 )
151     {
152         GDALRasterBlock::DumpAll();
153         return;
154     }
155 #endif
156 
157     {
158         INITIALIZE_LOCK;
159     }
160     bCacheMaxInitialized = true;
161     nCacheMax = nNewSizeInBytes;
162 
163 /* -------------------------------------------------------------------- */
164 /*      Flush blocks till we are under the new limit or till we         */
165 /*      can't seem to flush anymore.                                    */
166 /* -------------------------------------------------------------------- */
167     while( nCacheUsed > nCacheMax )
168     {
169         const GIntBig nOldCacheUsed = nCacheUsed;
170 
171         GDALFlushCacheBlock();
172 
173         if( nCacheUsed == nOldCacheUsed )
174             break;
175     }
176 }
177 
178 /************************************************************************/
179 /*                          GDALGetCacheMax()                           */
180 /************************************************************************/
181 
182 /**
183  * \brief Get maximum cache memory.
184  *
185  * Gets the maximum amount of memory available to the GDALRasterBlock
186  * caching system for caching GDAL read/write imagery.
187  *
188  * The first type this function is called, it will read the GDAL_CACHEMAX
189  * configuration option to initialize the maximum cache memory.
190  * Starting with GDAL 2.1, the value can be expressed as x% of the usable
191  * physical RAM (which may potentially be used by other processes). Otherwise
192  * it is expected to be a value in MB.
193  *
194  * This function cannot return a value higher than 2 GB. Use
195  * GDALGetCacheMax64() to get a non-truncated value.
196  *
197  * @return maximum in bytes.
198  */
199 
GDALGetCacheMax()200 int CPL_STDCALL GDALGetCacheMax()
201 {
202     GIntBig nRes = GDALGetCacheMax64();
203     if (nRes > INT_MAX)
204     {
205         static bool bHasWarned = false;
206         if (!bHasWarned)
207         {
208             CPLError( CE_Warning, CPLE_AppDefined,
209                       "Cache max value doesn't fit on a 32 bit integer. "
210                       "Call GDALGetCacheMax64() instead" );
211             bHasWarned = true;
212         }
213         nRes = INT_MAX;
214     }
215     return static_cast<int>(nRes);
216 }
217 
218 /************************************************************************/
219 /*                         GDALGetCacheMax64()                          */
220 /************************************************************************/
221 
222 /**
223  * \brief Get maximum cache memory.
224  *
225  * Gets the maximum amount of memory available to the GDALRasterBlock
226  * caching system for caching GDAL read/write imagery.
227  *
228  * The first type this function is called, it will read the GDAL_CACHEMAX
229  * configuration option to initialize the maximum cache memory.
230  * Starting with GDAL 2.1, the value can be expressed as x% of the usable
231  * physical RAM (which may potentially be used by other processes). Otherwise
232  * it is expected to be a value in MB.
233  *
234  * @return maximum in bytes.
235  *
236  * @since GDAL 1.8.0
237  */
238 
GDALGetCacheMax64()239 GIntBig CPL_STDCALL GDALGetCacheMax64()
240 {
241     if( !bCacheMaxInitialized )
242     {
243         {
244             INITIALIZE_LOCK;
245         }
246         bSleepsForBockCacheDebug = CPLTestBool(
247             CPLGetConfigOption("GDAL_DEBUG_BLOCK_CACHE", "NO"));
248 
249         const char* pszCacheMax = CPLGetConfigOption("GDAL_CACHEMAX","5%");
250 
251         GIntBig nNewCacheMax;
252         if( strchr(pszCacheMax, '%') != nullptr )
253         {
254             GIntBig nUsablePhysicalRAM = CPLGetUsablePhysicalRAM();
255             if( nUsablePhysicalRAM > 0 )
256             {
257                 // For some reason, coverity pretends that this will overflow.
258                 // "Multiply operation overflows on operands static_cast<double>(
259                 // nUsablePhysicalRAM ) and CPLAtof(pszCacheMax). Example values for
260                 // operands: CPLAtof( pszCacheMax ) = 2251799813685248,
261                 // static_cast<double>(nUsablePhysicalRAM) = -9223372036854775808."
262                 // coverity[overflow,tainted_data]
263                 double dfCacheMax =
264                     static_cast<double>(nUsablePhysicalRAM) *
265                     CPLAtof(pszCacheMax) / 100.0;
266                 if( dfCacheMax >= 0 && dfCacheMax < 1e15 )
267                     nNewCacheMax = static_cast<GIntBig>(dfCacheMax);
268                 else
269                     nNewCacheMax = nCacheMax;
270             }
271             else
272             {
273                 CPLDebug("GDAL",
274                          "Cannot determine usable physical RAM.");
275                 nNewCacheMax = nCacheMax;
276             }
277         }
278         else
279         {
280             nNewCacheMax = CPLAtoGIntBig(pszCacheMax);
281             if( nNewCacheMax < 100000 )
282             {
283                 if (nNewCacheMax < 0)
284                 {
285                     CPLError(
286                         CE_Failure, CPLE_NotSupported,
287                         "Invalid value for GDAL_CACHEMAX. "
288                         "Using default value.");
289                     GIntBig nUsablePhysicalRAM = CPLGetUsablePhysicalRAM();
290                     if( nUsablePhysicalRAM )
291                         nNewCacheMax = nUsablePhysicalRAM / 20;
292                     else
293                     {
294                         CPLDebug("GDAL",
295                                  "Cannot determine usable physical RAM.");
296                         nNewCacheMax = nCacheMax;
297                     }
298                 }
299                 else
300                 {
301                     nNewCacheMax *= 1024 * 1024;
302                 }
303             }
304         }
305         nCacheMax = nNewCacheMax;
306         CPLDebug( "GDAL", "GDAL_CACHEMAX = " CPL_FRMT_GIB " MB",
307                   nCacheMax / (1024 * 1024));
308         bCacheMaxInitialized = true;
309     }
310     // coverity[overflow_sink]
311     return nCacheMax;
312 }
313 
314 /************************************************************************/
315 /*                          GDALGetCacheUsed()                          */
316 /************************************************************************/
317 
318 /**
319  * \brief Get cache memory used.
320  *
321  * @return the number of bytes of memory currently in use by the
322  * GDALRasterBlock memory caching.
323  */
324 
GDALGetCacheUsed()325 int CPL_STDCALL GDALGetCacheUsed()
326 {
327     if (nCacheUsed > INT_MAX)
328     {
329         static bool bHasWarned = false;
330         if (!bHasWarned)
331         {
332             CPLError( CE_Warning, CPLE_AppDefined,
333                       "Cache used value doesn't fit on a 32 bit integer. "
334                       "Call GDALGetCacheUsed64() instead" );
335             bHasWarned = true;
336         }
337         return INT_MAX;
338     }
339     return static_cast<int>(nCacheUsed);
340 }
341 
342 /************************************************************************/
343 /*                        GDALGetCacheUsed64()                          */
344 /************************************************************************/
345 
346 /**
347  * \brief Get cache memory used.
348  *
349  * @return the number of bytes of memory currently in use by the
350  * GDALRasterBlock memory caching.
351  *
352  * @since GDAL 1.8.0
353  */
354 
GDALGetCacheUsed64()355 GIntBig CPL_STDCALL GDALGetCacheUsed64() { return nCacheUsed; }
356 
357 /************************************************************************/
358 /*                        GDALFlushCacheBlock()                         */
359 /*                                                                      */
360 /*      The workhorse of cache management!                              */
361 /************************************************************************/
362 
363 /**
364  * \brief Try to flush one cached raster block
365  *
366  * This function will search the first unlocked raster block and will
367  * flush it to release the associated memory.
368  *
369  * @return TRUE if one block was flushed, FALSE if there are no cached blocks
370  *         or if they are currently locked.
371  */
GDALFlushCacheBlock()372 int CPL_STDCALL GDALFlushCacheBlock()
373 
374 {
375     return GDALRasterBlock::FlushCacheBlock();
376 }
377 
378 /************************************************************************/
379 /* ==================================================================== */
380 /*                           GDALRasterBlock                            */
381 /* ==================================================================== */
382 /************************************************************************/
383 
384 /**
385  * \class GDALRasterBlock "gdal_priv.h"
386  *
387  * GDALRasterBlock objects hold one block of raster data for one band
388  * that is currently stored in the GDAL raster cache.  The cache holds
389  * some blocks of raster data for zero or more GDALRasterBand objects
390  * across zero or more GDALDataset objects in a global raster cache with
391  * a least recently used (LRU) list and an upper cache limit (see
392  * GDALSetCacheMax()) under which the cache size is normally kept.
393  *
394  * Some blocks in the cache may be modified relative to the state on disk
395  * (they are marked "Dirty") and must be flushed to disk before they can
396  * be discarded.  Other (Clean) blocks may just be discarded if their memory
397  * needs to be recovered.
398  *
399  * In normal situations applications do not interact directly with the
400  * GDALRasterBlock - instead it it utilized by the RasterIO() interfaces
401  * to implement caching.
402  *
403  * Some driver classes are implemented in a fashion that completely avoids
404  * use of the GDAL raster cache (and GDALRasterBlock) though this is not very
405  * common.
406  */
407 
408 /************************************************************************/
409 /*                          FlushCacheBlock()                           */
410 /*                                                                      */
411 /*      Note, if we have a lot of blocks locked for a long time, this    */
412 /*      method is going to get slow because it will have to traverse    */
413 /*      the linked list a long ways looking for a flushing              */
414 /*      candidate.   It might help to re-touch locked blocks to push    */
415 /*      them to the top of the list.                                    */
416 /************************************************************************/
417 
418 /**
419  * \brief Attempt to flush at least one block from the cache.
420  *
421  * This static method is normally used to recover memory when a request
422  * for a new cache block would put cache memory use over the established
423  * limit.
424  *
425  * C++ analog to the C function GDALFlushCacheBlock().
426  *
427  * @param bDirtyBlocksOnly Only flushes dirty blocks.
428  * @return TRUE if successful or FALSE if no flushable block is found.
429  */
430 
FlushCacheBlock(int bDirtyBlocksOnly)431 int GDALRasterBlock::FlushCacheBlock( int bDirtyBlocksOnly )
432 
433 {
434     GDALRasterBlock *poTarget;
435 
436     {
437         INITIALIZE_LOCK;
438         poTarget = poOldest;
439 
440         while( poTarget != nullptr )
441         {
442             if( !bDirtyBlocksOnly ||
443                 (poTarget->GetDirty() && nDisableDirtyBlockFlushCounter == 0) )
444             {
445                 if( CPLAtomicCompareAndExchange(
446                         &(poTarget->nLockCount), 0, -1) )
447                     break;
448             }
449             poTarget = poTarget->poPrevious;
450         }
451 
452         if( poTarget == nullptr )
453             return FALSE;
454         if( bSleepsForBockCacheDebug )
455         {
456             // coverity[tainted_data]
457             const double dfDelay = CPLAtof(
458                 CPLGetConfigOption(
459                     "GDAL_RB_FLUSHBLOCK_SLEEP_AFTER_DROP_LOCK", "0"));
460             if( dfDelay > 0 )
461                 CPLSleep(dfDelay);
462         }
463 
464         poTarget->Detach_unlocked();
465         poTarget->GetBand()->UnreferenceBlock(poTarget);
466     }
467 
468     if( bSleepsForBockCacheDebug )
469     {
470         // coverity[tainted_data]
471         const double dfDelay = CPLAtof(
472             CPLGetConfigOption("GDAL_RB_FLUSHBLOCK_SLEEP_AFTER_RB_LOCK", "0"));
473         if( dfDelay > 0 )
474             CPLSleep(dfDelay);
475     }
476 
477     if( poTarget->GetDirty() )
478     {
479         const CPLErr eErr = poTarget->Write();
480         if( eErr != CE_None )
481         {
482             // Save the error for later reporting.
483             poTarget->GetBand()->SetFlushBlockErr(eErr);
484         }
485     }
486 
487     VSIFreeAligned(poTarget->pData);
488     poTarget->pData = nullptr;
489     poTarget->GetBand()->AddBlockToFreeList(poTarget);
490 
491     return TRUE;
492 }
493 
494 /************************************************************************/
495 /*                          FlushDirtyBlocks()                          */
496 /************************************************************************/
497 
498 /**
499  * \brief Flush all dirty blocks from cache.
500  *
501  * This static method is normally used to recover memory and is especially
502  * useful when doing multi-threaded code that can trigger the block cache.
503  *
504  * Due to the current design of the block cache, dirty blocks belonging to a
505  * same dataset could be pushed simultaneously to the IWriteBlock() method of
506  * that dataset from different threads, causing races.
507  *
508  * Calling this method before that code can help workarounding that issue,
509  * in a multiple readers, one writer scenario.
510  *
511  * @since GDAL 2.0
512  */
513 
FlushDirtyBlocks()514 void GDALRasterBlock::FlushDirtyBlocks()
515 
516 {
517     while( FlushCacheBlock(TRUE) )
518     {
519         /* go on */
520     }
521 }
522 
523 /************************************************************************/
524 /*                      EnterDisableDirtyBlockFlush()                   */
525 /************************************************************************/
526 
527 /**
528  * \brief Starts preventing dirty blocks from being flushed
529  *
530  * This static method is used to prevent dirty blocks from being flushed.
531  * This might be useful when in a IWriteBlock() method, whose implementation
532  * can directly/indirectly cause the block cache to evict new blocks, to
533  * be recursively called on the same dataset.
534  *
535  * This method implements a reference counter and is thread-safe.
536  *
537  * This call must be paired with a corresponding LeaveDisableDirtyBlockFlush().
538  *
539  * @since GDAL 2.2.2
540  */
541 
EnterDisableDirtyBlockFlush()542 void GDALRasterBlock::EnterDisableDirtyBlockFlush()
543 {
544     CPLAtomicInc(&nDisableDirtyBlockFlushCounter);
545 }
546 
547 /************************************************************************/
548 /*                      LeaveDisableDirtyBlockFlush()                   */
549 /************************************************************************/
550 
551 /**
552  * \brief Ends preventing dirty blocks from being flushed.
553  *
554  * Undoes the effect of EnterDisableDirtyBlockFlush().
555  *
556  * @since GDAL 2.2.2
557  */
558 
LeaveDisableDirtyBlockFlush()559 void GDALRasterBlock::LeaveDisableDirtyBlockFlush()
560 {
561     CPLAtomicDec(&nDisableDirtyBlockFlushCounter);
562 }
563 
564 /************************************************************************/
565 /*                          GDALRasterBlock()                           */
566 /************************************************************************/
567 
568 /**
569  * @brief GDALRasterBlock Constructor
570  *
571  * Normally only called from GDALRasterBand::GetLockedBlockRef().
572  *
573  * @param poBandIn the raster band used as source of raster block
574  * being constructed.
575  *
576  * @param nXOffIn the horizontal block offset, with zero indicating
577  * the left most block, 1 the next block and so forth.
578  *
579  * @param nYOffIn the vertical block offset, with zero indicating
580  * the top most block, 1 the next block and so forth.
581  */
582 
GDALRasterBlock(GDALRasterBand * poBandIn,int nXOffIn,int nYOffIn)583 GDALRasterBlock::GDALRasterBlock( GDALRasterBand *poBandIn,
584                                   int nXOffIn, int nYOffIn ) :
585     eType(poBandIn->GetRasterDataType()),
586     bDirty(false),
587     nLockCount(0),
588     nXOff(nXOffIn),
589     nYOff(nYOffIn),
590     nXSize(0),
591     nYSize(0),
592     pData(nullptr),
593     poBand(poBandIn),
594     poNext(nullptr),
595     poPrevious(nullptr),
596     bMustDetach(true)
597 {
598     CPLAssert( poBandIn != nullptr );
599     poBand->GetBlockSize( &nXSize, &nYSize );
600 }
601 
602 /************************************************************************/
603 /*                          GDALRasterBlock()                           */
604 /************************************************************************/
605 
606 /**
607  * @brief GDALRasterBlock Constructor (for GDALHashSetBandBlockAccess purpose)
608  *
609  * Normally only called from GDALHashSetBandBlockAccess class. Such a block
610  * is completely non functional and only meant as being used to do a look-up
611  * in the hash set of GDALHashSetBandBlockAccess
612  *
613  * @param nXOffIn the horizontal block offset, with zero indicating
614  * the left most block, 1 the next block and so forth.
615  *
616  * @param nYOffIn the vertical block offset, with zero indicating
617  * the top most block, 1 the next block and so forth.
618  */
619 
GDALRasterBlock(int nXOffIn,int nYOffIn)620 GDALRasterBlock::GDALRasterBlock( int nXOffIn, int nYOffIn ) :
621     eType(GDT_Unknown),
622     bDirty(false),
623     nLockCount(0),
624     nXOff(nXOffIn),
625     nYOff(nYOffIn),
626     nXSize(0),
627     nYSize(0),
628     pData(nullptr),
629     poBand(nullptr),
630     poNext(nullptr),
631     poPrevious(nullptr),
632     bMustDetach(false)
633 {}
634 
635 /************************************************************************/
636 /*                                  RecycleFor()                        */
637 /************************************************************************/
638 
639 /**
640  * Recycle an existing block (of the same band)
641  *
642  * Normally called from GDALAbstractBandBlockCache::CreateBlock().
643  */
644 
RecycleFor(int nXOffIn,int nYOffIn)645 void GDALRasterBlock::RecycleFor( int nXOffIn, int nYOffIn )
646 {
647     CPLAssert(pData == nullptr);
648     pData = nullptr;
649     bDirty = false;
650     nLockCount = 0;
651 
652     poNext = nullptr;
653     poPrevious = nullptr;
654 
655     nXOff = nXOffIn;
656     nYOff = nYOffIn;
657     bMustDetach = true;
658 }
659 
660 /************************************************************************/
661 /*                          ~GDALRasterBlock()                          */
662 /************************************************************************/
663 
664 /**
665  * Block destructor.
666  *
667  * Normally called from GDALRasterBand::FlushBlock().
668  */
669 
~GDALRasterBlock()670 GDALRasterBlock::~GDALRasterBlock()
671 
672 {
673     Detach();
674 
675     if( pData != nullptr )
676     {
677         VSIFreeAligned( pData );
678     }
679 
680     CPLAssert( nLockCount <= 0 );
681 
682 #ifdef ENABLE_DEBUG
683     Verify();
684 #endif
685 }
686 
687 /************************************************************************/
688 /*                        GetEffectiveBlockSize()                       */
689 /************************************************************************/
690 
GetEffectiveBlockSize(GPtrDiff_t nBlockSize)691 static size_t GetEffectiveBlockSize(GPtrDiff_t nBlockSize)
692 {
693     // The real cost of a block allocation is more than just nBlockSize
694     // As we allocate with 64-byte alignment, use 64 as a multiple.
695     // We arbitrarily add 2 * sizeof(GDALRasterBlock) to account for that
696     return static_cast<size_t>(
697         std::min(static_cast<GUIntBig>(UINT_MAX),
698                     static_cast<GUIntBig>(DIV_ROUND_UP(nBlockSize, 64)) * 64 +
699                         2 * sizeof(GDALRasterBlock)));
700 }
701 
702 /************************************************************************/
703 /*                               Detach()                               */
704 /************************************************************************/
705 
706 /**
707  * Remove block from cache.
708  *
709  * This method removes the current block from the linked list used to keep
710  * track of all cached blocks in order of age.  It does not affect whether
711  * the block is referenced by a GDALRasterBand nor does it destroy or flush
712  * the block.
713  */
714 
Detach()715 void GDALRasterBlock::Detach()
716 
717 {
718     if( bMustDetach )
719     {
720         TAKE_LOCK;
721         Detach_unlocked();
722     }
723 }
724 
Detach_unlocked()725 void GDALRasterBlock::Detach_unlocked()
726 {
727     if( poOldest == this )
728         poOldest = poPrevious;
729 
730     if( poNewest == this )
731     {
732         poNewest = poNext;
733     }
734 
735     if( poPrevious != nullptr )
736         poPrevious->poNext = poNext;
737 
738     if( poNext != nullptr )
739         poNext->poPrevious = poPrevious;
740 
741     poPrevious = nullptr;
742     poNext = nullptr;
743     bMustDetach = false;
744 
745     if( pData )
746         nCacheUsed -= GetEffectiveBlockSize(GetBlockSize());
747 
748 #ifdef ENABLE_DEBUG
749     Verify();
750 #endif
751 }
752 
753 /************************************************************************/
754 /*                               Verify()                               */
755 /************************************************************************/
756 
757 /**
758  * Confirms (via assertions) that the block cache linked list is in a
759  * consistent state.
760  */
761 
762 #ifdef ENABLE_DEBUG
Verify()763 void GDALRasterBlock::Verify()
764 
765 {
766     TAKE_LOCK;
767 
768     CPLAssert( (poNewest == nullptr && poOldest == nullptr)
769                || (poNewest != nullptr && poOldest != nullptr) );
770 
771     if( poNewest != nullptr )
772     {
773         CPLAssert( poNewest->poPrevious == nullptr );
774         CPLAssert( poOldest->poNext == nullptr );
775 
776         GDALRasterBlock* poLast = nullptr;
777         for( GDALRasterBlock *poBlock = poNewest;
778              poBlock != nullptr;
779              poBlock = poBlock->poNext )
780         {
781             CPLAssert( poBlock->poPrevious == poLast );
782 
783             poLast = poBlock;
784         }
785 
786         CPLAssert( poOldest == poLast );
787     }
788 }
789 
790 #else
Verify()791 void GDALRasterBlock::Verify() {}
792 #endif
793 
794 #ifdef notdef
CheckNonOrphanedBlocks(GDALRasterBand * poBand)795 void GDALRasterBlock::CheckNonOrphanedBlocks( GDALRasterBand* poBand )
796 {
797     TAKE_LOCK;
798     for( GDALRasterBlock *poBlock = poNewest;
799                           poBlock != nullptr;
800                           poBlock = poBlock->poNext )
801     {
802         if ( poBlock->GetBand() == poBand )
803         {
804             printf("Cache has still blocks of band %p\n", poBand);/*ok*/
805             printf("Band : %d\n", poBand->GetBand());/*ok*/
806             printf("nRasterXSize = %d\n", poBand->GetXSize());/*ok*/
807             printf("nRasterYSize = %d\n", poBand->GetYSize());/*ok*/
808             int nBlockXSize, nBlockYSize;
809             poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
810             printf("nBlockXSize = %d\n", nBlockXSize);/*ok*/
811             printf("nBlockYSize = %d\n", nBlockYSize);/*ok*/
812             printf("Dataset : %p\n", poBand->GetDataset());/*ok*/
813             if( poBand->GetDataset() )
814                 printf("Dataset : %s\n",/*ok*/
815                        poBand->GetDataset()->GetDescription());
816         }
817     }
818 }
819 #endif
820 
821 /************************************************************************/
822 /*                               Write()                                */
823 /************************************************************************/
824 
825 /**
826  * Force writing of the current block, if dirty.
827  *
828  * The block is written using GDALRasterBand::IWriteBlock() on its
829  * corresponding band object.  Even if the write fails the block will
830  * be marked clean.
831  *
832  * @return CE_None otherwise the error returned by IWriteBlock().
833  */
834 
Write()835 CPLErr GDALRasterBlock::Write()
836 
837 {
838     if( !GetDirty() )
839         return CE_None;
840 
841     if( poBand == nullptr )
842         return CE_Failure;
843 
844     MarkClean();
845 
846     if (poBand->eFlushBlockErr == CE_None)
847     {
848         int bCallLeaveReadWrite = poBand->EnterReadWrite(GF_Write);
849         CPLErr eErr = poBand->IWriteBlock( nXOff, nYOff, pData );
850         if( bCallLeaveReadWrite ) poBand->LeaveReadWrite();
851         return eErr;
852     }
853     else
854         return poBand->eFlushBlockErr;
855 }
856 
857 /************************************************************************/
858 /*                               Touch()                                */
859 /************************************************************************/
860 
861 /**
862  * Push block to top of LRU (least-recently used) list.
863  *
864  * This method is normally called when a block is used to keep track
865  * that it has been recently used.
866  */
867 
Touch()868 void GDALRasterBlock::Touch()
869 
870 {
871     // Can be safely tested outside the lock
872     if( poNewest == this )
873         return;
874 
875     TAKE_LOCK;
876     Touch_unlocked();
877 }
878 
Touch_unlocked()879 void GDALRasterBlock::Touch_unlocked()
880 
881 {
882     // Could happen even if tested in Touch() before taking the lock
883     // Scenario would be :
884     // 0. this is the second block (the one pointed by poNewest->poNext)
885     // 1. Thread 1 calls Touch() and poNewest != this at that point
886     // 2. Thread 2 detaches poNewest
887     // 3. Thread 1 arrives here
888     if( poNewest == this )
889         return;
890 
891     // We should not try to touch a block that has been detached.
892     // If that happen, corruption has already occurred.
893     CPLAssert(bMustDetach);
894 
895     if( poOldest == this )
896         poOldest = this->poPrevious;
897 
898     if( poPrevious != nullptr )
899         poPrevious->poNext = poNext;
900 
901     if( poNext != nullptr )
902         poNext->poPrevious = poPrevious;
903 
904     poPrevious = nullptr;
905     poNext = poNewest;
906 
907     if( poNewest != nullptr )
908     {
909         CPLAssert( poNewest->poPrevious == nullptr );
910         poNewest->poPrevious = this;
911     }
912     poNewest = this;
913 
914     if( poOldest == nullptr )
915     {
916         CPLAssert( poPrevious == nullptr && poNext == nullptr );
917         poOldest = this;
918     }
919 #ifdef ENABLE_DEBUG
920     Verify();
921 #endif
922 }
923 
924 /************************************************************************/
925 /*                            Internalize()                             */
926 /************************************************************************/
927 
928 /**
929  * Allocate memory for block.
930  *
931  * This method allocates memory for the block, and attempts to flush other
932  * blocks, if necessary, to bring the total cache size back within the limits.
933  * The newly allocated block is touched and will be considered most recently
934  * used in the LRU list.
935  *
936  * @return CE_None on success or CE_Failure if memory allocation fails.
937  */
938 
Internalize()939 CPLErr GDALRasterBlock::Internalize()
940 
941 {
942     CPLAssert( pData == nullptr );
943 
944     void        *pNewData = nullptr;
945 
946     // This call will initialize the hRBLock mutex. Other call places can
947     // only be called if we have go through there.
948     const GIntBig nCurCacheMax = GDALGetCacheMax64();
949 
950     // No risk of overflow as it is checked in GDALRasterBand::InitBlockInfo().
951     const auto nSizeInBytes = GetBlockSize();
952 
953 /* -------------------------------------------------------------------- */
954 /*      Flush old blocks if we are nearing our memory limit.            */
955 /* -------------------------------------------------------------------- */
956     bool bFirstIter = true;
957     bool bLoopAgain = false;
958     GDALDataset* poThisDS = poBand->GetDataset();
959     do
960     {
961         bLoopAgain = false;
962         GDALRasterBlock* apoBlocksToFree[64] = { nullptr };
963         int nBlocksToFree = 0;
964         {
965             TAKE_LOCK;
966 
967             if( bFirstIter )
968                 nCacheUsed += GetEffectiveBlockSize(nSizeInBytes);
969             GDALRasterBlock *poTarget = poOldest;
970             while( nCacheUsed > nCurCacheMax )
971             {
972                 GDALRasterBlock* poDirtyBlockOtherDataset = nullptr;
973                 // In this first pass, only discard dirty blocks of this
974                 // dataset. We do this to decrease significantly the likelihood
975                 // of the following weakness of the block cache design:
976                 // 1. Thread 1 fills block B with ones
977                 // 2. Thread 2 evicts this dirty block, while thread 1 almost
978                 //    at the same time (but slightly after) tries to reacquire
979                 //    this block. As it has been removed from the block cache
980                 //    array/set, thread 1 now tries to read block B from disk,
981                 //    so gets the old value.
982                 while( poTarget != nullptr )
983                 {
984                     if( !poTarget->GetDirty() )
985                     {
986                         if( CPLAtomicCompareAndExchange(
987                                 &(poTarget->nLockCount), 0, -1) )
988                             break;
989                     }
990                     else if (nDisableDirtyBlockFlushCounter == 0)
991                     {
992                         if( poTarget->poBand->GetDataset() == poThisDS )
993                         {
994                             if( CPLAtomicCompareAndExchange(
995                                     &(poTarget->nLockCount), 0, -1) )
996                                 break;
997                         }
998                         else if( poDirtyBlockOtherDataset == nullptr )
999                         {
1000                             poDirtyBlockOtherDataset = poTarget;
1001                         }
1002                     }
1003                     poTarget = poTarget->poPrevious;
1004                 }
1005                 if( poTarget == nullptr && poDirtyBlockOtherDataset )
1006                 {
1007                     if( CPLAtomicCompareAndExchange(
1008                             &(poDirtyBlockOtherDataset->nLockCount), 0, -1) )
1009                     {
1010                         CPLDebug("GDAL", "Evicting dirty block of another dataset");
1011                         poTarget = poDirtyBlockOtherDataset;
1012                     }
1013                     else
1014                     {
1015                         poTarget = poOldest;
1016                         while( poTarget != nullptr )
1017                         {
1018                             if( CPLAtomicCompareAndExchange(
1019                                 &(poTarget->nLockCount), 0, -1) )
1020                             {
1021                                 CPLDebug("GDAL", "Evicting dirty block of another dataset");
1022                                 break;
1023                             }
1024                             poTarget = poTarget->poPrevious;
1025                         }
1026                     }
1027                 }
1028 
1029                 if( poTarget != nullptr )
1030                 {
1031                     if( bSleepsForBockCacheDebug )
1032                     {
1033                         // coverity[tainted_data]
1034                         const double dfDelay = CPLAtof(
1035                             CPLGetConfigOption(
1036                                 "GDAL_RB_INTERNALIZE_SLEEP_AFTER_DROP_LOCK",
1037                                 "0"));
1038                         if( dfDelay > 0 )
1039                             CPLSleep(dfDelay);
1040                     }
1041 
1042                     GDALRasterBlock* _poPrevious = poTarget->poPrevious;
1043 
1044                     poTarget->Detach_unlocked();
1045                     poTarget->GetBand()->UnreferenceBlock(poTarget);
1046 
1047                     apoBlocksToFree[nBlocksToFree++] = poTarget;
1048                     if( poTarget->GetDirty() )
1049                     {
1050                         // Only free one dirty block at a time so that
1051                         // other dirty blocks of other bands with the same
1052                         // coordinates can be found with TryGetLockedBlock()
1053                         bLoopAgain = nCacheUsed > nCurCacheMax;
1054                         break;
1055                     }
1056                     if( nBlocksToFree == 64 )
1057                     {
1058                         bLoopAgain = ( nCacheUsed > nCurCacheMax );
1059                         break;
1060                     }
1061 
1062                     poTarget = _poPrevious;
1063                 }
1064                 else
1065                 {
1066                     break;
1067                 }
1068             }
1069 
1070         /* ------------------------------------------------------------------ */
1071         /*      Add this block to the list.                                   */
1072         /* ------------------------------------------------------------------ */
1073             if( !bLoopAgain )
1074                 Touch_unlocked();
1075         }
1076 
1077         bFirstIter = false;
1078 
1079         // Now free blocks we have detached and removed from their band.
1080         for( int i = 0; i < nBlocksToFree; ++i)
1081         {
1082             GDALRasterBlock * const poBlock = apoBlocksToFree[i];
1083 
1084             if( poBlock->GetDirty() )
1085             {
1086                 if( bSleepsForBockCacheDebug )
1087                 {
1088                     // coverity[tainted_data]
1089                     const double dfDelay = CPLAtof(
1090                         CPLGetConfigOption(
1091                             "GDAL_RB_INTERNALIZE_SLEEP_AFTER_DETACH_BEFORE_WRITE",
1092                             "0"));
1093                     if( dfDelay > 0 )
1094                         CPLSleep(dfDelay);
1095                 }
1096 
1097                 CPLErr eErr = poBlock->Write();
1098                 if( eErr != CE_None )
1099                 {
1100                     // Save the error for later reporting.
1101                     poBlock->GetBand()->SetFlushBlockErr(eErr);
1102                 }
1103             }
1104 
1105             // Try to recycle the data of an existing block.
1106             void* pDataBlock = poBlock->pData;
1107             if( pNewData == nullptr && pDataBlock != nullptr &&
1108                 poBlock->GetBlockSize() == nSizeInBytes )
1109             {
1110                 pNewData = pDataBlock;
1111             }
1112             else
1113             {
1114                 VSIFreeAligned(poBlock->pData);
1115             }
1116             poBlock->pData = nullptr;
1117 
1118             poBlock->GetBand()->AddBlockToFreeList(poBlock);
1119         }
1120     }
1121     while(bLoopAgain);
1122 
1123     if( pNewData == nullptr )
1124     {
1125         pNewData = VSI_MALLOC_ALIGNED_AUTO_VERBOSE( nSizeInBytes );
1126         if( pNewData == nullptr )
1127         {
1128             return( CE_Failure );
1129         }
1130     }
1131 
1132     pData = pNewData;
1133 
1134     return CE_None;
1135 }
1136 
1137 /************************************************************************/
1138 /*                             MarkDirty()                              */
1139 /************************************************************************/
1140 
1141 /**
1142  * Mark the block as modified.
1143  *
1144  * A dirty block is one that has been modified and will need to be written
1145  * to disk before it can be flushed.
1146  */
1147 
MarkDirty()1148 void GDALRasterBlock::MarkDirty()
1149 {
1150     if( poBand )
1151     {
1152         poBand->InitRWLock();
1153         if( !bDirty )
1154             poBand->IncDirtyBlocks(1);
1155     }
1156     bDirty = true;
1157 }
1158 
1159 /************************************************************************/
1160 /*                             MarkClean()                              */
1161 /************************************************************************/
1162 
1163 /**
1164  * Mark the block as unmodified.
1165  *
1166  * A dirty block is one that has been modified and will need to be written
1167  * to disk before it can be flushed.
1168  */
1169 
MarkClean()1170 void GDALRasterBlock::MarkClean()
1171 {
1172     if( bDirty && poBand )
1173         poBand->IncDirtyBlocks(-1);
1174     bDirty = false;
1175 }
1176 
1177 /************************************************************************/
1178 /*                          DestroyRBMutex()                           */
1179 /************************************************************************/
1180 
1181 /*! @cond Doxygen_Suppress */
DestroyRBMutex()1182 void GDALRasterBlock::DestroyRBMutex()
1183 {
1184     if( hRBLock != nullptr )
1185         DESTROY_LOCK;
1186     hRBLock = nullptr;
1187 }
1188 /*! @endcond */
1189 
1190 /************************************************************************/
1191 /*                              TakeLock()                              */
1192 /************************************************************************/
1193 
1194 /**
1195  * Take a lock and Touch().
1196  *
1197  * Should only be used by GDALArrayBandBlockCache::TryGetLockedBlockRef()
1198  * and GDALHashSetBandBlockCache::TryGetLockedBlockRef()
1199  *
1200  * @return TRUE if the lock has been successfully acquired. If FALSE, the
1201  *         block is being evicted by another thread, and so should be
1202  *         considered as invalid.
1203  */
1204 
TakeLock()1205 int GDALRasterBlock::TakeLock()
1206 {
1207     const int nLockVal = AddLock();
1208     CPLAssert(nLockVal >= 0);
1209     if( bSleepsForBockCacheDebug )
1210     {
1211         // coverity[tainted_data]
1212         const double dfDelay = CPLAtof(
1213             CPLGetConfigOption("GDAL_RB_TRYGET_SLEEP_AFTER_TAKE_LOCK", "0"));
1214         if( dfDelay > 0 )
1215             CPLSleep(dfDelay);
1216     }
1217     if( nLockVal == 0 )
1218     {
1219         // The block is being evicted by GDALRasterBlock::Internalize()
1220         // or FlushCacheBlock()
1221 
1222 #ifdef DEBUG
1223         CPLDebug(
1224             "GDAL",
1225             "TakeLock(%p): Block(%d,%d,%p) is being evicted while trying to "
1226             "reacquire it.",
1227             reinterpret_cast<void *>(CPLGetPID()), nXOff, nYOff, poBand );
1228 #endif
1229         DropLock();
1230 
1231         return FALSE;
1232     }
1233     Touch();
1234     return TRUE;
1235 }
1236 
1237 /************************************************************************/
1238 /*                      DropLockForRemovalFromStorage()                 */
1239 /************************************************************************/
1240 
1241 /**
1242  * Drop a lock before removing the block from the band storage.
1243  *
1244  * Should only be used by GDALArrayBandBlockCache::FlushBlock()
1245  * and GDALHashSetBandBlockCache::FlushBlock()
1246  *
1247  * @return TRUE if the lock has been successfully dropped.
1248  */
1249 
DropLockForRemovalFromStorage()1250 int GDALRasterBlock::DropLockForRemovalFromStorage()
1251 {
1252     // Detect potential conflict with GDALRasterBlock::Internalize()
1253     // or FlushCacheBlock()
1254     if( CPLAtomicCompareAndExchange(&nLockCount, 0, -1) )
1255         return TRUE;
1256 #ifdef DEBUG
1257     CPLDebug(
1258         "GDAL",
1259         "DropLockForRemovalFromStorage(%p): Block(%d,%d,%p) was attempted "
1260         "to be flushed from band but it is flushed by global cache.",
1261         reinterpret_cast<void *>(CPLGetPID()), nXOff, nYOff, poBand );
1262 #endif
1263 
1264     // Wait for the block for having been unreferenced.
1265     TAKE_LOCK;
1266 
1267     return FALSE;
1268 }
1269 
1270 #if 0
1271 void GDALRasterBlock::DumpAll()
1272 {
1273     int iBlock = 0;
1274     for( GDALRasterBlock *poBlock = poNewest;
1275          poBlock != nullptr;
1276          poBlock = poBlock->poNext )
1277     {
1278         printf("Block %d\n", iBlock);/*ok*/
1279         poBlock->DumpBlock();
1280         printf("\n");/*ok*/
1281         iBlock++;
1282     }
1283 }
1284 
1285 void GDALRasterBlock::DumpBlock()
1286 {
1287     printf("  Lock count = %d\n", nLockCount);/*ok*/
1288     printf("  bDirty = %d\n", static_cast<int>(bDirty));/*ok*/
1289     printf("  nXOff = %d\n", nXOff);/*ok*/
1290     printf("  nYOff = %d\n", nYOff);/*ok*/
1291     printf("  nXSize = %d\n", nXSize);/*ok*/
1292     printf("  nYSize = %d\n", nYSize);/*ok*/
1293     printf("  eType = %d\n", eType);/*ok*/
1294     printf("  Band %p\n", GetBand());/*ok*/
1295     printf("  Band %d\n", GetBand()->GetBand());/*ok*/
1296     if( GetBand()->GetDataset() )
1297         printf("  Dataset = %s\n",/*ok*/
1298                GetBand()->GetDataset()->GetDescription());
1299 }
1300 #endif  // if 0
1301