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