wasCSharpSQLite – Blame information for rev 1
?pathlinks?
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 | } |