test_pcache.c 13 KB

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