root/tags/gdal_1_3_2/gcore/gdalrasterblock.cpp

Revision 9125, 15.1 kB (checked in by fwarmerdam, 3 years ago)

applied some strategic improved outofmemory checking

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 /******************************************************************************
2  * $Id$
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  * $Log$
32  * Revision 1.22  2006/02/07 19:07:08  fwarmerdam
33  * applied some strategic improved outofmemory checking
34  *
35  * Revision 1.21  2006/01/13 20:40:47  fwarmerdam
36  * grab mutex in raster block destructor while updating nCacheUsed.
37  *
38  * Revision 1.20  2005/07/11 19:06:36  fwarmerdam
39  * Removed tile ticker, and GDALRasterBlock age.
40  *
41  * Revision 1.19  2005/05/23 14:28:21  fwarmerdam
42  * fixed serious flaw in Detach() method
43  *
44  * Revision 1.18  2005/05/23 06:43:37  fwarmerdam
45  * adding mutex for block list
46  *
47  * Revision 1.17  2005/04/04 15:24:48  fwarmerdam
48  * Most C entry points now CPL_STDCALL
49  *
50  * Revision 1.16  2004/04/07 13:15:57  warmerda
51  * Ensure that GDALSetCacheMax() keeps flushing till the new limit is
52  * honoured. http://bugzilla.remotesensing.org/show_bug.cgi?id=542
53  *
54  * Revision 1.15  2004/04/06 19:16:16  dron
55  * Remove GDALRasterBlock::IsCached() method in favor
56  * of GDALRasterBand::IsBlockCached().
57  *
58  * Revision 1.14  2004/03/19 05:17:42  warmerda
59  * Increase default cachesize to 10MB.
60  *
61  * Revision 1.13  2004/03/15 08:33:38  warmerda
62  * Use CPLGetConfigOption() for cache max.
63  *
64  * Revision 1.12  2003/07/27 11:01:01  dron
65  * GDALRasterBlock::IsCached() method added.
66  *
67  * Revision 1.11  2003/04/25 19:48:16  warmerda
68  * added block locking to ensure in-use blocks arent flushed
69  *
70  * Revision 1.10  2003/02/21 20:07:55  warmerda
71  * update header
72  *
73  * Revision 1.9  2003/01/28 16:51:24  warmerda
74  * document cache functions
75  *
76  * Revision 1.8  2002/07/09 20:33:12  warmerda
77  * expand tabs
78  *
79  * Revision 1.7  2001/09/27 16:33:41  warmerda
80  * fixed problems with blocks larger than 2GB/8
81  *
82  * Revision 1.6  2001/07/18 04:04:30  warmerda
83  * added CPL_CVSID
84  *
85  * Revision 1.5  2001/06/22 21:00:06  warmerda
86  * fixed support for caching override by environment variable
87  *
88  * Revision 1.4  2001/06/22 20:09:13  warmerda
89  * added GDAL_CACHEMAX environment variable support
90  *
91  * Revision 1.3  2000/03/31 13:42:49  warmerda
92  * added debugging code
93  *
94  * Revision 1.2  2000/03/24 00:09:05  warmerda
95  * rewrote cache management
96  *
97  * Revision 1.1  1998/12/31 18:52:58  warmerda
98  * New
99  *
100  */
101
102 #include "gdal_priv.h"
103 #include "cpl_multiproc.h"
104
105 CPL_CVSID("$Id$");
106
107 static int bCacheMaxInitialized = FALSE;
108 static int nCacheMax = 10 * 1024*1024;
109 static volatile int nCacheUsed = 0;
110
111 static volatile GDALRasterBlock *poOldest = NULL;    /* tail */
112 static volatile GDALRasterBlock *poNewest = NULL;    /* head */
113
114 static void *hRBMutex = NULL;
115
116
117 /************************************************************************/
118 /*                          GDALSetCacheMax()                           */
119 /************************************************************************/
120
121 /**
122  * Set maximum cache memory.
123  *
124  * This function sets the maximum amount of memory that GDAL is permitted
125  * to use for GDALRasterBlock caching.
126  *
127  * @param nNewSize the maximum number of bytes for caching.  Maximum is 2GB.
128  */
129
130 void CPL_STDCALL GDALSetCacheMax( int nNewSize )
131
132 {
133     nCacheMax = nNewSize;
134
135 /* -------------------------------------------------------------------- */
136 /*      Flush blocks till we are under the new limit or till we         */
137 /*      can't seem to flush anymore.                                    */
138 /* -------------------------------------------------------------------- */
139     while( nCacheUsed > nCacheMax )
140     {
141         int nOldCacheUsed = nCacheUsed;
142
143         GDALFlushCacheBlock();
144
145         if( nCacheUsed == nOldCacheUsed )
146             break;
147     }
148 }
149
150 /************************************************************************/
151 /*                          GDALGetCacheMax()                           */
152 /************************************************************************/
153
154 /**
155  * Get maximum cache memory.
156  *
157  * Gets the maximum amount of memory available to the GDALRasterBlock
158  * caching system for caching GDAL read/write imagery.
159  *
160  * @return maximum in bytes.
161  */
162
163 int CPL_STDCALL GDALGetCacheMax()
164 {
165     if( !bCacheMaxInitialized )
166     {
167         if( CPLGetConfigOption("GDAL_CACHEMAX",NULL) != NULL )
168         {
169             nCacheMax = atoi(CPLGetConfigOption("GDAL_CACHEMAX","10"));
170             if( nCacheMax < 1000 )
171                 nCacheMax *= 1024 * 1024;
172         }
173         bCacheMaxInitialized = TRUE;
174     }
175    
176     return nCacheMax;
177 }
178
179 /************************************************************************/
180 /*                          GDALGetCacheUsed()                          */
181 /************************************************************************/
182
183 /**
184  * Get cache memory used.
185  *
186  * @return the number of bytes of memory currently in use by the
187  * GDALRasterBlock memory caching.
188  */
189
190 int CPL_STDCALL GDALGetCacheUsed()
191 {
192     return nCacheUsed;
193 }
194
195 /************************************************************************/
196 /*                        GDALFlushCacheBlock()                         */
197 /*                                                                      */
198 /*      The workhorse of cache management!                              */
199 /************************************************************************/
200
201 int CPL_STDCALL GDALFlushCacheBlock()
202
203 {
204     return GDALRasterBlock::FlushCacheBlock();
205 }
206
207 /************************************************************************/
208 /*                          FlushCacheBlock()                           */
209 /*                                                                      */
210 /*      Note, if we have alot of blocks locked for a long time, this    */
211 /*      method is going to get slow because it will have to traverse    */
212 /*      the linked list a long ways looking for a flushing              */
213 /*      candidate.   It might help to re-touch locked blocks to push    */
214 /*      them to the top of the list.                                    */
215 /************************************************************************/
216
217 int GDALRasterBlock::FlushCacheBlock()
218
219 {
220     int nXOff, nYOff;
221     GDALRasterBand *poBand;
222
223     {
224         CPLMutexHolderD( &hRBMutex );
225         GDALRasterBlock *poTarget = (GDALRasterBlock *) poOldest;
226
227         while( poTarget != NULL && poTarget->GetLockCount() > 0 )
228             poTarget = poTarget->poPrevious;
229        
230         if( poTarget == NULL )
231             return FALSE;
232
233         poTarget->Detach();
234
235         nXOff = poTarget->GetXOff();
236         nYOff = poTarget->GetYOff();
237         poBand = poTarget->GetBand();
238     }
239
240     poBand->FlushBlock( nXOff, nYOff );
241
242     return TRUE;
243 }
244
245 /************************************************************************/
246 /*                          GDALRasterBlock()                           */
247 /************************************************************************/
248
249 GDALRasterBlock::GDALRasterBlock( GDALRasterBand *poBandIn,
250                                   int nXOffIn, int nYOffIn )
251
252 {
253     poBand = poBandIn;
254
255     poBand->GetBlockSize( &nXSize, &nYSize );
256     eType = poBand->GetRasterDataType();
257     pData = NULL;
258     bDirty = FALSE;
259     nLockCount = 0;
260
261     poNext = poPrevious = NULL;
262
263     nXOff = nXOffIn;
264     nYOff = nYOffIn;
265 }
266
267 /************************************************************************/
268 /*                          ~GDALRasterBlock()                          */
269 /************************************************************************/
270
271 GDALRasterBlock::~GDALRasterBlock()
272
273 {
274     Detach();
275
276     if( pData != NULL )
277     {
278         int nSizeInBytes;
279
280         VSIFree( pData );
281
282         nSizeInBytes = (nXSize * nYSize * GDALGetDataTypeSize(eType)+7)/8;
283
284         {
285             CPLMutexHolderD( &hRBMutex );
286             nCacheUsed -= nSizeInBytes;
287         }
288     }
289
290     CPLAssert( nLockCount == 0 );
291
292 #ifdef ENABLE_DEBUG
293     Verify();
294 #endif
295 }
296
297 /************************************************************************/
298 /*                               Detach()                               */
299 /*                                                                      */
300 /*      Remove from block lists.                                        */
301 /************************************************************************/
302
303 void GDALRasterBlock::Detach()
304
305 {
306     CPLMutexHolderD( &hRBMutex );
307
308     if( poOldest == this )
309         poOldest = poPrevious;
310
311     if( poNewest == this )
312     {
313         poNewest = poNext;
314     }
315
316     if( poPrevious != NULL )
317         poPrevious->poNext = poNext;
318
319     if( poNext != NULL )
320         poNext->poPrevious = poPrevious;
321
322     poPrevious = NULL;
323     poNext = NULL;
324 }
325
326 /************************************************************************/
327 /*                               Verify()                               */
328 /************************************************************************/
329
330 void GDALRasterBlock::Verify()
331
332 {
333     CPLMutexHolderD( &hRBMutex );
334
335     CPLAssert( (poNewest == NULL && poOldest == NULL)
336                || (poNewest != NULL && poOldest != NULL) );
337
338     if( poNewest != NULL )
339     {
340         CPLAssert( poNewest->poPrevious == NULL );
341         CPLAssert( poOldest->poNext == NULL );
342        
343         for( GDALRasterBlock *poBlock = (GDALRasterBlock *) poNewest;
344              poBlock != NULL;
345              poBlock = poBlock->poNext )
346         {
347             if( poBlock->poPrevious )
348             {
349                 CPLAssert( poBlock->poPrevious->poNext == poBlock );
350             }
351
352             if( poBlock->poNext )
353             {
354                 CPLAssert( poBlock->poNext->poPrevious == poBlock );
355             }
356         }
357     }
358 }
359
360 /************************************************************************/
361 /*                               Write()                                */
362 /************************************************************************/
363
364 CPLErr GDALRasterBlock::Write()
365
366 {
367     if( !GetDirty() )
368         return CE_None;
369
370     if( poBand == NULL )
371         return CE_Failure;
372
373     MarkClean();
374
375     return poBand->IWriteBlock( nXOff, nYOff, pData );
376 }
377
378 /************************************************************************/
379 /*                               Touch()                                */
380 /************************************************************************/
381
382 void GDALRasterBlock::Touch()
383
384 {
385     CPLMutexHolderD( &hRBMutex );
386
387     if( poNewest == this )
388         return;
389
390     if( poOldest == this )
391         poOldest = this->poPrevious;
392    
393     if( poPrevious != NULL )
394         poPrevious->poNext = poNext;
395
396     if( poNext != NULL )
397         poNext->poPrevious = poPrevious;
398
399     poPrevious = NULL;
400     poNext = (GDALRasterBlock *) poNewest;
401
402     if( poNewest != NULL )
403     {
404         CPLAssert( poNewest->poPrevious == NULL );
405         poNewest->poPrevious = this;
406     }
407     poNewest = this;
408    
409     if( poOldest == NULL )
410     {
411         CPLAssert( poPrevious == NULL && poNext == NULL );
412         poOldest = this;
413     }
414 #ifdef ENABLE_DEBUG
415     Verify();
416 #endif
417 }
418
419 /************************************************************************/
420 /*                            Internalize()                             */
421 /************************************************************************/
422
423 CPLErr GDALRasterBlock::Internalize()
424
425 {
426     CPLMutexHolderD( &hRBMutex );
427     void        *pNewData;
428     int         nSizeInBytes;
429     int         nCurCacheMax = GDALGetCacheMax();
430
431     nSizeInBytes = nXSize * nYSize * (GDALGetDataTypeSize(eType) / 8);
432
433     pNewData = VSIMalloc( nSizeInBytes );
434     if( pNewData == NULL )
435     {
436         CPLError( CE_Failure, CPLE_OutOfMemory,
437                   "Internalize failed" );
438         return( CE_Failure );
439     }
440
441     if( pData != NULL )
442         memcpy( pNewData, pData, nSizeInBytes );
443    
444     pData = pNewData;
445
446 /* -------------------------------------------------------------------- */
447 /*      Flush old blocks if we are nearing our memory limit.            */
448 /* -------------------------------------------------------------------- */
449     AddLock(); /* don't flush this block! */
450
451     nCacheUsed += nSizeInBytes;
452     while( nCacheUsed > nCurCacheMax )
453     {
454         int nOldCacheUsed = nCacheUsed;
455
456         GDALFlushCacheBlock();
457
458         if( nCacheUsed == nOldCacheUsed )
459             break;
460     }
461
462 /* -------------------------------------------------------------------- */
463 /*      Add this block to the list.                                     */
464 /* -------------------------------------------------------------------- */
465     Touch();
466     DropLock();
467
468     return( CE_None );
469 }
470
471 /************************************************************************/
472 /*                             MarkDirty()                              */
473 /************************************************************************/
474
475 void GDALRasterBlock::MarkDirty()
476
477 {
478     bDirty = TRUE;
479 }
480
481
482 /************************************************************************/
483 /*                             MarkClean()                              */
484 /************************************************************************/
485
486 void GDALRasterBlock::MarkClean()
487
488 {
489     bDirty = FALSE;
490 }
491
492 /************************************************************************/
493 /*                           SafeLockBlock()                            */
494 /************************************************************************/
495
496 /**
497  * Safely lock block.
498  *
499  * This method locks a GDALRasterBlock (and touches it) in a thread-safe
500  * manner.  The global block cache mutex is held while locking the block,
501  * in order to avoid race conditions with other threads that might be
502  * trying to expire the block at the same time.  The block pointer may be
503  * safely NULL, in which case this method does nothing.
504  *
505  * @param ppBlock Pointer to the block pointer to try and lock/touch.
506  */
507  
508 int GDALRasterBlock::SafeLockBlock( GDALRasterBlock ** ppBlock )
509
510 {
511     CPLMutexHolderD( &hRBMutex );
512
513     if( *ppBlock != NULL )
514     {
515         (*ppBlock)->AddLock();
516         (*ppBlock)->Touch();
517        
518         return TRUE;
519     }
520     else
521         return FALSE;
522 }
Note: See TracBrowser for help on using the browser.