1 /******************************************************************************
2  * $Id: gdalrasterblock.cpp 13986 2008-03-12 22:10:20Z 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 "../port/cpl_multiproc.h"
33 
34 CPL_CVSID("$Id: gdalrasterblock.cpp 13986 2008-03-12 22:10:20Z 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  * 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 
GDALSetCacheMax(int nNewSize)59 void CPL_STDCALL GDALSetCacheMax( int nNewSize )
60 
61 {
62     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     while( nCacheUsed > nCacheMax )
69     {
70         int nOldCacheUsed = nCacheUsed;
71 
72         GDALFlushCacheBlock();
73 
74         if( nCacheUsed == nOldCacheUsed )
75             break;
76     }
77 }
78 
79 /************************************************************************/
80 /*                          GDALGetCacheMax()                           */
81 /************************************************************************/
82 
83 /**
84  * 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 
GDALGetCacheMax()92 int CPL_STDCALL GDALGetCacheMax()
93 {
94     if( !bCacheMaxInitialized )
95     {
96         if( CPLGetConfigOption("GDAL_CACHEMAX",NULL) != NULL )
97         {
98             nCacheMax = atoi(CPLGetConfigOption("GDAL_CACHEMAX","10"));
99             if( nCacheMax < 10000 )
100                 nCacheMax *= 1024 * 1024;
101         }
102         bCacheMaxInitialized = TRUE;
103     }
104 
105     return nCacheMax;
106 }
107 
108 /************************************************************************/
109 /*                          GDALGetCacheUsed()                          */
110 /************************************************************************/
111 
112 /**
113  * Get cache memory used.
114  *
115  * @return the number of bytes of memory currently in use by the
116  * GDALRasterBlock memory caching.
117  */
118 
GDALGetCacheUsed()119 int CPL_STDCALL GDALGetCacheUsed()
120 {
121     return nCacheUsed;
122 }
123 
124 /************************************************************************/
125 /*                        GDALFlushCacheBlock()                         */
126 /*                                                                      */
127 /*      The workhorse of cache management!                              */
128 /************************************************************************/
129 
GDALFlushCacheBlock()130 int CPL_STDCALL GDALFlushCacheBlock()
131 
132 {
133     return GDALRasterBlock::FlushCacheBlock();
134 }
135 
136 /************************************************************************/
137 /*                          FlushCacheBlock()                           */
138 /*                                                                      */
139 /*      Note, if we have alot of blocks locked for a long time, this    */
140 /*      method is going to get slow because it will have to traverse    */
141 /*      the linked list a long ways looking for a flushing              */
142 /*      candidate.   It might help to re-touch locked blocks to push    */
143 /*      them to the top of the list.                                    */
144 /************************************************************************/
145 
FlushCacheBlock()146 int GDALRasterBlock::FlushCacheBlock()
147 
148 {
149     int nXOff, nYOff;
150     GDALRasterBand *poBand;
151 
152     {
153         CPLMutexHolderD( &hRBMutex );
154         GDALRasterBlock *poTarget = (GDALRasterBlock *) poOldest;
155 
156         while( poTarget != NULL && poTarget->GetLockCount() > 0 )
157             poTarget = poTarget->poPrevious;
158 
159         if( poTarget == NULL )
160             return FALSE;
161 
162         poTarget->Detach();
163 
164         nXOff = poTarget->GetXOff();
165         nYOff = poTarget->GetYOff();
166         poBand = poTarget->GetBand();
167     }
168 
169     poBand->FlushBlock( nXOff, nYOff );
170 
171     return TRUE;
172 }
173 
174 /************************************************************************/
175 /*                          GDALRasterBlock()                           */
176 /************************************************************************/
177 
GDALRasterBlock(GDALRasterBand * poBandIn,int nXOffIn,int nYOffIn)178 GDALRasterBlock::GDALRasterBlock( GDALRasterBand *poBandIn,
179                                   int nXOffIn, int nYOffIn )
180 
181 {
182     poBand = poBandIn;
183 
184     poBand->GetBlockSize( &nXSize, &nYSize );
185     eType = poBand->GetRasterDataType();
186     pData = NULL;
187     bDirty = FALSE;
188     nLockCount = 0;
189 
190     poNext = poPrevious = NULL;
191 
192     nXOff = nXOffIn;
193     nYOff = nYOffIn;
194 }
195 
196 /************************************************************************/
197 /*                          ~GDALRasterBlock()                          */
198 /************************************************************************/
199 
~GDALRasterBlock()200 GDALRasterBlock::~GDALRasterBlock()
201 
202 {
203     Detach();
204 
205     if( pData != NULL )
206     {
207         int nSizeInBytes;
208 
209         VSIFree( pData );
210 
211         nSizeInBytes = (nXSize * nYSize * GDALGetDataTypeSize(eType)+7)/8;
212 
213         {
214             CPLMutexHolderD( &hRBMutex );
215             nCacheUsed -= nSizeInBytes;
216         }
217     }
218 
219     CPLAssert( nLockCount == 0 );
220 
221 #ifdef ENABLE_DEBUG
222     Verify();
223 #endif
224 }
225 
226 /************************************************************************/
227 /*                               Detach()                               */
228 /*                                                                      */
229 /*      Remove from block lists.                                        */
230 /************************************************************************/
231 
Detach()232 void GDALRasterBlock::Detach()
233 
234 {
235     CPLMutexHolderD( &hRBMutex );
236 
237     if( poOldest == this )
238         poOldest = poPrevious;
239 
240     if( poNewest == this )
241     {
242         poNewest = poNext;
243     }
244 
245     if( poPrevious != NULL )
246         poPrevious->poNext = poNext;
247 
248     if( poNext != NULL )
249         poNext->poPrevious = poPrevious;
250 
251     poPrevious = NULL;
252     poNext = NULL;
253 }
254 
255 /************************************************************************/
256 /*                               Verify()                               */
257 /************************************************************************/
258 
Verify()259 void GDALRasterBlock::Verify()
260 
261 {
262     CPLMutexHolderD( &hRBMutex );
263 
264     CPLAssert( (poNewest == NULL && poOldest == NULL)
265                || (poNewest != NULL && poOldest != NULL) );
266 
267     if( poNewest != NULL )
268     {
269         CPLAssert( poNewest->poPrevious == NULL );
270         CPLAssert( poOldest->poNext == NULL );
271 
272         for( GDALRasterBlock *poBlock = (GDALRasterBlock *) poNewest;
273              poBlock != NULL;
274              poBlock = poBlock->poNext )
275         {
276             if( poBlock->poPrevious )
277             {
278                 CPLAssert( poBlock->poPrevious->poNext == poBlock );
279             }
280 
281             if( poBlock->poNext )
282             {
283                 CPLAssert( poBlock->poNext->poPrevious == poBlock );
284             }
285         }
286     }
287 }
288 
289 /************************************************************************/
290 /*                               Write()                                */
291 /************************************************************************/
292 
Write()293 CPLErr GDALRasterBlock::Write()
294 
295 {
296     if( !GetDirty() )
297         return CE_None;
298 
299     if( poBand == NULL )
300         return CE_Failure;
301 
302     MarkClean();
303 
304     return poBand->IWriteBlock( nXOff, nYOff, pData );
305 }
306 
307 /************************************************************************/
308 /*                               Touch()                                */
309 /************************************************************************/
310 
Touch()311 void GDALRasterBlock::Touch()
312 
313 {
314     CPLMutexHolderD( &hRBMutex );
315 
316     if( poNewest == this )
317         return;
318 
319     if( poOldest == this )
320         poOldest = this->poPrevious;
321 
322     if( poPrevious != NULL )
323         poPrevious->poNext = poNext;
324 
325     if( poNext != NULL )
326         poNext->poPrevious = poPrevious;
327 
328     poPrevious = NULL;
329     poNext = (GDALRasterBlock *) poNewest;
330 
331     if( poNewest != NULL )
332     {
333         CPLAssert( poNewest->poPrevious == NULL );
334         poNewest->poPrevious = this;
335     }
336     poNewest = this;
337 
338     if( poOldest == NULL )
339     {
340         CPLAssert( poPrevious == NULL && poNext == NULL );
341         poOldest = this;
342     }
343 #ifdef ENABLE_DEBUG
344     Verify();
345 #endif
346 }
347 
348 /************************************************************************/
349 /*                            Internalize()                             */
350 /************************************************************************/
351 
Internalize()352 CPLErr GDALRasterBlock::Internalize()
353 
354 {
355     CPLMutexHolderD( &hRBMutex );
356     void        *pNewData;
357     int         nSizeInBytes;
358     int         nCurCacheMax = GDALGetCacheMax();
359 
360     //FIXME? : risk of overflow in multiplication
361     nSizeInBytes = nXSize * nYSize * (GDALGetDataTypeSize(eType) / 8);
362 
363     pNewData = VSIMalloc( nSizeInBytes );
364     if( pNewData == NULL )
365     {
366         CPLError( CE_Failure, CPLE_OutOfMemory,
367                   "GDALRasterBlock::Internalize : Out of memory allocating %d bytes.",
368                   nSizeInBytes);
369         return( CE_Failure );
370     }
371 
372     if( pData != NULL )
373         memcpy( pNewData, pData, nSizeInBytes );
374 
375     pData = pNewData;
376 
377 /* -------------------------------------------------------------------- */
378 /*      Flush old blocks if we are nearing our memory limit.            */
379 /* -------------------------------------------------------------------- */
380     AddLock(); /* don't flush this block! */
381 
382     nCacheUsed += nSizeInBytes;
383     while( nCacheUsed > nCurCacheMax )
384     {
385         int nOldCacheUsed = nCacheUsed;
386 
387         GDALFlushCacheBlock();
388 
389         if( nCacheUsed == nOldCacheUsed )
390             break;
391     }
392 
393 /* -------------------------------------------------------------------- */
394 /*      Add this block to the list.                                     */
395 /* -------------------------------------------------------------------- */
396     Touch();
397     DropLock();
398 
399     return( CE_None );
400 }
401 
402 /************************************************************************/
403 /*                             MarkDirty()                              */
404 /************************************************************************/
405 
MarkDirty()406 void GDALRasterBlock::MarkDirty()
407 
408 {
409     bDirty = TRUE;
410 }
411 
412 
413 /************************************************************************/
414 /*                             MarkClean()                              */
415 /************************************************************************/
416 
MarkClean()417 void GDALRasterBlock::MarkClean()
418 
419 {
420     bDirty = FALSE;
421 }
422 
423 /************************************************************************/
424 /*                           SafeLockBlock()                            */
425 /************************************************************************/
426 
427 /**
428  * Safely lock block.
429  *
430  * This method locks a GDALRasterBlock (and touches it) in a thread-safe
431  * manner.  The global block cache mutex is held while locking the block,
432  * in order to avoid race conditions with other threads that might be
433  * trying to expire the block at the same time.  The block pointer may be
434  * safely NULL, in which case this method does nothing.
435  *
436  * @param ppBlock Pointer to the block pointer to try and lock/touch.
437  */
438 
SafeLockBlock(GDALRasterBlock ** ppBlock)439 int GDALRasterBlock::SafeLockBlock( GDALRasterBlock ** ppBlock )
440 
441 {
442     CPLMutexHolderD( &hRBMutex );
443 
444     if( *ppBlock != NULL )
445     {
446         (*ppBlock)->AddLock();
447         (*ppBlock)->Touch();
448 
449         return TRUE;
450     }
451     else
452         return FALSE;
453 }
454