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-01-18 12:42:00 Functions: 29 31 93.5 %

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

Generated by: LCOV version 1.14