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