Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: third_party/sqlite/src/test_pcache.c

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

Powered by Google App Engine
This is Rietveld 408576698