LCOV - code coverage report
Current view: top level - gcore - gdalrasterblock.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 281 313 89.8 %
Date: 2025-10-21 22:35:35 Functions: 28 30 93.3 %

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

Generated by: LCOV version 1.14