xref: /sqlite-3.40.0/src/test_pcache.c (revision 81ef0f97)
1b232c232Sdrh /*
2b232c232Sdrh ** 2008 November 18
3b232c232Sdrh **
4b232c232Sdrh ** The author disclaims copyright to this source code.  In place of
5b232c232Sdrh ** a legal notice, here is a blessing:
6b232c232Sdrh **
7b232c232Sdrh **    May you do good and not evil.
8b232c232Sdrh **    May you find forgiveness for yourself and forgive others.
9b232c232Sdrh **    May you share freely, never taking more than you give.
10b232c232Sdrh **
11b232c232Sdrh *************************************************************************
12b232c232Sdrh **
13b232c232Sdrh ** This file contains code used for testing the SQLite system.
14b232c232Sdrh ** None of the code in this file goes into a deliverable build.
15b232c232Sdrh **
16b232c232Sdrh ** This file contains an application-defined pager cache
17b232c232Sdrh ** implementation that can be plugged in in place of the
18b232c232Sdrh ** default pcache.  This alternative pager cache will throw
19b232c232Sdrh ** some errors that the default cache does not.
20b232c232Sdrh **
21b232c232Sdrh ** This pagecache implementation is designed for simplicity
22b232c232Sdrh ** not speed.
23b232c232Sdrh */
24b232c232Sdrh #include "sqlite3.h"
25b232c232Sdrh #include <string.h>
26b232c232Sdrh #include <assert.h>
27b232c232Sdrh 
28b232c232Sdrh /*
29b232c232Sdrh ** Global data used by this test implementation.  There is no
30b232c232Sdrh ** mutexing, which means this page cache will not work in a
31b232c232Sdrh ** multi-threaded test.
32b232c232Sdrh */
33b232c232Sdrh typedef struct testpcacheGlobalType testpcacheGlobalType;
34b232c232Sdrh struct testpcacheGlobalType {
35b232c232Sdrh   void *pDummy;             /* Dummy allocation to simulate failures */
36b232c232Sdrh   int nInstance;            /* Number of current instances */
37f2a84e3cSdrh   unsigned discardChance;   /* Chance of discarding on an unpin (0-100) */
38b232c232Sdrh   unsigned prngSeed;        /* Seed for the PRNG */
39f2a84e3cSdrh   unsigned highStress;      /* Call xStress agressively */
40b232c232Sdrh };
41b232c232Sdrh static testpcacheGlobalType testpcacheGlobal;
42b232c232Sdrh 
43b232c232Sdrh /*
44b232c232Sdrh ** Initializer.
45b232c232Sdrh **
46b232c232Sdrh ** Verify that the initializer is only called when the system is
47b232c232Sdrh ** uninitialized.  Allocate some memory and report SQLITE_NOMEM if
48b232c232Sdrh ** the allocation fails.  This provides a means to test the recovery
49b232c232Sdrh ** from a failed initialization attempt.  It also verifies that the
50b232c232Sdrh ** the destructor always gets call - otherwise there would be a
51b232c232Sdrh ** memory leak.
52b232c232Sdrh */
testpcacheInit(void * pArg)53b232c232Sdrh static int testpcacheInit(void *pArg){
54b232c232Sdrh   assert( pArg==(void*)&testpcacheGlobal );
55b232c232Sdrh   assert( testpcacheGlobal.pDummy==0 );
56b232c232Sdrh   assert( testpcacheGlobal.nInstance==0 );
57b232c232Sdrh   testpcacheGlobal.pDummy = sqlite3_malloc(10);
58b232c232Sdrh   return testpcacheGlobal.pDummy==0 ? SQLITE_NOMEM : SQLITE_OK;
59b232c232Sdrh }
60b232c232Sdrh 
61b232c232Sdrh /*
62b232c232Sdrh ** Destructor
63b232c232Sdrh **
64b232c232Sdrh ** Verify that this is only called after initialization.
65b232c232Sdrh ** Free the memory allocated by the initializer.
66b232c232Sdrh */
testpcacheShutdown(void * pArg)67b232c232Sdrh static void testpcacheShutdown(void *pArg){
68b232c232Sdrh   assert( pArg==(void*)&testpcacheGlobal );
69b232c232Sdrh   assert( testpcacheGlobal.pDummy!=0 );
70b232c232Sdrh   assert( testpcacheGlobal.nInstance==0 );
71b232c232Sdrh   sqlite3_free( testpcacheGlobal.pDummy );
72b232c232Sdrh   testpcacheGlobal.pDummy = 0;
73b232c232Sdrh }
74b232c232Sdrh 
75b232c232Sdrh /*
761a0cc28eSdrh ** Number of pages in a cache.
771a0cc28eSdrh **
781a0cc28eSdrh ** The number of pages is a hard upper bound in this test module.
791a0cc28eSdrh ** If more pages are requested, sqlite3PcacheFetch() returns NULL.
801a0cc28eSdrh **
811a0cc28eSdrh ** If testing with in-memory temp tables, provide a larger pcache.
821a0cc28eSdrh ** Some of the test cases need this.
83b232c232Sdrh */
841a0cc28eSdrh #if defined(SQLITE_TEMP_STORE) && SQLITE_TEMP_STORE>=2
851a0cc28eSdrh # define TESTPCACHE_NPAGE    499
861a0cc28eSdrh #else
87b232c232Sdrh # define TESTPCACHE_NPAGE    217
881a0cc28eSdrh #endif
89b232c232Sdrh #define TESTPCACHE_RESERVE   17
90b232c232Sdrh 
91b232c232Sdrh /*
92b232c232Sdrh ** Magic numbers used to determine validity of the page cache.
93b232c232Sdrh */
94b232c232Sdrh #define TESTPCACHE_VALID  0x364585fd
95b232c232Sdrh #define TESTPCACHE_CLEAR  0xd42670d4
96b232c232Sdrh 
97b232c232Sdrh /*
98b232c232Sdrh ** Private implementation of a page cache.
99b232c232Sdrh */
100b232c232Sdrh typedef struct testpcache testpcache;
101b232c232Sdrh struct testpcache {
102b232c232Sdrh   int szPage;               /* Size of each page.  Multiple of 8. */
10322e21ff4Sdan   int szExtra;              /* Size of extra data that accompanies each page */
104b232c232Sdrh   int bPurgeable;           /* True if the page cache is purgeable */
105b232c232Sdrh   int nFree;                /* Number of unused slots in a[] */
106b232c232Sdrh   int nPinned;              /* Number of pinned slots in a[] */
107b232c232Sdrh   unsigned iRand;           /* State of the PRNG */
108b232c232Sdrh   unsigned iMagic;          /* Magic number for sanity checking */
109b232c232Sdrh   struct testpcachePage {
11022e21ff4Sdan     sqlite3_pcache_page page;  /* Base class */
111b232c232Sdrh     unsigned key;              /* The key for this page. 0 means unallocated */
112b232c232Sdrh     int isPinned;              /* True if the page is pinned */
113b232c232Sdrh   } a[TESTPCACHE_NPAGE];    /* All pages in the cache */
114b232c232Sdrh };
115b232c232Sdrh 
116b232c232Sdrh /*
117b232c232Sdrh ** Get a random number using the PRNG in the given page cache.
118b232c232Sdrh */
testpcacheRandom(testpcache * p)119b232c232Sdrh static unsigned testpcacheRandom(testpcache *p){
120b232c232Sdrh   unsigned x = 0;
121b232c232Sdrh   int i;
122b232c232Sdrh   for(i=0; i<4; i++){
123b232c232Sdrh     p->iRand = (p->iRand*69069 + 5);
124b232c232Sdrh     x = (x<<8) | ((p->iRand>>16)&0xff);
125b232c232Sdrh   }
126b232c232Sdrh   return x;
127b232c232Sdrh }
128b232c232Sdrh 
129b232c232Sdrh 
130b232c232Sdrh /*
131b232c232Sdrh ** Allocate a new page cache instance.
132b232c232Sdrh */
testpcacheCreate(int szPage,int szExtra,int bPurgeable)13322e21ff4Sdan static sqlite3_pcache *testpcacheCreate(
13422e21ff4Sdan   int szPage,
135e5c40b18Sdrh   int szExtra,
13622e21ff4Sdan   int bPurgeable
13722e21ff4Sdan ){
138b232c232Sdrh   int nMem;
139b232c232Sdrh   char *x;
140b232c232Sdrh   testpcache *p;
141b232c232Sdrh   int i;
142b232c232Sdrh   assert( testpcacheGlobal.pDummy!=0 );
143b232c232Sdrh   szPage = (szPage+7)&~7;
14422e21ff4Sdan   nMem = sizeof(testpcache) + TESTPCACHE_NPAGE*(szPage+szExtra);
145b232c232Sdrh   p = sqlite3_malloc( nMem );
146b232c232Sdrh   if( p==0 ) return 0;
147b232c232Sdrh   x = (char*)&p[1];
148b232c232Sdrh   p->szPage = szPage;
14922e21ff4Sdan   p->szExtra = szExtra;
150b232c232Sdrh   p->nFree = TESTPCACHE_NPAGE;
151b232c232Sdrh   p->nPinned = 0;
152b232c232Sdrh   p->iRand = testpcacheGlobal.prngSeed;
153b232c232Sdrh   p->bPurgeable = bPurgeable;
154b232c232Sdrh   p->iMagic = TESTPCACHE_VALID;
15522e21ff4Sdan   for(i=0; i<TESTPCACHE_NPAGE; i++, x += (szPage+szExtra)){
156b232c232Sdrh     p->a[i].key = 0;
157b232c232Sdrh     p->a[i].isPinned = 0;
15822e21ff4Sdan     p->a[i].page.pBuf = (void*)x;
15922e21ff4Sdan     p->a[i].page.pExtra = (void*)&x[szPage];
160b232c232Sdrh   }
161b232c232Sdrh   testpcacheGlobal.nInstance++;
162b232c232Sdrh   return (sqlite3_pcache*)p;
163b232c232Sdrh }
164b232c232Sdrh 
165b232c232Sdrh /*
166b232c232Sdrh ** Set the cache size
167b232c232Sdrh */
testpcacheCachesize(sqlite3_pcache * pCache,int newSize)168b232c232Sdrh static void testpcacheCachesize(sqlite3_pcache *pCache, int newSize){
169b232c232Sdrh   testpcache *p = (testpcache*)pCache;
170b232c232Sdrh   assert( p->iMagic==TESTPCACHE_VALID );
171b232c232Sdrh   assert( testpcacheGlobal.pDummy!=0 );
172b232c232Sdrh   assert( testpcacheGlobal.nInstance>0 );
173b232c232Sdrh }
174b232c232Sdrh 
175b232c232Sdrh /*
176b232c232Sdrh ** Return the number of pages in the cache that are being used.
177b232c232Sdrh ** This includes both pinned and unpinned pages.
178b232c232Sdrh */
testpcachePagecount(sqlite3_pcache * pCache)179b232c232Sdrh static int testpcachePagecount(sqlite3_pcache *pCache){
180b232c232Sdrh   testpcache *p = (testpcache*)pCache;
181b232c232Sdrh   assert( p->iMagic==TESTPCACHE_VALID );
182b232c232Sdrh   assert( testpcacheGlobal.pDummy!=0 );
183b232c232Sdrh   assert( testpcacheGlobal.nInstance>0 );
184b232c232Sdrh   return TESTPCACHE_NPAGE - p->nFree;
185b232c232Sdrh }
186b232c232Sdrh 
187b232c232Sdrh /*
188b232c232Sdrh ** Fetch a page.
189b232c232Sdrh */
testpcacheFetch(sqlite3_pcache * pCache,unsigned key,int createFlag)190b232c232Sdrh static sqlite3_pcache_page *testpcacheFetch(
19122e21ff4Sdan   sqlite3_pcache *pCache,
192b232c232Sdrh   unsigned key,
193b232c232Sdrh   int createFlag
194b232c232Sdrh ){
195b232c232Sdrh   testpcache *p = (testpcache*)pCache;
196b232c232Sdrh   int i, j;
197b232c232Sdrh   assert( p->iMagic==TESTPCACHE_VALID );
198b232c232Sdrh   assert( testpcacheGlobal.pDummy!=0 );
199b232c232Sdrh   assert( testpcacheGlobal.nInstance>0 );
200b232c232Sdrh 
201b232c232Sdrh   /* See if the page is already in cache.  Return immediately if it is */
202b232c232Sdrh   for(i=0; i<TESTPCACHE_NPAGE; i++){
203b232c232Sdrh     if( p->a[i].key==key ){
204b232c232Sdrh       if( !p->a[i].isPinned ){
205b232c232Sdrh         p->nPinned++;
206b232c232Sdrh         assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree );
207b232c232Sdrh         p->a[i].isPinned = 1;
208b232c232Sdrh       }
209b232c232Sdrh       return &p->a[i].page;
21022e21ff4Sdan     }
211b232c232Sdrh   }
212b232c232Sdrh 
213b232c232Sdrh   /* If createFlag is 0, never allocate a new page */
214b232c232Sdrh   if( createFlag==0 ){
215b232c232Sdrh     return 0;
216b232c232Sdrh   }
217b232c232Sdrh 
218b232c232Sdrh   /* If no pages are available, always fail */
219b232c232Sdrh   if( p->nPinned==TESTPCACHE_NPAGE ){
220b232c232Sdrh     return 0;
221b232c232Sdrh   }
222b232c232Sdrh 
223b232c232Sdrh   /* Do not allocate the last TESTPCACHE_RESERVE pages unless createFlag is 2 */
224b232c232Sdrh   if( p->nPinned>=TESTPCACHE_NPAGE-TESTPCACHE_RESERVE && createFlag<2 ){
225b232c232Sdrh     return 0;
226b232c232Sdrh   }
227b232c232Sdrh 
228b232c232Sdrh   /* Do not allocate if highStress is enabled and createFlag is not 2.
229f2a84e3cSdrh   **
230f2a84e3cSdrh   ** The highStress setting causes pagerStress() to be called much more
231f2a84e3cSdrh   ** often, which exercises the pager logic more intensely.
232f2a84e3cSdrh   */
233f2a84e3cSdrh   if( testpcacheGlobal.highStress && createFlag<2 ){
234f2a84e3cSdrh     return 0;
235f2a84e3cSdrh   }
236f2a84e3cSdrh 
237f2a84e3cSdrh   /* Find a free page to allocate if there are any free pages.
238b232c232Sdrh   ** Withhold TESTPCACHE_RESERVE free pages until createFlag is 2.
239b232c232Sdrh   */
240b232c232Sdrh   if( p->nFree>TESTPCACHE_RESERVE || (createFlag==2 && p->nFree>0) ){
241b232c232Sdrh     j = testpcacheRandom(p) % TESTPCACHE_NPAGE;
242b232c232Sdrh     for(i=0; i<TESTPCACHE_NPAGE; i++, j = (j+1)%TESTPCACHE_NPAGE){
243b232c232Sdrh       if( p->a[j].key==0 ){
244b232c232Sdrh         p->a[j].key = key;
245b232c232Sdrh         p->a[j].isPinned = 1;
246b232c232Sdrh         memset(p->a[j].page.pBuf, 0, p->szPage);
24722e21ff4Sdan         memset(p->a[j].page.pExtra, 0, p->szExtra);
24822e21ff4Sdan         p->nPinned++;
249b232c232Sdrh         p->nFree--;
250b232c232Sdrh         assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree );
251b232c232Sdrh         return &p->a[j].page;
25222e21ff4Sdan       }
253b232c232Sdrh     }
254b232c232Sdrh 
255b232c232Sdrh     /* The prior loop always finds a freepage to allocate */
256b232c232Sdrh     assert( 0 );
257b232c232Sdrh   }
258b232c232Sdrh 
259b232c232Sdrh   /* If this cache is not purgeable then we have to fail.
260b232c232Sdrh   */
261b232c232Sdrh   if( p->bPurgeable==0 ){
262b232c232Sdrh     return 0;
263b232c232Sdrh   }
264b232c232Sdrh 
265b232c232Sdrh   /* If there are no free pages, recycle a page.  The page to
266b232c232Sdrh   ** recycle is selected at random from all unpinned pages.
267b232c232Sdrh   */
268b232c232Sdrh   j = testpcacheRandom(p) % TESTPCACHE_NPAGE;
269b232c232Sdrh   for(i=0; i<TESTPCACHE_NPAGE; i++, j = (j+1)%TESTPCACHE_NPAGE){
270b232c232Sdrh     if( p->a[j].key>0 && p->a[j].isPinned==0 ){
271b232c232Sdrh       p->a[j].key = key;
272b232c232Sdrh       p->a[j].isPinned = 1;
273b232c232Sdrh       memset(p->a[j].page.pBuf, 0, p->szPage);
27422e21ff4Sdan       memset(p->a[j].page.pExtra, 0, p->szExtra);
27522e21ff4Sdan       p->nPinned++;
276b232c232Sdrh       assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree );
277b232c232Sdrh       return &p->a[j].page;
27822e21ff4Sdan     }
279b232c232Sdrh   }
280b232c232Sdrh 
281b232c232Sdrh   /* The previous loop always finds a page to recycle. */
282b232c232Sdrh   assert(0);
283b232c232Sdrh   return 0;
284b232c232Sdrh }
285b232c232Sdrh 
286b232c232Sdrh /*
287b232c232Sdrh ** Unpin a page.
288b232c232Sdrh */
testpcacheUnpin(sqlite3_pcache * pCache,sqlite3_pcache_page * pOldPage,int discard)289b232c232Sdrh static void testpcacheUnpin(
290b232c232Sdrh   sqlite3_pcache *pCache,
291b232c232Sdrh   sqlite3_pcache_page *pOldPage,
29222e21ff4Sdan   int discard
293b232c232Sdrh ){
294b232c232Sdrh   testpcache *p = (testpcache*)pCache;
295b232c232Sdrh   int i;
296b232c232Sdrh   assert( p->iMagic==TESTPCACHE_VALID );
297b232c232Sdrh   assert( testpcacheGlobal.pDummy!=0 );
298b232c232Sdrh   assert( testpcacheGlobal.nInstance>0 );
299b232c232Sdrh 
300b232c232Sdrh   /* Randomly discard pages as they are unpinned according to the
301b232c232Sdrh   ** discardChance setting.  If discardChance is 0, the random discard
302b232c232Sdrh   ** never happens.  If discardChance is 100, it always happens.
303b232c232Sdrh   */
304b232c232Sdrh   if( p->bPurgeable
305b232c232Sdrh   && (100-testpcacheGlobal.discardChance) <= (testpcacheRandom(p)%100)
306b232c232Sdrh   ){
307b232c232Sdrh     discard = 1;
308b232c232Sdrh   }
309b232c232Sdrh 
310b232c232Sdrh   for(i=0; i<TESTPCACHE_NPAGE; i++){
311b232c232Sdrh     if( &p->a[i].page==pOldPage ){
31222e21ff4Sdan       /* The pOldPage pointer always points to a pinned page */
313b232c232Sdrh       assert( p->a[i].isPinned );
314b232c232Sdrh       p->a[i].isPinned = 0;
315b232c232Sdrh       p->nPinned--;
316b232c232Sdrh       assert( p->nPinned>=0 );
317b232c232Sdrh       if( discard ){
318b232c232Sdrh         p->a[i].key = 0;
319b232c232Sdrh         p->nFree++;
320b232c232Sdrh         assert( p->nFree<=TESTPCACHE_NPAGE );
321b232c232Sdrh       }
322b232c232Sdrh       return;
323b232c232Sdrh     }
324b232c232Sdrh   }
325b232c232Sdrh 
326b232c232Sdrh   /* The pOldPage pointer always points to a valid page */
327b232c232Sdrh   assert( 0 );
328b232c232Sdrh }
329b232c232Sdrh 
330b232c232Sdrh 
331b232c232Sdrh /*
332b232c232Sdrh ** Rekey a single page.
333b232c232Sdrh */
testpcacheRekey(sqlite3_pcache * pCache,sqlite3_pcache_page * pOldPage,unsigned oldKey,unsigned newKey)334b232c232Sdrh static void testpcacheRekey(
335b232c232Sdrh   sqlite3_pcache *pCache,
336b232c232Sdrh   sqlite3_pcache_page *pOldPage,
33722e21ff4Sdan   unsigned oldKey,
338b232c232Sdrh   unsigned newKey
339b232c232Sdrh ){
340b232c232Sdrh   testpcache *p = (testpcache*)pCache;
341b232c232Sdrh   int i;
342b232c232Sdrh   assert( p->iMagic==TESTPCACHE_VALID );
343b232c232Sdrh   assert( testpcacheGlobal.pDummy!=0 );
344b232c232Sdrh   assert( testpcacheGlobal.nInstance>0 );
345b232c232Sdrh 
346b232c232Sdrh   /* If there already exists another page at newKey, verify that
347b232c232Sdrh   ** the other page is unpinned and discard it.
348b232c232Sdrh   */
349b232c232Sdrh   for(i=0; i<TESTPCACHE_NPAGE; i++){
350b232c232Sdrh     if( p->a[i].key==newKey ){
351b232c232Sdrh       /* The new key is never a page that is already pinned */
352b232c232Sdrh       assert( p->a[i].isPinned==0 );
353b232c232Sdrh       p->a[i].key = 0;
354b232c232Sdrh       p->nFree++;
355b232c232Sdrh       assert( p->nFree<=TESTPCACHE_NPAGE );
356b232c232Sdrh       break;
357b232c232Sdrh     }
358b232c232Sdrh   }
359b232c232Sdrh 
360b232c232Sdrh   /* Find the page to be rekeyed and rekey it.
361b232c232Sdrh   */
362b232c232Sdrh   for(i=0; i<TESTPCACHE_NPAGE; i++){
363b232c232Sdrh     if( p->a[i].key==oldKey ){
364b232c232Sdrh       /* The oldKey and pOldPage parameters match */
365b232c232Sdrh       assert( &p->a[i].page==pOldPage );
36622e21ff4Sdan       /* Page to be rekeyed must be pinned */
367b232c232Sdrh       assert( p->a[i].isPinned );
368b232c232Sdrh       p->a[i].key = newKey;
369b232c232Sdrh       return;
370b232c232Sdrh     }
371b232c232Sdrh   }
372b232c232Sdrh 
373b232c232Sdrh   /* Rekey is always given a valid page to work with */
374b232c232Sdrh   assert( 0 );
375b232c232Sdrh }
376b232c232Sdrh 
377b232c232Sdrh 
378b232c232Sdrh /*
379b232c232Sdrh ** Truncate the page cache.  Every page with a key of iLimit or larger
380b232c232Sdrh ** is discarded.
381b232c232Sdrh */
testpcacheTruncate(sqlite3_pcache * pCache,unsigned iLimit)382b232c232Sdrh static void testpcacheTruncate(sqlite3_pcache *pCache, unsigned iLimit){
383b232c232Sdrh   testpcache *p = (testpcache*)pCache;
384b232c232Sdrh   unsigned int i;
385b232c232Sdrh   assert( p->iMagic==TESTPCACHE_VALID );
386b232c232Sdrh   assert( testpcacheGlobal.pDummy!=0 );
387b232c232Sdrh   assert( testpcacheGlobal.nInstance>0 );
388b232c232Sdrh   for(i=0; i<TESTPCACHE_NPAGE; i++){
389b232c232Sdrh     if( p->a[i].key>=iLimit ){
390b232c232Sdrh       p->a[i].key = 0;
391b232c232Sdrh       if( p->a[i].isPinned ){
392b232c232Sdrh         p->nPinned--;
393b232c232Sdrh         assert( p->nPinned>=0 );
394b232c232Sdrh       }
395b232c232Sdrh       p->nFree++;
396b232c232Sdrh       assert( p->nFree<=TESTPCACHE_NPAGE );
397b232c232Sdrh     }
398b232c232Sdrh   }
399b232c232Sdrh }
400b232c232Sdrh 
401b232c232Sdrh /*
402b232c232Sdrh ** Destroy a page cache.
403b232c232Sdrh */
testpcacheDestroy(sqlite3_pcache * pCache)404b232c232Sdrh static void testpcacheDestroy(sqlite3_pcache *pCache){
405b232c232Sdrh   testpcache *p = (testpcache*)pCache;
406b232c232Sdrh   assert( p->iMagic==TESTPCACHE_VALID );
407b232c232Sdrh   assert( testpcacheGlobal.pDummy!=0 );
408b232c232Sdrh   assert( testpcacheGlobal.nInstance>0 );
409b232c232Sdrh   p->iMagic = TESTPCACHE_CLEAR;
410b232c232Sdrh   sqlite3_free(p);
411b232c232Sdrh   testpcacheGlobal.nInstance--;
412b232c232Sdrh }
413b232c232Sdrh 
414b232c232Sdrh 
415b232c232Sdrh /*
416b232c232Sdrh ** Invoke this routine to register or unregister the testing pager cache
417b232c232Sdrh ** implemented by this file.
418b232c232Sdrh **
419b232c232Sdrh ** Install the test pager cache if installFlag is 1 and uninstall it if
420b232c232Sdrh ** installFlag is 0.
421b232c232Sdrh **
422b232c232Sdrh ** When installing, discardChance is a number between 0 and 100 that
423b232c232Sdrh ** indicates the probability of discarding a page when unpinning the
424b232c232Sdrh ** page.  0 means never discard (unless the discard flag is set).
425b232c232Sdrh ** 100 means always discard.
426b232c232Sdrh */
installTestPCache(int installFlag,unsigned discardChance,unsigned prngSeed,unsigned highStress)427b232c232Sdrh void installTestPCache(
428b232c232Sdrh   int installFlag,            /* True to install.  False to uninstall. */
429b232c232Sdrh   unsigned discardChance,     /* 0-100.  Chance to discard on unpin */
430b232c232Sdrh   unsigned prngSeed,          /* Seed for the PRNG */
431f2a84e3cSdrh   unsigned highStress         /* Call xStress agressively */
432f2a84e3cSdrh ){
433b232c232Sdrh   static const sqlite3_pcache_methods2 testPcache = {
43422e21ff4Sdan     1,
435*81ef0f97Sdrh     (void*)&testpcacheGlobal,
436b232c232Sdrh     testpcacheInit,
437b232c232Sdrh     testpcacheShutdown,
438b232c232Sdrh     testpcacheCreate,
439b232c232Sdrh     testpcacheCachesize,
440b232c232Sdrh     testpcachePagecount,
441b232c232Sdrh     testpcacheFetch,
442b232c232Sdrh     testpcacheUnpin,
443b232c232Sdrh     testpcacheRekey,
444b232c232Sdrh     testpcacheTruncate,
445b232c232Sdrh     testpcacheDestroy,
446b232c232Sdrh   };
447b232c232Sdrh   static sqlite3_pcache_methods2 defaultPcache;
44822e21ff4Sdan   static int isInstalled = 0;
449b232c232Sdrh 
450b232c232Sdrh   assert( testpcacheGlobal.nInstance==0 );
451b232c232Sdrh   assert( testpcacheGlobal.pDummy==0 );
452b232c232Sdrh   assert( discardChance<=100 );
453b232c232Sdrh   testpcacheGlobal.discardChance = discardChance;
454b232c232Sdrh   testpcacheGlobal.prngSeed = prngSeed ^ (prngSeed<<16);
455b232c232Sdrh   testpcacheGlobal.highStress = highStress;
456f2a84e3cSdrh   if( installFlag!=isInstalled ){
457b232c232Sdrh     if( installFlag ){
458b232c232Sdrh       sqlite3_config(SQLITE_CONFIG_GETPCACHE2, &defaultPcache);
45922e21ff4Sdan       assert( defaultPcache.xCreate!=testpcacheCreate );
460b232c232Sdrh       sqlite3_config(SQLITE_CONFIG_PCACHE2, &testPcache);
46122e21ff4Sdan     }else{
462b232c232Sdrh       assert( defaultPcache.xCreate!=0 );
463b232c232Sdrh       sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultPcache);
46422e21ff4Sdan     }
465b232c232Sdrh     isInstalled = installFlag;
466b232c232Sdrh   }
467b232c232Sdrh }
468b232c232Sdrh