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