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