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