1 : /******************************************************************************
2 : * $Id: gdalrasterblock.cpp 19341 2010-04-07 22:15:28Z rouault $
3 : *
4 : * Project: GDAL Core
5 : * Purpose: Implementation of GDALRasterBlock class and related global
6 : * raster block cache management.
7 : * Author: Frank Warmerdam, warmerdam@pobox.com
8 : *
9 : **********************************************************************
10 : * Copyright (c) 1998, Frank Warmerdam <warmerdam@pobox.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 "gdal_priv.h"
32 : #include "cpl_multiproc.h"
33 :
34 : CPL_CVSID("$Id: gdalrasterblock.cpp 19341 2010-04-07 22:15:28Z rouault $");
35 :
36 : static int bCacheMaxInitialized = FALSE;
37 : static int nCacheMax = 40 * 1024*1024;
38 : static volatile int nCacheUsed = 0;
39 :
40 : static volatile GDALRasterBlock *poOldest = NULL; /* tail */
41 : static volatile GDALRasterBlock *poNewest = NULL; /* head */
42 :
43 : static void *hRBMutex = NULL;
44 :
45 :
46 : /************************************************************************/
47 : /* GDALSetCacheMax() */
48 : /************************************************************************/
49 :
50 : /**
51 : * \brief Set maximum cache memory.
52 : *
53 : * This function sets the maximum amount of memory that GDAL is permitted
54 : * to use for GDALRasterBlock caching.
55 : *
56 : * @param nNewSize the maximum number of bytes for caching. Maximum is 2GB.
57 : */
58 :
59 13 : void CPL_STDCALL GDALSetCacheMax( int nNewSize )
60 :
61 : {
62 13 : nCacheMax = nNewSize;
63 :
64 : /* -------------------------------------------------------------------- */
65 : /* Flush blocks till we are under the new limit or till we */
66 : /* can't seem to flush anymore. */
67 : /* -------------------------------------------------------------------- */
68 26 : while( nCacheUsed > nCacheMax )
69 : {
70 0 : int nOldCacheUsed = nCacheUsed;
71 :
72 0 : GDALFlushCacheBlock();
73 :
74 0 : if( nCacheUsed == nOldCacheUsed )
75 0 : break;
76 : }
77 13 : }
78 :
79 : /************************************************************************/
80 : /* GDALGetCacheMax() */
81 : /************************************************************************/
82 :
83 : /**
84 : * \brief Get maximum cache memory.
85 : *
86 : * Gets the maximum amount of memory available to the GDALRasterBlock
87 : * caching system for caching GDAL read/write imagery.
88 : *
89 : * @return maximum in bytes.
90 : */
91 :
92 289892 : int CPL_STDCALL GDALGetCacheMax()
93 : {
94 289892 : if( !bCacheMaxInitialized )
95 : {
96 333 : const char* pszCacheMax = CPLGetConfigOption("GDAL_CACHEMAX",NULL);
97 333 : bCacheMaxInitialized = TRUE;
98 333 : if( pszCacheMax != NULL )
99 : {
100 2 : int nNewCacheMax = atoi(pszCacheMax);
101 2 : if( nNewCacheMax < 100000 )
102 : {
103 2 : if (nNewCacheMax < 0)
104 : {
105 : CPLError(CE_Failure, CPLE_NotSupported,
106 0 : "Invalid value for GDAL_CACHEMAX. Using default value.");
107 0 : return nCacheMax;
108 : }
109 2 : if (nNewCacheMax > 2047)
110 : {
111 : CPLError(CE_Failure, CPLE_NotSupported,
112 0 : "Maximum value supported for GDAL_CACHEMAX is 2 GB. Using default value.");
113 0 : return nCacheMax;
114 : }
115 2 : nNewCacheMax *= 1024 * 1024;
116 : }
117 2 : nCacheMax = nNewCacheMax;
118 : }
119 : }
120 :
121 289892 : return nCacheMax;
122 : }
123 :
124 : /************************************************************************/
125 : /* GDALGetCacheUsed() */
126 : /************************************************************************/
127 :
128 : /**
129 : * \brief Get cache memory used.
130 : *
131 : * @return the number of bytes of memory currently in use by the
132 : * GDALRasterBlock memory caching.
133 : */
134 :
135 2 : int CPL_STDCALL GDALGetCacheUsed()
136 : {
137 2 : return nCacheUsed;
138 : }
139 :
140 : /************************************************************************/
141 : /* GDALFlushCacheBlock() */
142 : /* */
143 : /* The workhorse of cache management! */
144 : /************************************************************************/
145 :
146 : /**
147 : * \brief Try to flush one cached raster block
148 : *
149 : * This function will search the first unlocked raster block and will
150 : * flush it to release the associated memory.
151 : *
152 : * @return TRUE if one block was flushed, FALSE if there are no cached blocks
153 : * or if they are currently locked.
154 : */
155 1109 : int CPL_STDCALL GDALFlushCacheBlock()
156 :
157 : {
158 1109 : return GDALRasterBlock::FlushCacheBlock();
159 : }
160 :
161 : /************************************************************************/
162 : /* ==================================================================== */
163 : /* GDALRasterBlock */
164 : /* ==================================================================== */
165 : /************************************************************************/
166 :
167 : /**
168 : * \class GDALRasterBlock "gdal_priv.h"
169 : *
170 : * GDALRasterBlock objects hold one block of raster data for one band
171 : * that is currently stored in the GDAL raster cache. The cache holds
172 : * some blocks of raster data for zero or more GDALRasterBand objects
173 : * across zero or more GDALDataset objects in a global raster cache with
174 : * a least recently used (LRU) list and an upper cache limit (see
175 : * GDALSetCacheMax()) under which the cache size is normally kept.
176 : *
177 : * Some blocks in the cache may be modified relative to the state on disk
178 : * (they are marked "Dirty") and must be flushed to disk before they can
179 : * be discarded. Other (Clean) blocks may just be discarded if their memory
180 : * needs to be recovered.
181 : *
182 : * In normal situations applications do not interact directly with the
183 : * GDALRasterBlock - instead it it utilized by the RasterIO() interfaces
184 : * to implement caching.
185 : *
186 : * Some driver classes are implemented in a fashion that completely avoids
187 : * use of the GDAL raster cache (and GDALRasterBlock) though this is not very
188 : * common.
189 : */
190 :
191 : /************************************************************************/
192 : /* FlushCacheBlock() */
193 : /* */
194 : /* Note, if we have alot of blocks locked for a long time, this */
195 : /* method is going to get slow because it will have to traverse */
196 : /* the linked list a long ways looking for a flushing */
197 : /* candidate. It might help to re-touch locked blocks to push */
198 : /* them to the top of the list. */
199 : /************************************************************************/
200 :
201 : /**
202 : * \brief Attempt to flush at least one block from the cache.
203 : *
204 : * This static method is normally used to recover memory when a request
205 : * for a new cache block would put cache memory use over the established
206 : * limit.
207 : *
208 : * C++ analog to the C function GDALFlushCacheBlock().
209 : *
210 : * @return TRUE if successful or FALSE if no flushable block is found.
211 : */
212 :
213 1109 : int GDALRasterBlock::FlushCacheBlock()
214 :
215 : {
216 : int nXOff, nYOff;
217 : GDALRasterBand *poBand;
218 :
219 : {
220 1109 : CPLMutexHolderD( &hRBMutex );
221 1109 : GDALRasterBlock *poTarget = (GDALRasterBlock *) poOldest;
222 :
223 2218 : while( poTarget != NULL && poTarget->GetLockCount() > 0 )
224 0 : poTarget = poTarget->poPrevious;
225 :
226 1109 : if( poTarget == NULL )
227 950 : return FALSE;
228 :
229 634 : poTarget->Detach();
230 :
231 634 : nXOff = poTarget->GetXOff();
232 634 : nYOff = poTarget->GetYOff();
233 634 : poBand = poTarget->GetBand();
234 : }
235 :
236 634 : poBand->FlushBlock( nXOff, nYOff );
237 :
238 634 : return TRUE;
239 : }
240 :
241 : /************************************************************************/
242 : /* GDALRasterBlock() */
243 : /************************************************************************/
244 :
245 : /**
246 : * @brief GDALRasterBlock Constructor
247 : *
248 : * Normally only called from GDALRasterBand::GetLockedBlockRef().
249 : *
250 : * @param poBandIn the raster band used as source of raster block
251 : * being constructed.
252 : *
253 : * @param nXOffIn the horizontal block offset, with zero indicating
254 : * the left most block, 1 the next block and so forth.
255 : *
256 : * @param nYOffIn the vertical block offset, with zero indicating
257 : * the top most block, 1 the next block and so forth.
258 : */
259 :
260 : GDALRasterBlock::GDALRasterBlock( GDALRasterBand *poBandIn,
261 288957 : int nXOffIn, int nYOffIn )
262 :
263 : {
264 288957 : CPLAssert( NULL != poBandIn );
265 :
266 288957 : poBand = poBandIn;
267 :
268 288957 : poBand->GetBlockSize( &nXSize, &nYSize );
269 288957 : eType = poBand->GetRasterDataType();
270 288957 : pData = NULL;
271 288957 : bDirty = FALSE;
272 288957 : nLockCount = 0;
273 :
274 288957 : poNext = poPrevious = NULL;
275 :
276 288957 : nXOff = nXOffIn;
277 288957 : nYOff = nYOffIn;
278 288957 : }
279 :
280 : /************************************************************************/
281 : /* ~GDALRasterBlock() */
282 : /************************************************************************/
283 :
284 : /**
285 : * Block destructor.
286 : *
287 : * Normally called from GDALRasterBand::FlushBlock().
288 : */
289 :
290 288957 : GDALRasterBlock::~GDALRasterBlock()
291 :
292 : {
293 288957 : Detach();
294 :
295 288957 : if( pData != NULL )
296 : {
297 : int nSizeInBytes;
298 :
299 288957 : VSIFree( pData );
300 :
301 288957 : nSizeInBytes = (nXSize * nYSize * GDALGetDataTypeSize(eType)+7)/8;
302 :
303 : {
304 288957 : CPLMutexHolderD( &hRBMutex );
305 288957 : nCacheUsed -= nSizeInBytes;
306 : }
307 : }
308 :
309 288957 : CPLAssert( nLockCount == 0 );
310 :
311 : #ifdef ENABLE_DEBUG
312 : Verify();
313 : #endif
314 288957 : }
315 :
316 : /************************************************************************/
317 : /* Detach() */
318 : /************************************************************************/
319 :
320 : /**
321 : * Remove block from cache.
322 : *
323 : * This method removes the current block from the linked list used to keep
324 : * track of all cached blocks in order of age. It does not affect whether
325 : * the block is referenced by a GDALRasterBand nor does it destroy or flush
326 : * the block.
327 : */
328 :
329 578548 : void GDALRasterBlock::Detach()
330 :
331 : {
332 578548 : CPLMutexHolderD( &hRBMutex );
333 :
334 578548 : if( poOldest == this )
335 3390 : poOldest = poPrevious;
336 :
337 578548 : if( poNewest == this )
338 : {
339 289424 : poNewest = poNext;
340 : }
341 :
342 578548 : if( poPrevious != NULL )
343 167 : poPrevious->poNext = poNext;
344 :
345 578548 : if( poNext != NULL )
346 286201 : poNext->poPrevious = poPrevious;
347 :
348 578548 : poPrevious = NULL;
349 578548 : poNext = NULL;
350 578548 : }
351 :
352 : /************************************************************************/
353 : /* Verify() */
354 : /************************************************************************/
355 :
356 : /**
357 : * Confirms (via assertions) that the block cache linked list is in a
358 : * consistent state.
359 : */
360 :
361 0 : void GDALRasterBlock::Verify()
362 :
363 : {
364 0 : CPLMutexHolderD( &hRBMutex );
365 :
366 0 : CPLAssert( (poNewest == NULL && poOldest == NULL)
367 : || (poNewest != NULL && poOldest != NULL) );
368 :
369 0 : if( poNewest != NULL )
370 : {
371 0 : CPLAssert( poNewest->poPrevious == NULL );
372 0 : CPLAssert( poOldest->poNext == NULL );
373 :
374 0 : for( GDALRasterBlock *poBlock = (GDALRasterBlock *) poNewest;
375 : poBlock != NULL;
376 : poBlock = poBlock->poNext )
377 : {
378 0 : if( poBlock->poPrevious )
379 : {
380 0 : CPLAssert( poBlock->poPrevious->poNext == poBlock );
381 : }
382 :
383 0 : if( poBlock->poNext )
384 : {
385 0 : CPLAssert( poBlock->poNext->poPrevious == poBlock );
386 : }
387 : }
388 0 : }
389 0 : }
390 :
391 : /************************************************************************/
392 : /* Write() */
393 : /************************************************************************/
394 :
395 : /**
396 : * Force writing of the current block, if dirty.
397 : *
398 : * The block is written using GDALRasterBand::IWriteBlock() on it's
399 : * corresponding band object. Even if the write fails the block will
400 : * be marked clean.
401 : *
402 : * @return CE_None otherwise the error returned by IWriteBlock().
403 : */
404 :
405 28892 : CPLErr GDALRasterBlock::Write()
406 :
407 : {
408 28892 : if( !GetDirty() )
409 0 : return CE_None;
410 :
411 28892 : if( poBand == NULL )
412 0 : return CE_Failure;
413 :
414 28892 : MarkClean();
415 :
416 28892 : return poBand->IWriteBlock( nXOff, nYOff, pData );
417 : }
418 :
419 : /************************************************************************/
420 : /* Touch() */
421 : /************************************************************************/
422 :
423 : /**
424 : * Push block to top of LRU (least-recently used) list.
425 : *
426 : * This method is normally called when a block is used to keep track
427 : * that it has been recently used.
428 : */
429 :
430 4605742 : void GDALRasterBlock::Touch()
431 :
432 : {
433 4605742 : CPLMutexHolderD( &hRBMutex );
434 :
435 4605742 : if( poNewest == this )
436 705070 : return;
437 :
438 3900672 : if( poOldest == this )
439 1275144 : poOldest = this->poPrevious;
440 :
441 3900672 : if( poPrevious != NULL )
442 3611081 : poPrevious->poNext = poNext;
443 :
444 3900672 : if( poNext != NULL )
445 2335937 : poNext->poPrevious = poPrevious;
446 :
447 3900672 : poPrevious = NULL;
448 3900672 : poNext = (GDALRasterBlock *) poNewest;
449 :
450 3900672 : if( poNewest != NULL )
451 : {
452 3897449 : CPLAssert( poNewest->poPrevious == NULL );
453 3897449 : poNewest->poPrevious = this;
454 : }
455 3900672 : poNewest = this;
456 :
457 3900672 : if( poOldest == NULL )
458 : {
459 3223 : CPLAssert( poPrevious == NULL && poNext == NULL );
460 3223 : poOldest = this;
461 0 : }
462 : #ifdef ENABLE_DEBUG
463 : Verify();
464 : #endif
465 : }
466 :
467 : /************************************************************************/
468 : /* Internalize() */
469 : /************************************************************************/
470 :
471 : /**
472 : * Allocate memory for block.
473 : *
474 : * This method allocates memory for the block, and attempts to flush other
475 : * blocks, if necessary, to bring the total cache size back within the limits.
476 : * The newly allocated block is touched and will be considered most recently
477 : * used in the LRU list.
478 : *
479 : * @return CE_None on success or CE_Failure if memory allocation fails.
480 : */
481 :
482 288957 : CPLErr GDALRasterBlock::Internalize()
483 :
484 : {
485 288957 : CPLMutexHolderD( &hRBMutex );
486 : void *pNewData;
487 : int nSizeInBytes;
488 288957 : int nCurCacheMax = GDALGetCacheMax();
489 :
490 : /* No risk of overflow as it is checked in GDALRasterBand::InitBlockInfo() */
491 288957 : nSizeInBytes = nXSize * nYSize * (GDALGetDataTypeSize(eType) / 8);
492 :
493 288957 : pNewData = VSIMalloc( nSizeInBytes );
494 288957 : if( pNewData == NULL )
495 : {
496 : CPLError( CE_Failure, CPLE_OutOfMemory,
497 : "GDALRasterBlock::Internalize : Out of memory allocating %d bytes.",
498 0 : nSizeInBytes);
499 0 : return( CE_Failure );
500 : }
501 :
502 288957 : if( pData != NULL )
503 0 : memcpy( pNewData, pData, nSizeInBytes );
504 :
505 288957 : pData = pNewData;
506 :
507 : /* -------------------------------------------------------------------- */
508 : /* Flush old blocks if we are nearing our memory limit. */
509 : /* -------------------------------------------------------------------- */
510 288957 : AddLock(); /* don't flush this block! */
511 :
512 288957 : nCacheUsed += nSizeInBytes;
513 578548 : while( nCacheUsed > nCurCacheMax )
514 : {
515 1109 : int nOldCacheUsed = nCacheUsed;
516 :
517 1109 : GDALFlushCacheBlock();
518 :
519 1109 : if( nCacheUsed == nOldCacheUsed )
520 475 : break;
521 : }
522 :
523 : /* -------------------------------------------------------------------- */
524 : /* Add this block to the list. */
525 : /* -------------------------------------------------------------------- */
526 288957 : Touch();
527 288957 : DropLock();
528 :
529 288957 : return( CE_None );
530 : }
531 :
532 : /************************************************************************/
533 : /* MarkDirty() */
534 : /************************************************************************/
535 :
536 : /**
537 : * Mark the block as modified.
538 : *
539 : * A dirty block is one that has been modified and will need to be written
540 : * to disk before it can be flushed.
541 : */
542 :
543 1411798 : void GDALRasterBlock::MarkDirty()
544 :
545 : {
546 1411798 : bDirty = TRUE;
547 1411798 : }
548 :
549 :
550 : /************************************************************************/
551 : /* MarkClean() */
552 : /************************************************************************/
553 :
554 : /**
555 : * Mark the block as unmodified.
556 : *
557 : * A dirty block is one that has been modified and will need to be written
558 : * to disk before it can be flushed.
559 : */
560 :
561 30258 : void GDALRasterBlock::MarkClean()
562 :
563 : {
564 30258 : bDirty = FALSE;
565 30258 : }
566 :
567 : /************************************************************************/
568 : /* SafeLockBlock() */
569 : /************************************************************************/
570 :
571 : /**
572 : * \brief Safely lock block.
573 : *
574 : * This method locks a GDALRasterBlock (and touches it) in a thread-safe
575 : * manner. The global block cache mutex is held while locking the block,
576 : * in order to avoid race conditions with other threads that might be
577 : * trying to expire the block at the same time. The block pointer may be
578 : * safely NULL, in which case this method does nothing.
579 : *
580 : * @param ppBlock Pointer to the block pointer to try and lock/touch.
581 : */
582 :
583 4316840 : int GDALRasterBlock::SafeLockBlock( GDALRasterBlock ** ppBlock )
584 :
585 : {
586 4316840 : CPLAssert( NULL != ppBlock );
587 :
588 4316840 : CPLMutexHolderD( &hRBMutex );
589 :
590 4316840 : if( *ppBlock != NULL )
591 : {
592 4027828 : (*ppBlock)->AddLock();
593 4027828 : (*ppBlock)->Touch();
594 :
595 4027828 : return TRUE;
596 : }
597 : else
598 289012 : return FALSE;
599 : }
|