1 /******************************************************************************
2  * $Id: gdalrasterblock.cpp 29334 2015-06-14 17:30:54Z rouault $
3  *
4  * Project:  GDAL Core
5  * Purpose:  Implementation of GDALRasterBlock class and related global
6  *           raster block cache management.
7  * Author:   Frank Warmerdam, warmerdam@pobox.com
8  *
9  **********************************************************************
10  * Copyright (c) 1998, Frank Warmerdam <warmerdam@pobox.com>
11  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at mines-paris dot org>
12  *
13  * Permission is hereby granted, free of charge, to any person obtaining a
14  * copy of this software and associated documentation files (the "Software"),
15  * to deal in the Software without restriction, including without limitation
16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17  * and/or sell copies of the Software, and to permit persons to whom the
18  * Software is furnished to do so, subject to the following conditions:
19  *
20  * The above copyright notice and this permission notice shall be included
21  * in all copies or substantial portions of the Software.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29  * DEALINGS IN THE SOFTWARE.
30  ****************************************************************************/
31 
32 #include "gdal_priv.h"
33 #include "cpl_multiproc.h"
34 
35 CPL_CVSID("$Id: gdalrasterblock.cpp 29334 2015-06-14 17:30:54Z rouault $");
36 
37 static int bCacheMaxInitialized = FALSE;
38 static GIntBig nCacheMax = 40 * 1024*1024;
39 static volatile GIntBig nCacheUsed = 0;
40 
41 static GDALRasterBlock *poOldest = NULL;    /* tail */
42 static GDALRasterBlock *poNewest = NULL;    /* head */
43 
44 #if 0
45 static CPLMutex *hRBLock = NULL;
46 #define INITIALIZE_LOCK         CPLMutexHolderD( &hRBLock )
47 #define TAKE_LOCK               CPLMutexHolderOptionalLockD( hRBLock )
48 #define DESTROY_LOCK            CPLDestroyMutex( hRBLock )
49 #else
50 
51 static CPLLock* hRBLock = NULL;
52 static int bDebugContention = FALSE;
GetLockType()53 static CPLLockType GetLockType()
54 {
55     static int nLockType = -1;
56     if( nLockType < 0 )
57     {
58         const char* pszLockType = CPLGetConfigOption("GDAL_RB_LOCK_TYPE", "ADAPTIVE");
59         if( EQUAL(pszLockType, "ADAPTIVE") )
60             nLockType = LOCK_ADAPTIVE_MUTEX;
61         else if( EQUAL(pszLockType, "RECURSIVE") )
62             nLockType = LOCK_RECURSIVE_MUTEX;
63         else if( EQUAL(pszLockType, "SPIN") )
64             nLockType = LOCK_SPIN;
65         else
66         {
67             CPLError(CE_Warning, CPLE_NotSupported,
68                      "GDAL_RB_LOCK_TYPE=%s not supported. Falling back to ADAPTIVE",
69                      pszLockType);
70             nLockType = LOCK_ADAPTIVE_MUTEX;
71         }
72         bDebugContention = CSLTestBoolean(CPLGetConfigOption("GDAL_RB_LOCK_DEBUG_CONTENTION", "NO"));
73     }
74     return (CPLLockType) nLockType;
75 }
76 
77 #define INITIALIZE_LOCK         CPLLockHolderD( &hRBLock, GetLockType() ); \
78                                 CPLLockSetDebugPerf(hRBLock, bDebugContention)
79 #define TAKE_LOCK               CPLLockHolderOptionalLockD( hRBLock )
80 #define DESTROY_LOCK            CPLDestroyLock( hRBLock )
81 
82 #endif
83 
84 //#define ENABLE_DEBUG
85 
86 /************************************************************************/
87 /*                          GDALSetCacheMax()                           */
88 /************************************************************************/
89 
90 /**
91  * \brief Set maximum cache memory.
92  *
93  * This function sets the maximum amount of memory that GDAL is permitted
94  * to use for GDALRasterBlock caching. The unit of the value is bytes.
95  *
96  * The maximum value is 2GB, due to the use of a signed 32 bit integer.
97  * Use GDALSetCacheMax64() to be able to set a higher value.
98  *
99  * @param nNewSizeInBytes the maximum number of bytes for caching.
100  */
101 
GDALSetCacheMax(int nNewSizeInBytes)102 void CPL_STDCALL GDALSetCacheMax( int nNewSizeInBytes )
103 
104 {
105     GDALSetCacheMax64(nNewSizeInBytes);
106 }
107 
108 
109 /************************************************************************/
110 /*                        GDALSetCacheMax64()                           */
111 /************************************************************************/
112 
113 /**
114  * \brief Set maximum cache memory.
115  *
116  * This function sets the maximum amount of memory that GDAL is permitted
117  * to use for GDALRasterBlock caching. The unit of the value is bytes.
118  *
119  * Note: On 32 bit platforms, the maximum amount of memory that can be addressed
120  * by a process might be 2 GB or 3 GB, depending on the operating system
121  * capabilities. This function will not make any attempt to check the
122  * consistency of the passed value with the effective capabilities of the OS.
123  *
124  * @param nNewSizeInBytes the maximum number of bytes for caching.
125  *
126  * @since GDAL 1.8.0
127  */
128 
GDALSetCacheMax64(GIntBig nNewSizeInBytes)129 void CPL_STDCALL GDALSetCacheMax64( GIntBig nNewSizeInBytes )
130 
131 {
132     bCacheMaxInitialized = TRUE;
133     nCacheMax = nNewSizeInBytes;
134 
135 /* -------------------------------------------------------------------- */
136 /*      Flush blocks till we are under the new limit or till we         */
137 /*      can't seem to flush anymore.                                    */
138 /* -------------------------------------------------------------------- */
139     while( nCacheUsed > nCacheMax )
140     {
141         GIntBig nOldCacheUsed = nCacheUsed;
142 
143         GDALFlushCacheBlock();
144 
145         if( nCacheUsed == nOldCacheUsed )
146             break;
147     }
148 }
149 
150 /************************************************************************/
151 /*                          GDALGetCacheMax()                           */
152 /************************************************************************/
153 
154 /**
155  * \brief Get maximum cache memory.
156  *
157  * Gets the maximum amount of memory available to the GDALRasterBlock
158  * caching system for caching GDAL read/write imagery.
159  *
160  * The first type this function is called, it will read the GDAL_CACHEMAX
161  * configuation option to initialize the maximum cache memory.
162  *
163  * This function cannot return a value higher than 2 GB. Use
164  * GDALGetCacheMax64() to get a non-truncated value.
165  *
166  * @return maximum in bytes.
167  */
168 
GDALGetCacheMax()169 int CPL_STDCALL GDALGetCacheMax()
170 {
171     GIntBig nRes = GDALGetCacheMax64();
172     if (nRes > INT_MAX)
173     {
174         static int bHasWarned = FALSE;
175         if (!bHasWarned)
176         {
177             CPLError(CE_Warning, CPLE_AppDefined,
178                      "Cache max value doesn't fit on a 32 bit integer. "
179                      "Call GDALGetCacheMax64() instead");
180             bHasWarned = TRUE;
181         }
182         nRes = INT_MAX;
183     }
184     return (int)nRes;
185 }
186 
187 /************************************************************************/
188 /*                         GDALGetCacheMax64()                          */
189 /************************************************************************/
190 
191 /**
192  * \brief Get maximum cache memory.
193  *
194  * Gets the maximum amount of memory available to the GDALRasterBlock
195  * caching system for caching GDAL read/write imagery.
196  *
197  * The first type this function is called, it will read the GDAL_CACHEMAX
198  * configuation option to initialize the maximum cache memory.
199  *
200  * @return maximum in bytes.
201  *
202  * @since GDAL 1.8.0
203  */
204 
GDALGetCacheMax64()205 GIntBig CPL_STDCALL GDALGetCacheMax64()
206 {
207     if( !bCacheMaxInitialized )
208     {
209         {
210             INITIALIZE_LOCK;
211         }
212         const char* pszCacheMax = CPLGetConfigOption("GDAL_CACHEMAX",NULL);
213         bCacheMaxInitialized = TRUE;
214         if( pszCacheMax != NULL )
215         {
216             GIntBig nNewCacheMax = (GIntBig)CPLScanUIntBig(pszCacheMax, strlen(pszCacheMax));
217             if( nNewCacheMax < 100000 )
218             {
219                 if (nNewCacheMax < 0)
220                 {
221                     CPLError(CE_Failure, CPLE_NotSupported,
222                              "Invalid value for GDAL_CACHEMAX. Using default value.");
223                     return nCacheMax;
224                 }
225                 nNewCacheMax *= 1024 * 1024;
226             }
227             nCacheMax = nNewCacheMax;
228         }
229     }
230 
231     return nCacheMax;
232 }
233 
234 /************************************************************************/
235 /*                          GDALGetCacheUsed()                          */
236 /************************************************************************/
237 
238 /**
239  * \brief Get cache memory used.
240  *
241  * @return the number of bytes of memory currently in use by the
242  * GDALRasterBlock memory caching.
243  */
244 
GDALGetCacheUsed()245 int CPL_STDCALL GDALGetCacheUsed()
246 {
247     if (nCacheUsed > INT_MAX)
248     {
249         static int bHasWarned = FALSE;
250         if (!bHasWarned)
251         {
252             CPLError(CE_Warning, CPLE_AppDefined,
253                      "Cache used value doesn't fit on a 32 bit integer. "
254                      "Call GDALGetCacheUsed64() instead");
255             bHasWarned = TRUE;
256         }
257         return INT_MAX;
258     }
259     return (int)nCacheUsed;
260 }
261 
262 /************************************************************************/
263 /*                        GDALGetCacheUsed64()                          */
264 /************************************************************************/
265 
266 /**
267  * \brief Get cache memory used.
268  *
269  * @return the number of bytes of memory currently in use by the
270  * GDALRasterBlock memory caching.
271  *
272  * @since GDAL 1.8.0
273  */
274 
GDALGetCacheUsed64()275 GIntBig CPL_STDCALL GDALGetCacheUsed64()
276 {
277     return nCacheUsed;
278 }
279 
280 /************************************************************************/
281 /*                        GDALFlushCacheBlock()                         */
282 /*                                                                      */
283 /*      The workhorse of cache management!                              */
284 /************************************************************************/
285 
286 /**
287  * \brief Try to flush one cached raster block
288  *
289  * This function will search the first unlocked raster block and will
290  * flush it to release the associated memory.
291  *
292  * @return TRUE if one block was flushed, FALSE if there are no cached blocks
293  *         or if they are currently locked.
294  */
GDALFlushCacheBlock()295 int CPL_STDCALL GDALFlushCacheBlock()
296 
297 {
298     return GDALRasterBlock::FlushCacheBlock();
299 }
300 
301 /************************************************************************/
302 /* ==================================================================== */
303 /*                           GDALRasterBlock                            */
304 /* ==================================================================== */
305 /************************************************************************/
306 
307 /**
308  * \class GDALRasterBlock "gdal_priv.h"
309  *
310  * GDALRasterBlock objects hold one block of raster data for one band
311  * that is currently stored in the GDAL raster cache.  The cache holds
312  * some blocks of raster data for zero or more GDALRasterBand objects
313  * across zero or more GDALDataset objects in a global raster cache with
314  * a least recently used (LRU) list and an upper cache limit (see
315  * GDALSetCacheMax()) under which the cache size is normally kept.
316  *
317  * Some blocks in the cache may be modified relative to the state on disk
318  * (they are marked "Dirty") and must be flushed to disk before they can
319  * be discarded.  Other (Clean) blocks may just be discarded if their memory
320  * needs to be recovered.
321  *
322  * In normal situations applications do not interact directly with the
323  * GDALRasterBlock - instead it it utilized by the RasterIO() interfaces
324  * to implement caching.
325  *
326  * Some driver classes are implemented in a fashion that completely avoids
327  * use of the GDAL raster cache (and GDALRasterBlock) though this is not very
328  * common.
329  */
330 
331 /************************************************************************/
332 /*                          FlushCacheBlock()                           */
333 /*                                                                      */
334 /*      Note, if we have alot of blocks locked for a long time, this    */
335 /*      method is going to get slow because it will have to traverse    */
336 /*      the linked list a long ways looking for a flushing              */
337 /*      candidate.   It might help to re-touch locked blocks to push    */
338 /*      them to the top of the list.                                    */
339 /************************************************************************/
340 
341 /**
342  * \brief Attempt to flush at least one block from the cache.
343  *
344  * This static method is normally used to recover memory when a request
345  * for a new cache block would put cache memory use over the established
346  * limit.
347  *
348  * C++ analog to the C function GDALFlushCacheBlock().
349  *
350  * @param bDirtyBlocksOnly Only flushes dirty blocks.
351  * @return TRUE if successful or FALSE if no flushable block is found.
352  */
353 
FlushCacheBlock(int bDirtyBlocksOnly)354 int GDALRasterBlock::FlushCacheBlock(int bDirtyBlocksOnly)
355 
356 {
357     GDALRasterBlock *poTarget;
358 
359     {
360         INITIALIZE_LOCK;
361         poTarget = poOldest;
362 
363         while( poTarget != NULL && (poTarget->GetLockCount() > 0 ||
364                (bDirtyBlocksOnly && !poTarget->GetDirty())) )
365             poTarget = poTarget->poPrevious;
366 
367         if( poTarget == NULL )
368             return FALSE;
369 
370         poTarget->Detach_unlocked();
371         poTarget->GetBand()->UnreferenceBlock(poTarget->GetXOff(),poTarget->GetYOff());
372     }
373 
374     if( poTarget->GetDirty() )
375     {
376         CPLErr eErr = poTarget->Write();
377         if( eErr != CE_None )
378         {
379              /* Save the error for later reporting */
380             poTarget->GetBand()->SetFlushBlockErr(eErr);
381         }
382     }
383     delete poTarget;
384 
385     return TRUE;
386 }
387 
388 /************************************************************************/
389 /*                          FlushDirtyBlocks()                          */
390 /************************************************************************/
391 
392 /**
393  * \brief Flush all dirty blocks from cache.
394  *
395  * This static method is normally used to recover memory and is especially
396  * useful when doing multi-threaded code that can trigger the block cache.
397  *
398  * Due to the current design of the block cache, dirty blocks belonging to a same
399  * dataset could be pushed simultanously to the IWriteBlock() method of that
400  * dataset from different threads, causing races.
401  *
402  * Calling this method before that code can help workarounding that issue,
403  * in a multiple readers, one writer scenario.
404  *
405  * @since GDAL 2.0
406  */
407 
FlushDirtyBlocks()408 void GDALRasterBlock::FlushDirtyBlocks()
409 
410 {
411     while( FlushCacheBlock(TRUE) )
412     {
413         /* go on */
414     }
415 }
416 
417 /************************************************************************/
418 /*                          GDALRasterBlock()                           */
419 /************************************************************************/
420 
421 /**
422  * @brief GDALRasterBlock Constructor
423  *
424  * Normally only called from GDALRasterBand::GetLockedBlockRef().
425  *
426  * @param poBandIn the raster band used as source of raster block
427  * being constructed.
428  *
429  * @param nXOffIn the horizontal block offset, with zero indicating
430  * the left most block, 1 the next block and so forth.
431  *
432  * @param nYOffIn the vertical block offset, with zero indicating
433  * the top most block, 1 the next block and so forth.
434  */
435 
GDALRasterBlock(GDALRasterBand * poBandIn,int nXOffIn,int nYOffIn)436 GDALRasterBlock::GDALRasterBlock( GDALRasterBand *poBandIn,
437                                   int nXOffIn, int nYOffIn )
438 
439 {
440     CPLAssert( NULL != poBandIn );
441 
442     poBand = poBandIn;
443 
444     poBand->GetBlockSize( &nXSize, &nYSize );
445     eType = poBand->GetRasterDataType();
446     pData = NULL;
447     bDirty = FALSE;
448     nLockCount = 0;
449 
450     poNext = poPrevious = NULL;
451 
452     nXOff = nXOffIn;
453     nYOff = nYOffIn;
454     bMustDetach = TRUE;
455 }
456 
457 /************************************************************************/
458 /*                          ~GDALRasterBlock()                          */
459 /************************************************************************/
460 
461 /**
462  * Block destructor.
463  *
464  * Normally called from GDALRasterBand::FlushBlock().
465  */
466 
~GDALRasterBlock()467 GDALRasterBlock::~GDALRasterBlock()
468 
469 {
470     Detach();
471 
472     if( pData != NULL )
473     {
474         VSIFree( pData );
475     }
476 
477     CPLAssert( nLockCount == 0 );
478 
479 #ifdef ENABLE_DEBUG
480     Verify();
481 #endif
482 }
483 
484 /************************************************************************/
485 /*                               Detach()                               */
486 /************************************************************************/
487 
488 /**
489  * Remove block from cache.
490  *
491  * This method removes the current block from the linked list used to keep
492  * track of all cached blocks in order of age.  It does not affect whether
493  * the block is referenced by a GDALRasterBand nor does it destroy or flush
494  * the block.
495  */
496 
Detach()497 void GDALRasterBlock::Detach()
498 
499 {
500     if( bMustDetach )
501     {
502         TAKE_LOCK;
503         Detach_unlocked();
504     }
505 }
506 
Detach_unlocked()507 void GDALRasterBlock::Detach_unlocked()
508 {
509     if( poOldest == this )
510         poOldest = poPrevious;
511 
512     if( poNewest == this )
513     {
514         poNewest = poNext;
515     }
516 
517     if( poPrevious != NULL )
518         poPrevious->poNext = poNext;
519 
520     if( poNext != NULL )
521         poNext->poPrevious = poPrevious;
522 
523     poPrevious = NULL;
524     poNext = NULL;
525     bMustDetach = FALSE;
526 
527     if( pData )
528         nCacheUsed -= GetBlockSize();
529 
530 #ifdef ENABLE_DEBUG
531     Verify();
532 #endif
533 }
534 
535 /************************************************************************/
536 /*                               Verify()                               */
537 /************************************************************************/
538 
539 /**
540  * Confirms (via assertions) that the block cache linked list is in a
541  * consistent state.
542  */
543 
Verify()544 void GDALRasterBlock::Verify()
545 
546 {
547     TAKE_LOCK;
548 
549     CPLAssert( (poNewest == NULL && poOldest == NULL)
550                || (poNewest != NULL && poOldest != NULL) );
551 
552     if( poNewest != NULL )
553     {
554         CPLAssert( poNewest->poPrevious == NULL );
555         CPLAssert( poOldest->poNext == NULL );
556 
557         GDALRasterBlock* poLast = NULL;
558         for( GDALRasterBlock *poBlock = poNewest;
559              poBlock != NULL;
560              poBlock = poBlock->poNext )
561         {
562             CPLAssert( poBlock->poPrevious == poLast );
563 
564             poLast = poBlock;
565         }
566 
567         CPLAssert( poOldest == poLast );
568     }
569 }
570 
571 /************************************************************************/
572 /*                               Write()                                */
573 /************************************************************************/
574 
575 /**
576  * Force writing of the current block, if dirty.
577  *
578  * The block is written using GDALRasterBand::IWriteBlock() on it's
579  * corresponding band object.  Even if the write fails the block will
580  * be marked clean.
581  *
582  * @return CE_None otherwise the error returned by IWriteBlock().
583  */
584 
Write()585 CPLErr GDALRasterBlock::Write()
586 
587 {
588     if( !GetDirty() )
589         return CE_None;
590 
591     if( poBand == NULL )
592         return CE_Failure;
593 
594     MarkClean();
595 
596     if (poBand->eFlushBlockErr == CE_None)
597     {
598         int bCallLeaveReadWrite = poBand->EnterReadWrite(GF_Write);
599         CPLErr eErr = poBand->IWriteBlock( nXOff, nYOff, pData );
600         if( bCallLeaveReadWrite ) poBand->LeaveReadWrite();
601         return eErr;
602     }
603     else
604         return poBand->eFlushBlockErr;
605 }
606 
607 /************************************************************************/
608 /*                               Touch()                                */
609 /************************************************************************/
610 
611 /**
612  * Push block to top of LRU (least-recently used) list.
613  *
614  * This method is normally called when a block is used to keep track
615  * that it has been recently used.
616  */
617 
Touch()618 void GDALRasterBlock::Touch()
619 
620 {
621     TAKE_LOCK;
622     Touch_unlocked();
623 }
624 
625 
Touch_unlocked()626 void GDALRasterBlock::Touch_unlocked()
627 
628 {
629     if( poNewest == this )
630         return;
631 
632     // In theory, we shouldn't try to touch a block that has been detached
633     CPLAssert(bMustDetach);
634     if( !bMustDetach )
635     {
636         if( pData )
637             nCacheUsed += GetBlockSize();
638 
639         bMustDetach = TRUE;
640     }
641 
642     if( poOldest == this )
643         poOldest = this->poPrevious;
644 
645     if( poPrevious != NULL )
646         poPrevious->poNext = poNext;
647 
648     if( poNext != NULL )
649         poNext->poPrevious = poPrevious;
650 
651     poPrevious = NULL;
652     poNext = poNewest;
653 
654     if( poNewest != NULL )
655     {
656         CPLAssert( poNewest->poPrevious == NULL );
657         poNewest->poPrevious = this;
658     }
659     poNewest = this;
660 
661     if( poOldest == NULL )
662     {
663         CPLAssert( poPrevious == NULL && poNext == NULL );
664         poOldest = this;
665     }
666 #ifdef ENABLE_DEBUG
667     Verify();
668 #endif
669 }
670 
671 /************************************************************************/
672 /*                            Internalize()                             */
673 /************************************************************************/
674 
675 /**
676  * Allocate memory for block.
677  *
678  * This method allocates memory for the block, and attempts to flush other
679  * blocks, if necessary, to bring the total cache size back within the limits.
680  * The newly allocated block is touched and will be considered most recently
681  * used in the LRU list.
682  *
683  * @return CE_None on success or CE_Failure if memory allocation fails.
684  */
685 
Internalize()686 CPLErr GDALRasterBlock::Internalize()
687 
688 {
689     void        *pNewData = NULL;
690     int         nSizeInBytes;
691 
692     CPLAssert( pData == NULL );
693 
694     // This call will initialize the hRBLock mutex. Other call places can
695     // only be called if we have go through there.
696     GIntBig     nCurCacheMax = GDALGetCacheMax64();
697 
698     /* No risk of overflow as it is checked in GDALRasterBand::InitBlockInfo() */
699     nSizeInBytes = GetBlockSize();
700 
701 /* -------------------------------------------------------------------- */
702 /*      Flush old blocks if we are nearing our memory limit.            */
703 /* -------------------------------------------------------------------- */
704     int bFirstIter = TRUE;
705     int bLoopAgain;
706     do
707     {
708         bLoopAgain = FALSE;
709         GDALRasterBlock* apoBlocksToFree[64];
710         int nBlocksToFree = 0;
711         {
712             TAKE_LOCK;
713 
714             if( bFirstIter )
715                 nCacheUsed += nSizeInBytes;
716             GDALRasterBlock *poTarget = poOldest;
717             while( nCacheUsed > nCurCacheMax )
718             {
719                 while( poTarget != NULL && poTarget->GetLockCount() > 0 )
720                     poTarget = poTarget->poPrevious;
721 
722                 if( poTarget != NULL )
723                 {
724                     GDALRasterBlock* _poPrevious = poTarget->poPrevious;
725 
726                     poTarget->Detach_unlocked();
727                     poTarget->GetBand()->UnreferenceBlock(poTarget->GetXOff(),poTarget->GetYOff());
728 
729                     apoBlocksToFree[nBlocksToFree++] = poTarget;
730                     if( poTarget->GetDirty() )
731                     {
732                         // Only free one dirty block at a time so that
733                         // other dirty blocks of other bands with the same coordinates
734                         // can be found with TryGetLockedBlock()
735                         bLoopAgain = ( nCacheUsed > nCurCacheMax );
736                         break;
737                     }
738                     if( nBlocksToFree == 64 )
739                     {
740                         CPLDebug("GDAL", "More than 64 blocks are flagged to be flushed. Not trying more");
741                         break;
742                     }
743 
744                     poTarget = _poPrevious;
745                 }
746                 else
747                     break;
748             }
749 
750         /* -------------------------------------------------------------------- */
751         /*      Add this block to the list.                                     */
752         /* -------------------------------------------------------------------- */
753             if( !bLoopAgain )
754                 Touch_unlocked();
755         }
756 
757         bFirstIter = FALSE;
758 
759         /* Now free blocks we have detached and removed from their band */
760         for(int i=0;i<nBlocksToFree;i++)
761         {
762             GDALRasterBlock *poBlock = apoBlocksToFree[i];
763 
764             if( poBlock->GetDirty() )
765             {
766                 CPLErr eErr = poBlock->Write();
767                 if( eErr != CE_None )
768                 {
769                     /* Save the error for later reporting */
770                     poBlock->GetBand()->SetFlushBlockErr(eErr);
771                 }
772             }
773 
774             /* Try to recycle the data of an existing block */
775             void* pDataBlock = poBlock->pData;
776             if( pNewData == NULL && pDataBlock != NULL &&
777                 poBlock->GetBlockSize() >= nSizeInBytes )
778             {
779                 pNewData = pDataBlock;
780                 poBlock->pData = NULL;
781             }
782 
783             delete poBlock;
784         }
785     }
786     while(bLoopAgain);
787 
788     if( pNewData == NULL )
789     {
790         pNewData = VSIMalloc( nSizeInBytes );
791         if( pNewData == NULL )
792         {
793             CPLError( CE_Failure, CPLE_OutOfMemory,
794                     "GDALRasterBlock::Internalize : Out of memory allocating %d bytes.",
795                     nSizeInBytes);
796             return( CE_Failure );
797         }
798     }
799 
800     pData = pNewData;
801 
802     return( CE_None );
803 }
804 
805 /************************************************************************/
806 /*                             MarkDirty()                              */
807 /************************************************************************/
808 
809 /**
810  * Mark the block as modified.
811  *
812  * A dirty block is one that has been modified and will need to be written
813  * to disk before it can be flushed.
814  */
815 
MarkDirty()816 void GDALRasterBlock::MarkDirty()
817 
818 {
819     bDirty = TRUE;
820 }
821 
822 
823 /************************************************************************/
824 /*                             MarkClean()                              */
825 /************************************************************************/
826 
827 /**
828  * Mark the block as unmodified.
829  *
830  * A dirty block is one that has been modified and will need to be written
831  * to disk before it can be flushed.
832  */
833 
MarkClean()834 void GDALRasterBlock::MarkClean()
835 
836 {
837     bDirty = FALSE;
838 }
839 
840 /************************************************************************/
841 /*                           SafeLockBlock()                            */
842 /************************************************************************/
843 
844 /**
845  * \brief Safely lock block.
846  *
847  * This method locks a GDALRasterBlock (and touches it) in a thread-safe
848  * manner.  The global block cache mutex is held while locking the block,
849  * in order to avoid race conditions with other threads that might be
850  * trying to expire the block at the same time.  The block pointer may be
851  * safely NULL, in which case this method does nothing.
852  *
853  * @param ppBlock Pointer to the block pointer to try and lock/touch.
854  */
855 
SafeLockBlock(GDALRasterBlock ** ppBlock)856 int GDALRasterBlock::SafeLockBlock( GDALRasterBlock ** ppBlock )
857 
858 {
859     CPLAssert( NULL != ppBlock );
860 
861     TAKE_LOCK;
862 
863     if( *ppBlock != NULL )
864     {
865         (*ppBlock)->AddLock();
866         (*ppBlock)->Touch_unlocked();
867 
868         return TRUE;
869     }
870     else
871         return FALSE;
872 }
873 
874 /************************************************************************/
875 /*                          DestroyRBMutex()                           */
876 /************************************************************************/
877 
DestroyRBMutex()878 void GDALRasterBlock::DestroyRBMutex()
879 {
880     if( hRBLock != NULL )
881         DESTROY_LOCK;
882     hRBLock = NULL;
883 }
884