wasCSharpSQLite – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 using System.Diagnostics;
2  
3 using u32 = System.UInt32;
4 using Pgno = System.UInt32;
5  
6 namespace Community.CsharpSqlite
7 {
8 using sqlite3_pcache = Sqlite3.PCache1;
9 public partial class Sqlite3
10 {
11 /*
12 ** 2008 November 05
13 **
14 ** The author disclaims copyright to this source code. In place of
15 ** a legal notice, here is a blessing:
16 **
17 ** May you do good and not evil.
18 ** May you find forgiveness for yourself and forgive others.
19 ** May you share freely, never taking more than you give.
20 **
21 *************************************************************************
22 **
23 ** This file implements the default page cache implementation (the
24 ** sqlite3_pcache interface). It also contains part of the implementation
25 ** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features.
26 ** If the default page cache implementation is overriden, then neither of
27 ** these two features are available.
28 *************************************************************************
29 ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
30 ** C#-SQLite is an independent reimplementation of the SQLite software library
31 **
32 ** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2
33 **
34 *************************************************************************
35 */
36  
37 //#include "sqliteInt.h"
38  
39 //typedef struct PCache1 PCache1;
40 //typedef struct PgHdr1 PgHdr1;
41 //typedef struct PgFreeslot PgFreeslot;
42 //typedef struct PGroup PGroup;
43  
44 /* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
45 ** of one or more PCaches that are able to recycle each others unpinned
46 ** pages when they are under memory pressure. A PGroup is an instance of
47 ** the following object.
48 **
49 ** This page cache implementation works in one of two modes:
50 **
51 ** (1) Every PCache is the sole member of its own PGroup. There is
52 ** one PGroup per PCache.
53 **
54 ** (2) There is a single global PGroup that all PCaches are a member
55 ** of.
56 **
57 ** Mode 1 uses more memory (since PCache instances are not able to rob
58 ** unused pages from other PCaches) but it also operates without a mutex,
59 ** and is therefore often faster. Mode 2 requires a mutex in order to be
60 ** threadsafe, but is able recycle pages more efficient.
61 **
62 ** For mode (1), PGroup.mutex is NULL. For mode (2) there is only a single
63 ** PGroup which is the pcache1.grp global variable and its mutex is
64 ** SQLITE_MUTEX_STATIC_LRU.
65 */
66 public class PGroup
67 {
68 public sqlite3_mutex mutex; /* MUTEX_STATIC_LRU or NULL */
69 public int nMaxPage; /* Sum of nMax for purgeable caches */
70 public int nMinPage; /* Sum of nMin for purgeable caches */
71 public int mxPinned; /* nMaxpage + 10 - nMinPage */
72 public int nCurrentPage; /* Number of purgeable pages allocated */
73 public PgHdr1 pLruHead, pLruTail; /* LRU list of unpinned pages */
74 // C#
75 public PGroup()
76 {
77 mutex = new sqlite3_mutex();
78 }
79 };
80  
81 /* Each page cache is an instance of the following object. Every
82 ** open database file (including each in-memory database and each
83 ** temporary or transient database) has a single page cache which
84 ** is an instance of this object.
85 **
86 ** Pointers to structures of this type are cast and returned as
87 ** opaque sqlite3_pcache* handles.
88 */
89 public class PCache1
90 {
91 /* Cache configuration parameters. Page size (szPage) and the purgeable
92 ** flag (bPurgeable) are set when the cache is created. nMax may be
93 ** modified at any time by a call to the pcache1CacheSize() method.
94 ** The PGroup mutex must be held when accessing nMax.
95 */
96 public PGroup pGroup; /* PGroup this cache belongs to */
97 public int szPage; /* Size of allocated pages in bytes */
98 public bool bPurgeable; /* True if cache is purgeable */
99 public int nMin; /* Minimum number of pages reserved */
100 public int nMax; /* Configured "cache_size" value */
101 public int n90pct; /* nMax*9/10 */
102  
103 /* Hash table of all pages. The following variables may only be accessed
104 ** when the accessor is holding the PGroup mutex.
105 */
106 public int nRecyclable; /* Number of pages in the LRU list */
107 public int nPage; /* Total number of pages in apHash */
108 public int nHash; /* Number of slots in apHash[] */
109 public PgHdr1[] apHash; /* Hash table for fast lookup by key */
110  
111 public Pgno iMaxKey; /* Largest key seen since xTruncate() */
112  
113 public void Clear()
114 {
115 nRecyclable = 0;
116 nPage = 0;
117 nHash = 0;
118 apHash = null;
119 iMaxKey = 0;
120 }
121 };
122  
123 /*
124 ** Each cache entry is represented by an instance of the following
125 ** structure. A buffer of PgHdr1.pCache.szPage bytes is allocated
126 ** directly before this structure in memory (see the PGHDR1_TO_PAGE()
127 ** macro below).
128 */
129 public class PgHdr1
130 {
131 public Pgno iKey; /* Key value (page number) */
132 public PgHdr1 pNext; /* Next in hash table chain */
133 public PCache1 pCache; /* Cache that currently owns this page */
134 public PgHdr1 pLruNext; /* Next in LRU list of unpinned pages */
135 public PgHdr1 pLruPrev; /* Previous in LRU list of unpinned pages */
136  
137 // For C#
138 public PgHdr pPgHdr = new PgHdr(); /* Pointer to Actual Page Header */
139  
140 public void Clear()
141 {
142 this.iKey = 0;
143 this.pNext = null;
144 this.pCache = null;
145 this.pPgHdr.Clear();
146 }
147  
148 };
149  
150 /*
151 ** Free slots in the allocator used to divide up the buffer provided using
152 ** the SQLITE_CONFIG_PAGECACHE mechanism.
153 */
154 public class PgFreeslot
155 {
156 public PgFreeslot pNext; /* Next free slot */
157 public PgHdr _PgHdr; /* Next Free Header */
158 };
159  
160 /*
161 ** Global data used by this cache.
162 */
163 public class PCacheGlobal
164 {
165 public PGroup grp; /* The global PGroup for mode (2) */
166  
167 /* Variables related to SQLITE_CONFIG_PAGECACHE settings. The
168 ** szSlot, nSlot, pStart, pEnd, nReserve, and isInit values are all
169 ** fixed at sqlite3_initialize() time and do not require mutex protection.
170 ** The nFreeSlot and pFree values do require mutex protection.
171 */
172 public bool isInit; /* True if initialized */
173 public int szSlot; /* Size of each free slot */
174 public int nSlot; /* The number of pcache slots */
175 public int nReserve; /* Try to keep nFreeSlot above this */
176 public object pStart, pEnd; /* Bounds of pagecache malloc range */
177 /* Above requires no mutex. Use mutex below for variable that follow. */
178 public sqlite3_mutex mutex; /* Mutex for accessing the following: */
179 public int nFreeSlot; /* Number of unused pcache slots */
180 public PgFreeslot pFree; /* Free page blocks */
181 /* The following value requires a mutex to change. We skip the mutex on
182 ** reading because (1) most platforms read a 32-bit integer atomically and
183 ** (2) even if an incorrect value is read, no great harm is done since this
184 ** is really just an optimization. */
185 public bool bUnderPressure; /* True if low on PAGECACHE memory */
186  
187 // C#
188 public PCacheGlobal()
189 {
190 grp = new PGroup();
191 }
192 }
193 static PCacheGlobal pcache = new PCacheGlobal();
194  
195 /*
196 ** All code in this file should access the global structure above via the
197 ** alias "pcache1". This ensures that the WSD emulation is used when
198 ** compiling for systems that do not support real WSD.
199 */
200 //#define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g))
201 static PCacheGlobal pcache1 = pcache;
202  
203 /*
204 ** When a PgHdr1 structure is allocated, the associated PCache1.szPage
205 ** bytes of data are located directly before it in memory (i.e. the total
206 ** size of the allocation is sizeof(PgHdr1)+PCache1.szPage byte). The
207 ** PGHDR1_TO_PAGE() macro takes a pointer to a PgHdr1 structure as
208 ** an argument and returns a pointer to the associated block of szPage
209 ** bytes. The PAGE_TO_PGHDR1() macro does the opposite: its argument is
210 ** a pointer to a block of szPage bytes of data and the return value is
211 ** a pointer to the associated PgHdr1 structure.
212 **
213 ** Debug.Assert( PGHDR1_TO_PAGE(PAGE_TO_PGHDR1(pCache, X))==X );
214 */
215 //#define PGHDR1_TO_PAGE(p) (void)(((char)p) - p.pCache.szPage)
216 static PgHdr PGHDR1_TO_PAGE( PgHdr1 p )
217 {
218 return p.pPgHdr;
219 }
220  
221 //#define PAGE_TO_PGHDR1(c, p) (PgHdr1)(((char)p) + c.szPage)
222 static PgHdr1 PAGE_TO_PGHDR1( PCache1 c, PgHdr p )
223 {
224 return p.pPgHdr1;
225 }
226  
227 /*
228 ** Macros to enter and leave the PCache LRU mutex.
229 */
230 //#define pcache1EnterMutex(X) sqlite3_mutex_enter((X).mutex)
231 static void pcache1EnterMutex( PGroup X )
232 {
233 sqlite3_mutex_enter( X.mutex );
234 }
235 //#define pcache1LeaveMutex(X) sqlite3_mutex_leave((X).mutex)
236 static void pcache1LeaveMutex( PGroup X )
237 {
238 sqlite3_mutex_leave( X.mutex );
239 }
240  
241 /******************************************************************************/
242 /******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************/
243  
244 /*
245 ** This function is called during initialization if a static buffer is
246 ** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE
247 ** verb to sqlite3_config(). Parameter pBuf points to an allocation large
248 ** enough to contain 'n' buffers of 'sz' bytes each.
249 **
250 ** This routine is called from sqlite3_initialize() and so it is guaranteed
251 ** to be serialized already. There is no need for further mutexing.
252 */
253 static void sqlite3PCacheBufferSetup( object pBuf, int sz, int n )
254 {
255 if ( pcache1.isInit )
256 {
257 PgFreeslot p;
258 sz = ROUNDDOWN8( sz );
259 pcache1.szSlot = sz;
260 pcache1.nSlot = pcache1.nFreeSlot = n;
261 pcache1.nReserve = n > 90 ? 10 : ( n / 10 + 1 );
262 pcache1.pStart = null;
263 pcache1.pEnd = null;
264 pcache1.pFree = null;
265 pcache1.bUnderPressure = false;
266 while ( n-- > 0 )
267 {
268 p = new PgFreeslot();// (PgFreeslot)pBuf;
269 p._PgHdr = new PgHdr();
270 p.pNext = pcache1.pFree;
271 pcache1.pFree = p;
272 //pBuf = (void)&((char)pBuf)[sz];
273 }
274 pcache1.pEnd = pBuf;
275 }
276 }
277  
278 /*
279 ** Malloc function used within this file to allocate space from the buffer
280 ** configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no
281 ** such buffer exists or there is no space left in it, this function falls
282 ** back to sqlite3Malloc().
283 **
284 ** Multiple threads can run this routine at the same time. Global variables
285 ** in pcache1 need to be protected via mutex.
286 */
287 static PgHdr pcache1Alloc( int nByte )
288 {
289 PgHdr p = null;
290 Debug.Assert( sqlite3_mutex_notheld( pcache1.grp.mutex ) );
291 sqlite3StatusSet( SQLITE_STATUS_PAGECACHE_SIZE, nByte );
292 if ( nByte <= pcache1.szSlot )
293 {
294 sqlite3_mutex_enter( pcache1.mutex );
295 p = pcache1.pFree._PgHdr;
296 if ( p != null )
297 {
298 pcache1.pFree = pcache1.pFree.pNext;
299 pcache1.nFreeSlot--;
300 pcache1.bUnderPressure = pcache1.nFreeSlot < pcache1.nReserve;
301 Debug.Assert( pcache1.nFreeSlot >= 0 );
302 sqlite3StatusAdd( SQLITE_STATUS_PAGECACHE_USED, 1 );
303 }
304 sqlite3_mutex_leave( pcache1.mutex );
305 }
306 if ( p == null )
307 {
308 /* Memory is not available in the SQLITE_CONFIG_PAGECACHE pool. Get
309 ** it from sqlite3Malloc instead.
310 */
311 p = new PgHdr();// sqlite3Malloc( nByte );
312 //if ( p != null )
313 {
314 int sz = nByte;//sqlite3MallocSize( p );
315 sqlite3_mutex_enter( pcache1.mutex );
316 sqlite3StatusAdd( SQLITE_STATUS_PAGECACHE_OVERFLOW, sz );
317 sqlite3_mutex_leave( pcache1.mutex );
318 }
319 sqlite3MemdebugSetType( p, MEMTYPE_PCACHE );
320 }
321 return p;
322 }
323  
324 /*
325 ** Free an allocated buffer obtained from pcache1Alloc().
326 */
327 static void pcache1Free( ref PgHdr p )
328 {
329 if ( p == null )
330 return;
331 if ( p.CacheAllocated )//if ( p >= pcache1.pStart && p < pcache1.pEnd )
332 {
333 PgFreeslot pSlot = new PgFreeslot();
334 sqlite3_mutex_enter( pcache1.mutex );
335 sqlite3StatusAdd( SQLITE_STATUS_PAGECACHE_USED, -1 );
336 pSlot._PgHdr = p;// pSlot = (PgFreeslot)p;
337 pSlot.pNext = pcache1.pFree;
338 pcache1.pFree = pSlot;
339 pcache1.nFreeSlot++;
340 pcache1.bUnderPressure = pcache1.nFreeSlot < pcache1.nReserve;
341 Debug.Assert( pcache1.nFreeSlot <= pcache1.nSlot );
342 sqlite3_mutex_leave( pcache1.mutex );
343 }
344 else
345 {
346 int iSize;
347 Debug.Assert( sqlite3MemdebugHasType( p, MEMTYPE_PCACHE ) );
348 sqlite3MemdebugSetType( p, MEMTYPE_HEAP );
349 iSize = sqlite3MallocSize( p.pData );
350 sqlite3_mutex_enter( pcache1.mutex );
351 sqlite3StatusAdd( SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize );
352 sqlite3_mutex_leave( pcache1.mutex );
353 sqlite3_free( ref p.pData );
354 }
355 }
356  
357 #if SQLITE_ENABLE_MEMORY_MANAGEMENT
358 /*
359 ** Return the size of a pcache allocation
360 */
361 static int pcache1MemSize(object p){
362 if( p>=pcache1.pStart && p<pcache1.pEnd ){
363 return pcache1.szSlot;
364 }else{
365 int iSize;
366 Debug.Assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
367 sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
368 iSize = sqlite3MallocSize(p);
369 sqlite3MemdebugSetType(p, MEMTYPE_PCACHE);
370 return iSize;
371 }
372 }
373 #endif //* SQLITE_ENABLE_MEMORY_MANAGEMENT */
374  
375 /*
376 ** Allocate a new page object initially associated with cache pCache.
377 */
378 static PgHdr1 pcache1AllocPage( PCache1 pCache )
379 {
380 //int nByte = sizeof( PgHdr1 ) + pCache.szPage;
381 PgHdr pPg = pcache1Alloc( pCache.szPage );//nByte );
382 PgHdr1 p = null;
383 //if ( pPg !=null)
384 {
385 //PAGE_TO_PGHDR1( pCache, pPg );
386 p = new PgHdr1();
387 p.pCache = pCache;
388 p.pPgHdr = pPg;
389 if ( pCache.bPurgeable )
390 {
391 pCache.pGroup.nCurrentPage++;
392 }
393 }
394 //else
395 //{
396 // p = 0;
397 //}
398 return p;
399 }
400  
401 /*
402 ** Free a page object allocated by pcache1AllocPage().
403 **
404 ** The pointer is allowed to be NULL, which is prudent. But it turns out
405 ** that the current implementation happens to never call this routine
406 ** with a NULL pointer, so we mark the NULL test with ALWAYS().
407 */
408 static void pcache1FreePage( ref PgHdr1 p )
409 {
410 if ( ALWAYS( p ) )
411 {
412 PCache1 pCache = p.pCache;
413 if ( pCache.bPurgeable )
414 {
415 pCache.pGroup.nCurrentPage--;
416 }
417 pcache1Free( ref p.pPgHdr );//PGHDR1_TO_PAGE( p );
418 }
419 }
420  
421 /*
422 ** Malloc function used by SQLite to obtain space from the buffer configured
423 ** using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no such buffer
424 ** exists, this function falls back to sqlite3Malloc().
425 */
426 static PgHdr sqlite3PageMalloc( int sz )
427 {
428 return pcache1Alloc( sz );
429 }
430  
431 /*
432 ** Free an allocated buffer obtained from sqlite3PageMalloc().
433 */
434 static void sqlite3PageFree( ref byte[] p )
435 {
436 if ( p != null )
437 {
438 sqlite3_free( ref p );
439 p = null;
440 }
441 }
442 static void sqlite3PageFree( ref PgHdr p )
443 {
444 pcache1Free( ref p );
445 }
446  
447 /*
448 ** Return true if it desirable to avoid allocating a new page cache
449 ** entry.
450 **
451 ** If memory was allocated specifically to the page cache using
452 ** SQLITE_CONFIG_PAGECACHE but that memory has all been used, then
453 ** it is desirable to avoid allocating a new page cache entry because
454 ** presumably SQLITE_CONFIG_PAGECACHE was suppose to be sufficient
455 ** for all page cache needs and we should not need to spill the
456 ** allocation onto the heap.
457 **
458 ** Or, the heap is used for all page cache memory put the heap is
459 ** under memory pressure, then again it is desirable to avoid
460 ** allocating a new page cache entry in order to avoid stressing
461 ** the heap even further.
462 */
463 static bool pcache1UnderMemoryPressure( PCache1 pCache )
464 {
465 if ( pcache1.nSlot != 0 && pCache.szPage <= pcache1.szSlot )
466 {
467 return pcache1.bUnderPressure;
468 }
469 else
470 {
471 return sqlite3HeapNearlyFull();
472 }
473 }
474  
475 /******************************************************************************/
476 /******** General Implementation Functions ************************************/
477  
478 /*
479 ** This function is used to resize the hash table used by the cache passed
480 ** as the first argument.
481 **
482 ** The PCache mutex must be held when this function is called.
483 */
484 static int pcache1ResizeHash( PCache1 p )
485 {
486 PgHdr1[] apNew;
487 int nNew;
488 int i;
489  
490 Debug.Assert( sqlite3_mutex_held( p.pGroup.mutex ) );
491  
492 nNew = p.nHash * 2;
493 if ( nNew < 256 )
494 {
495 nNew = 256;
496 }
497  
498 pcache1LeaveMutex( p.pGroup );
499 if ( p.nHash != 0 )
500 {
501 sqlite3BeginBenignMalloc();
502 }
503 apNew = new PgHdr1[nNew];//(PgHdr1 *)sqlite3_malloc(sizeof(PgHdr1 )*nNew);
504 if ( p.nHash != 0 )
505 {
506 sqlite3EndBenignMalloc();
507 }
508 pcache1EnterMutex( p.pGroup );
509 if ( apNew != null )
510 {
511 //memset(apNew, 0, sizeof(PgHdr1 )*nNew);
512 for ( i = 0; i < p.nHash; i++ )
513 {
514 PgHdr1 pPage;
515 PgHdr1 pNext = p.apHash[i];
516 while ( ( pPage = pNext ) != null )
517 {
518 Pgno h = (Pgno)( pPage.iKey % nNew );
519 pNext = pPage.pNext;
520 pPage.pNext = apNew[h];
521 apNew[h] = pPage;
522 }
523 }
524 //sqlite3_free( p.apHash );
525 p.apHash = apNew;
526 p.nHash = nNew;
527 }
528  
529 return ( p.apHash != null ? SQLITE_OK : SQLITE_NOMEM );
530 }
531  
532 /*
533 ** This function is used internally to remove the page pPage from the
534 ** PGroup LRU list, if is part of it. If pPage is not part of the PGroup
535 ** LRU list, then this function is a no-op.
536 **
537 ** The PGroup mutex must be held when this function is called.
538 **
539 ** If pPage is NULL then this routine is a no-op.
540 */
541 static void pcache1PinPage( PgHdr1 pPage )
542 {
543 PCache1 pCache;
544 PGroup pGroup;
545  
546 if ( pPage == null )
547 return;
548 pCache = pPage.pCache;
549 pGroup = pCache.pGroup;
550 Debug.Assert( sqlite3_mutex_held( pGroup.mutex ) );
551 if ( pPage.pLruNext != null || pPage == pGroup.pLruTail )
552 {
553 if ( pPage.pLruPrev != null )
554 {
555 pPage.pLruPrev.pLruNext = pPage.pLruNext;
556 }
557 if ( pPage.pLruNext != null )
558 {
559 pPage.pLruNext.pLruPrev = pPage.pLruPrev;
560 }
561 if ( pGroup.pLruHead == pPage )
562 {
563 pGroup.pLruHead = pPage.pLruNext;
564 }
565 if ( pGroup.pLruTail == pPage )
566 {
567 pGroup.pLruTail = pPage.pLruPrev;
568 }
569 pPage.pLruNext = null;
570 pPage.pLruPrev = null;
571 pPage.pCache.nRecyclable--;
572 }
573 }
574  
575  
576 /*
577 ** Remove the page supplied as an argument from the hash table
578 ** (PCache1.apHash structure) that it is currently stored in.
579 **
580 ** The PGroup mutex must be held when this function is called.
581 */
582 static void pcache1RemoveFromHash( PgHdr1 pPage )
583 {
584 int h;
585 PCache1 pCache = pPage.pCache;
586 PgHdr1 pp;
587 PgHdr1 pPrev = null;
588  
589 Debug.Assert( sqlite3_mutex_held( pCache.pGroup.mutex ) );
590 h = (int)( pPage.iKey % pCache.nHash );
591 for ( pp = pCache.apHash[h]; pp != pPage; pPrev = pp, pp = pp.pNext )
592 ;
593 if ( pPrev == null )
594 pCache.apHash[h] = pp.pNext;
595 else
596 pPrev.pNext = pp.pNext; // pCache.apHash[h] = pp.pNext;
597 pCache.nPage--;
598 }
599  
600 /*
601 ** If there are currently more than nMaxPage pages allocated, try
602 ** to recycle pages to reduce the number allocated to nMaxPage.
603 */
604 static void pcache1EnforceMaxPage( PGroup pGroup )
605 {
606 Debug.Assert( sqlite3_mutex_held( pGroup.mutex ) );
607 while ( pGroup.nCurrentPage > pGroup.nMaxPage && pGroup.pLruTail != null )
608 {
609 PgHdr1 p = pGroup.pLruTail;
610 Debug.Assert( p.pCache.pGroup == pGroup );
611 pcache1PinPage( p );
612 pcache1RemoveFromHash( p );
613 pcache1FreePage( ref p );
614 }
615 }
616  
617 /*
618 ** Discard all pages from cache pCache with a page number (key value)
619 ** greater than or equal to iLimit. Any pinned pages that meet this
620 ** criteria are unpinned before they are discarded.
621 **
622 ** The PCache mutex must be held when this function is called.
623 */
624 static void pcache1TruncateUnsafe(
625 PCache1 pCache, /* The cache to truncate */
626 uint iLimit /* Drop pages with this pgno or larger */
627 )
628 {
629 #if !NDEBUG || SQLITE_COVERAGE_TEST //TESTONLY( uint nPage = 0; ) /* To assert pCache.nPage is correct */
630 uint nPage = 0;
631 #endif
632 uint h;
633 Debug.Assert( sqlite3_mutex_held( pCache.pGroup.mutex ) );
634 for ( h = 0; h < pCache.nHash; h++ )
635 {
636 PgHdr1 pPrev = null;
637 PgHdr1 pp = pCache.apHash[h];
638 PgHdr1 pPage;
639 while ( ( pPage = pp ) != null )
640 {
641 if ( pPage.iKey >= iLimit )
642 {
643 pCache.nPage--;
644 pp = pPage.pNext;
645 pcache1PinPage( pPage );
646 if ( pCache.apHash[h] == pPage )
647 pCache.apHash[h] = pPage.pNext;
648 else
649 pPrev.pNext = pp;
650 pcache1FreePage( ref pPage );
651 }
652 else
653 {
654 pp = pPage.pNext;
655 #if !NDEBUG || SQLITE_COVERAGE_TEST //TESTONLY( nPage++; )
656 nPage++;
657 #endif
658 }
659 pPrev = pPage;
660 }
661 }
662 #if !NDEBUG || SQLITE_COVERAGE_TEST
663 Debug.Assert( pCache.nPage == nPage );
664 #endif
665 }
666  
667 /******************************************************************************/
668 /******** sqlite3_pcache Methods **********************************************/
669  
670 /*
671 ** Implementation of the sqlite3_pcache.xInit method.
672 */
673 static int pcache1Init<T>( T NotUsed )
674 {
675 UNUSED_PARAMETER( NotUsed );
676 Debug.Assert( pcache1.isInit == false );
677 pcache1 = new PCacheGlobal();//memset(&pcache1, 0, sizeof(pcache1));
678 if ( sqlite3GlobalConfig.bCoreMutex )
679 {
680 pcache1.grp.mutex = sqlite3_mutex_alloc( SQLITE_MUTEX_STATIC_LRU );
681 pcache1.mutex = sqlite3_mutex_alloc( SQLITE_MUTEX_STATIC_PMEM );
682 }
683 pcache1.grp.mxPinned = 10;
684 pcache1.isInit = true;
685 return SQLITE_OK;
686 }
687  
688 /*
689 ** Implementation of the sqlite3_pcache.xShutdown method.
690 ** Note that the static mutex allocated in xInit does
691 ** not need to be freed.
692 */
693 static void pcache1Shutdown<T>( T NotUsed )
694 {
695 UNUSED_PARAMETER( NotUsed );
696 Debug.Assert( pcache1.isInit );
697 pcache1 = new PCacheGlobal();//;memset( &pcache1, 0, sizeof( pcache1 ) );
698 }
699  
700 /*
701 ** Implementation of the sqlite3_pcache.xCreate method.
702 **
703 ** Allocate a new cache.
704 */
705 static sqlite3_pcache pcache1Create( int szPage, bool bPurgeable )
706 {
707 PCache1 pCache; /* The newly created page cache */
708 PGroup pGroup; /* The group the new page cache will belong to */
709 int sz; /* Bytes of memory required to allocate the new cache */
710  
711 /*
712 ** The seperateCache variable is true if each PCache has its own private
713 ** PGroup. In other words, separateCache is true for mode (1) where no
714 ** mutexing is required.
715 **
716 ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT
717 **
718 ** * Always use a unified cache in single-threaded applications
719 **
720 ** * Otherwise (if multi-threaded and ENABLE_MEMORY_MANAGEMENT is off)
721 ** use separate caches (mode-1)
722 */
723 #if (SQLITE_ENABLE_MEMORY_MANAGEMENT) || !SQLITE_THREADSAF
724 const int separateCache = 0;
725 #else
726 int separateCache = sqlite3GlobalConfig.bCoreMutex>0;
727 #endif
728  
729 //sz = sizeof( PCache1 ) + sizeof( PGroup ) * separateCache;
730 pCache = new PCache1();//(PCache1)sqlite3_malloc( sz );
731 //if ( pCache != null )
732 //{
733 //memset( pCache, 0, sz );
734 if ( separateCache == 0 )
735 {
736 pGroup = pcache1.grp;
737 }
738 ////else
739 ////{
740 ////pGroup = new PGroup();//(PGroup)pCache[1];
741 ////pGroup.mxPinned = 10;
742 ////}
743  
744 pCache.pGroup = pGroup;
745 pCache.szPage = szPage;
746 pCache.bPurgeable = bPurgeable;//( bPurgeable ? 1 : 0 );
747 if ( bPurgeable )
748 {
749 pCache.nMin = 10;
750 pcache1EnterMutex( pGroup );
751 pGroup.nMinPage += (int)pCache.nMin;
752 pGroup.mxPinned = pGroup.nMaxPage + 10 - pGroup.nMinPage;
753 pcache1LeaveMutex( pGroup );
754 }
755 //}
756 return (sqlite3_pcache)pCache;
757 }
758  
759 /*
760 ** Implementation of the sqlite3_pcache.xCachesize method.
761 **
762 ** Configure the cache_size limit for a cache.
763 */
764 static void pcache1Cachesize( sqlite3_pcache p, int nMax )
765 {
766 PCache1 pCache = (PCache1)p;
767 if ( pCache.bPurgeable )
768 {
769 PGroup pGroup = pCache.pGroup;
770 pcache1EnterMutex( pGroup );
771 pGroup.nMaxPage += nMax - pCache.nMax;
772 pGroup.mxPinned = pGroup.nMaxPage + 10 - pGroup.nMinPage;
773 pCache.nMax = nMax;
774 pCache.n90pct = pCache.nMax * 9 / 10;
775 pcache1EnforceMaxPage( pGroup );
776 pcache1LeaveMutex( pGroup );
777 }
778 }
779  
780 /*
781 ** Implementation of the sqlite3_pcache.xPagecount method.
782 */
783 static int pcache1Pagecount( sqlite3_pcache p )
784 {
785 int n;
786 PCache1 pCache = (PCache1)p;
787 pcache1EnterMutex( pCache.pGroup );
788 n = (int)pCache.nPage;
789 pcache1LeaveMutex( pCache.pGroup );
790 return n;
791 }
792  
793 /*
794 ** Implementation of the sqlite3_pcache.xFetch method.
795 **
796 ** Fetch a page by key value.
797 **
798 ** Whether or not a new page may be allocated by this function depends on
799 ** the value of the createFlag argument. 0 means do not allocate a new
800 ** page. 1 means allocate a new page if space is easily available. 2
801 ** means to try really hard to allocate a new page.
802 **
803 ** For a non-purgeable cache (a cache used as the storage for an in-memory
804 ** database) there is really no difference between createFlag 1 and 2. So
805 ** the calling function (pcache.c) will never have a createFlag of 1 on
806 ** a non-purgable cache.
807 **
808 ** There are three different approaches to obtaining space for a page,
809 ** depending on the value of parameter createFlag (which may be 0, 1 or 2).
810 **
811 ** 1. Regardless of the value of createFlag, the cache is searched for a
812 ** copy of the requested page. If one is found, it is returned.
813 **
814 ** 2. If createFlag==0 and the page is not already in the cache, NULL is
815 ** returned.
816 **
817 ** 3. If createFlag is 1, and the page is not already in the cache, then
818 ** return NULL (do not allocate a new page) if any of the following
819 ** conditions are true:
820 **
821 ** (a) the number of pages pinned by the cache is greater than
822 ** PCache1.nMax, or
823 **
824 ** (b) the number of pages pinned by the cache is greater than
825 ** the sum of nMax for all purgeable caches, less the sum of
826 ** nMin for all other purgeable caches, or
827 **
828 ** 4. If none of the first three conditions apply and the cache is marked
829 ** as purgeable, and if one of the following is true:
830 **
831 ** (a) The number of pages allocated for the cache is already
832 ** PCache1.nMax, or
833 **
834 ** (b) The number of pages allocated for all purgeable caches is
835 ** already equal to or greater than the sum of nMax for all
836 ** purgeable caches,
837 **
838 ** (c) The system is under memory pressure and wants to avoid
839 ** unnecessary pages cache entry allocations
840 **
841 ** then attempt to recycle a page from the LRU list. If it is the right
842 ** size, return the recycled buffer. Otherwise, free the buffer and
843 ** proceed to step 5.
844 **
845 ** 5. Otherwise, allocate and return a new page buffer.
846 */
847 static PgHdr pcache1Fetch( sqlite3_pcache p, Pgno iKey, int createFlag )
848 {
849 int nPinned;
850 PCache1 pCache = (PCache1)p;
851 PGroup pGroup;
852 PgHdr1 pPage = null;
853  
854 Debug.Assert( pCache.bPurgeable || createFlag != 1 );
855 Debug.Assert( pCache.bPurgeable || pCache.nMin == 0 );
856 Debug.Assert( pCache.bPurgeable == false || pCache.nMin == 10 );
857 Debug.Assert( pCache.nMin == 0 || pCache.bPurgeable );
858 pcache1EnterMutex( pGroup = pCache.pGroup );
859  
860 /* Step 1: Search the hash table for an existing entry. */
861 if ( pCache.nHash > 0 )
862 {
863 int h = (int)( iKey % pCache.nHash );
864 for ( pPage = pCache.apHash[h]; pPage != null && pPage.iKey != iKey; pPage = pPage.pNext )
865 ;
866 }
867  
868 /* Step 2: Abort if no existing page is found and createFlag is 0 */
869 if ( pPage != null || createFlag == 0 )
870 {
871 pcache1PinPage( pPage );
872 goto fetch_out;
873 }
874  
875 /* The pGroup local variable will normally be initialized by the
876 ** pcache1EnterMutex() macro above. But if SQLITE_MUTEX_OMIT is defined,
877 ** then pcache1EnterMutex() is a no-op, so we have to initialize the
878 ** local variable here. Delaying the initialization of pGroup is an
879 ** optimization: The common case is to exit the module before reaching
880 ** this point.
881 */
882 #if SQLITE_MUTEX_OMIT
883 pGroup = pCache.pGroup;
884 #endif
885  
886  
887 /* Step 3: Abort if createFlag is 1 but the cache is nearly full */
888 nPinned = pCache.nPage - pCache.nRecyclable;
889 Debug.Assert( nPinned >= 0 );
890 Debug.Assert( pGroup.mxPinned == pGroup.nMaxPage + 10 - pGroup.nMinPage );
891 Debug.Assert( pCache.n90pct == pCache.nMax * 9 / 10 );
892 if ( createFlag == 1 && (
893 nPinned >= pGroup.mxPinned
894 || nPinned >= (int)pCache.n90pct
895 || pcache1UnderMemoryPressure( pCache )
896 ) )
897 {
898 goto fetch_out;
899 }
900  
901 if ( pCache.nPage >= pCache.nHash && pcache1ResizeHash( pCache ) != 0 )
902 {
903 goto fetch_out;
904 }
905  
906 /* Step 4. Try to recycle a page. */
907 if ( pCache.bPurgeable && pGroup.pLruTail != null && (
908 ( pCache.nPage + 1 >= pCache.nMax )
909 || pGroup.nCurrentPage >= pGroup.nMaxPage
910 || pcache1UnderMemoryPressure( pCache )
911 ) )
912 {
913 PCache1 pOtherCache;
914 pPage = pGroup.pLruTail;
915 pcache1RemoveFromHash( pPage );
916 pcache1PinPage( pPage );
917 if ( ( pOtherCache = pPage.pCache ).szPage != pCache.szPage )
918 {
919 pcache1FreePage( ref pPage );
920 pPage = null;
921 }
922 else
923 {
924 pGroup.nCurrentPage -=
925 ( pOtherCache.bPurgeable ? 1 : 0 ) - ( pCache.bPurgeable ? 1 : 0 );
926 }
927 }
928  
929 /* Step 5. If a usable page buffer has still not been found,
930 ** attempt to allocate a new one.
931 */
932 if ( null == pPage )
933 {
934 if ( createFlag == 1 )
935 sqlite3BeginBenignMalloc();
936 pcache1LeaveMutex( pGroup );
937 pPage = pcache1AllocPage( pCache );
938 pcache1EnterMutex( pGroup );
939 if ( createFlag == 1 )
940 sqlite3EndBenignMalloc();
941 }
942  
943 if ( pPage != null )
944 {
945 int h = (int)( iKey % pCache.nHash );
946 pCache.nPage++;
947 pPage.iKey = iKey;
948 pPage.pNext = pCache.apHash[h];
949 pPage.pCache = pCache;
950 pPage.pLruPrev = null;
951 pPage.pLruNext = null;
952 PGHDR1_TO_PAGE( pPage ).Clear();// *(void **)(PGHDR1_TO_PAGE(pPage)) = 0;
953 pPage.pPgHdr.pPgHdr1 = pPage;
954 pCache.apHash[h] = pPage;
955 }
956  
957 fetch_out:
958 if ( pPage != null && iKey > pCache.iMaxKey )
959 {
960 pCache.iMaxKey = iKey;
961 }
962 pcache1LeaveMutex( pGroup );
963 return ( pPage != null ? PGHDR1_TO_PAGE( pPage ) : null );
964 }
965  
966  
967 /*
968 ** Implementation of the sqlite3_pcache.xUnpin method.
969 **
970 ** Mark a page as unpinned (eligible for asynchronous recycling).
971 */
972 static void pcache1Unpin( sqlite3_pcache p, PgHdr pPg, bool reuseUnlikely )
973 {
974 PCache1 pCache = (PCache1)p;
975 PgHdr1 pPage = PAGE_TO_PGHDR1( pCache, pPg );
976 PGroup pGroup = pCache.pGroup;
977  
978 Debug.Assert( pPage.pCache == pCache );
979 pcache1EnterMutex( pGroup );
980  
981 /* It is an error to call this function if the page is already
982 ** part of the PGroup LRU list.
983 */
984 Debug.Assert( pPage.pLruPrev == null && pPage.pLruNext == null );
985 Debug.Assert( pGroup.pLruHead != pPage && pGroup.pLruTail != pPage );
986  
987 if ( reuseUnlikely || pGroup.nCurrentPage > pGroup.nMaxPage )
988 {
989 pcache1RemoveFromHash( pPage );
990 pcache1FreePage( ref pPage );
991 }
992 else
993 {
994 /* Add the page to the PGroup LRU list. */
995 if ( pGroup.pLruHead != null )
996 {
997 pGroup.pLruHead.pLruPrev = pPage;
998 pPage.pLruNext = pGroup.pLruHead;
999 pGroup.pLruHead = pPage;
1000 }
1001 else
1002 {
1003 pGroup.pLruTail = pPage;
1004 pGroup.pLruHead = pPage;
1005 }
1006 pCache.nRecyclable++;
1007 }
1008  
1009 pcache1LeaveMutex( pCache.pGroup );
1010 }
1011  
1012 /*
1013 ** Implementation of the sqlite3_pcache.xRekey method.
1014 */
1015 static void pcache1Rekey(
1016 sqlite3_pcache p,
1017 PgHdr pPg,
1018 Pgno iOld,
1019 Pgno iNew
1020 )
1021 {
1022 PCache1 pCache = (PCache1)p;
1023 PgHdr1 pPage = PAGE_TO_PGHDR1( pCache, pPg );
1024 PgHdr1 pp;
1025 int h;
1026 Debug.Assert( pPage.iKey == iOld );
1027 Debug.Assert( pPage.pCache == pCache );
1028  
1029 pcache1EnterMutex( pCache.pGroup );
1030  
1031 h = (int)( iOld % pCache.nHash );
1032 pp = pCache.apHash[h];
1033 while ( ( pp ) != pPage )
1034 {
1035 pp = ( pp ).pNext;
1036 }
1037 if ( pp == pCache.apHash[h] )
1038 pCache.apHash[h] = pp.pNext;
1039 else
1040 pp.pNext = pPage.pNext;
1041  
1042 h = (int)( iNew % pCache.nHash );
1043 pPage.iKey = iNew;
1044 pPage.pNext = pCache.apHash[h];
1045 pCache.apHash[h] = pPage;
1046 if ( iNew > pCache.iMaxKey )
1047 {
1048 pCache.iMaxKey = iNew;
1049 }
1050  
1051 pcache1LeaveMutex( pCache.pGroup );
1052 }
1053  
1054 /*
1055 ** Implementation of the sqlite3_pcache.xTruncate method.
1056 **
1057 ** Discard all unpinned pages in the cache with a page number equal to
1058 ** or greater than parameter iLimit. Any pinned pages with a page number
1059 ** equal to or greater than iLimit are implicitly unpinned.
1060 */
1061 static void pcache1Truncate( sqlite3_pcache p, Pgno iLimit )
1062 {
1063 PCache1 pCache = (PCache1)p;
1064 pcache1EnterMutex( pCache.pGroup );
1065 if ( iLimit <= pCache.iMaxKey )
1066 {
1067 pcache1TruncateUnsafe( pCache, iLimit );
1068 pCache.iMaxKey = iLimit - 1;
1069 }
1070 pcache1LeaveMutex( pCache.pGroup );
1071 }
1072  
1073 /*
1074 ** Implementation of the sqlite3_pcache.xDestroy method.
1075 **
1076 ** Destroy a cache allocated using pcache1Create().
1077 */
1078 static void pcache1Destroy( ref sqlite3_pcache p )
1079 {
1080 PCache1 pCache = (PCache1)p;
1081 PGroup pGroup = pCache.pGroup;
1082 Debug.Assert( pCache.bPurgeable || ( pCache.nMax == 0 && pCache.nMin == 0 ) );
1083 pcache1EnterMutex( pGroup );
1084 pcache1TruncateUnsafe( pCache, 0 );
1085 pGroup.nMaxPage -= pCache.nMax;
1086 pGroup.nMinPage -= pCache.nMin;
1087 pGroup.mxPinned = pGroup.nMaxPage + 10 - pGroup.nMinPage;
1088 pcache1EnforceMaxPage( pGroup );
1089 pcache1LeaveMutex( pGroup );
1090 //sqlite3_free( pCache.apHash );
1091 //sqlite3_free( pCache );
1092 p = null;
1093 }
1094  
1095 /*
1096 ** This function is called during initialization (sqlite3_initialize()) to
1097 ** install the default pluggable cache module, assuming the user has not
1098 ** already provided an alternative.
1099 */
1100 static void sqlite3PCacheSetDefault()
1101 {
1102 sqlite3_pcache_methods defaultMethods = new sqlite3_pcache_methods(
1103 0, /* pArg */
1104 (dxPC_Init)pcache1Init, /* xInit */
1105 (dxPC_Shutdown)pcache1Shutdown, /* xShutdown */
1106 (dxPC_Create)pcache1Create, /* xCreate */
1107 (dxPC_Cachesize)pcache1Cachesize, /* xCachesize */
1108 (dxPC_Pagecount)pcache1Pagecount, /* xPagecount */
1109 (dxPC_Fetch)pcache1Fetch, /* xFetch */
1110 (dxPC_Unpin)pcache1Unpin, /* xUnpin */
1111 (dxPC_Rekey)pcache1Rekey, /* xRekey */
1112 (dxPC_Truncate)pcache1Truncate, /* xTruncate */
1113 (dxPC_Destroy)pcache1Destroy /* xDestroy */
1114 );
1115 sqlite3_config( SQLITE_CONFIG_PCACHE, defaultMethods );
1116 }
1117  
1118 #if SQLITE_ENABLE_MEMORY_MANAGEMENT
1119 /*
1120 ** This function is called to free superfluous dynamically allocated memory
1121 ** held by the pager system. Memory in use by any SQLite pager allocated
1122 ** by the current thread may be sqlite3_free()ed.
1123 **
1124 ** nReq is the number of bytes of memory required. Once this much has
1125 ** been released, the function returns. The return value is the total number
1126 ** of bytes of memory released.
1127 */
1128 int sqlite3PcacheReleaseMemory(int nReq){
1129 int nFree = 0;
1130 Debug.Assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
1131 Debug.Assert( sqlite3_mutex_notheld(pcache1.mutex) );
1132 if( pcache1.pStart==0 ){
1133 PgHdr1 p;
1134 pcache1EnterMutex(&pcache1.grp);
1135 while( (nReq<0 || nFree<nReq) && ((p=pcache1.grp.pLruTail)!=0) ){
1136 nFree += pcache1MemSize(PGHDR1_TO_PAGE(p));
1137 PCache1pinPage(p);
1138 pcache1RemoveFromHash(p);
1139 pcache1FreePage(p);
1140 }
1141 pcache1LeaveMutex(&pcache1.grp);
1142 }
1143 return nFree;
1144 }
1145 #endif //* SQLITE_ENABLE_MEMORY_MANAGEMENT */
1146  
1147 #if SQLITE_TEST
1148 /*
1149 ** This function is used by test procedures to inspect the internal state
1150 ** of the global cache.
1151 */
1152 static void sqlite3PcacheStats(
1153 out int pnCurrent, /* OUT: Total number of pages cached */
1154 out int pnMax, /* OUT: Global maximum cache size */
1155 out int pnMin, /* OUT: Sum of PCache1.nMin for purgeable caches */
1156 out int pnRecyclable /* OUT: Total number of pages available for recycling */
1157 )
1158 {
1159 PgHdr1 p;
1160 int nRecyclable = 0;
1161 for ( p = pcache1.grp.pLruHead; p != null; p = p.pLruNext )
1162 {
1163 nRecyclable++;
1164 }
1165 pnCurrent = pcache1.grp.nCurrentPage;
1166 pnMax = pcache1.grp.nMaxPage;
1167 pnMin = pcache1.grp.nMinPage;
1168 pnRecyclable = nRecyclable;
1169 }
1170 #endif
1171 }
1172 }