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