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

Generated by: LCOV version 1.14