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

Generated by: LCOV version 1.14