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