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