]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Simplify the pcache module by only recycling clean pages from 'other' caches. This...
authordanielk1977 <danielk1977@noemail.net>
Tue, 26 Aug 2008 18:05:48 +0000 (18:05 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Tue, 26 Aug 2008 18:05:48 +0000 (18:05 +0000)
FossilOrigin-Name: 9e511e161bcb077450d31fca5dd20c2557f103b3

manifest
manifest.uuid
src/pager.c
src/pcache.c
src/pcache.h
src/test2.c
test/memsubsys1.test
test/tkt2409.test

index 4092f7726c34334e7cac7d94fa4ef6bf1abbfb65..0f9a356385bb0992788dcdf77d3d1917a7ed9ef9 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Implement\sa\s"counter"\sSQL\sfunction\sthat\scan\sbe\sused\sto\sinsert\sa\ssequence\nnumber\seach\srow\sof\sa\sresult\sset.\s\sCurrently\sin\sthe\stest\sharness\sonly,\sbut\na\scandidate\sto\smove\sinto\sthe\score.\s(CVS\s5614)
-D 2008-08-26T14:42:15
+C Simplify\sthe\spcache\smodule\sby\sonly\srecycling\sclean\spages\sfrom\s'other'\scaches.\sThis\scommit\scauses\serrors\sin\stest\sfiles\sioerr5.test\sand\smalloc5.test\sbecause\sthey\stest\srecycling\sdirty\spages\sfrom\sother\scaches.\s(CVS\s5615)
+D 2008-08-26T18:05:48
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in 689e14735f862a5553bceef206d8c13e29504e44
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -135,11 +135,11 @@ F src/os_common.h 24525d8b7bce66c374dfc1810a6c9043f3359b60
 F src/os_os2.c e391fc95adc744bbdcefd4d11e3066998185a0a0
 F src/os_unix.c 4665cef7639dd937893c3ea076f0e8a8f215bb32
 F src/os_win.c aefe9ee26430678a19a058a874e4e2bd91398142
-F src/pager.c 67d689efa33fbca2788d006e18dd7d1a7de22487
+F src/pager.c 9dfaca6cd443228326b1837fbb712b9cbde9a0e0
 F src/pager.h 3b9c138d2e744b9d6e61d4c2742301e3bf464864
 F src/parse.y d0f76d2cb8d6883d5600dc20beb961a6022b94b8
-F src/pcache.c 4883f3714503242057643a5dddbc74065def5157
-F src/pcache.h f03fc3b8241da092bd929ba0eec15e84d9d2cca0
+F src/pcache.c 03c2ad23aa6f9cf008a6fe4ee68a07b3948d331f
+F src/pcache.h 3531f83e1771442af16f6ffeac68024ff8c8bb2d
 F src/pragma.c f5b271b090af7fcedd308d7c5807a5503f7a853d
 F src/prepare.c c197041e0c4770672cda75e6bfe10242f885e510
 F src/printf.c 785f87120589c1db672e37c6eb1087c456e6f84d
@@ -155,7 +155,7 @@ F src/status.c 8caa772cd9310bc297280f7cf0ede4d69ed5b801
 F src/table.c 22744786199c9195720c15a7a42cb97b2e2728d8
 F src/tclsqlite.c ec46084184f033ba396a9ee7b5514b695083d0f3
 F src/test1.c f92039530f6a6253ec8d3bfeaa54205a0036bbb6
-F src/test2.c 9601907ac0bab60f2f81695c3a6e9249621ae741
+F src/test2.c eaa77124786649eedf47d3c5e94d8070c0da228f
 F src/test3.c e85b7ce5c28c3ce7fbdbf7f98e1467b19786c62b
 F src/test4.c 41056378671e7b00e6305fa9ac6fa27e6f96f406
 F src/test5.c 162a1cea2105a2c460a3f39fa6919617b562a288
@@ -421,7 +421,7 @@ F test/malloc_common.tcl e082fe4791dad22b49d2ad3f7dcf1dcbee1a4cec
 F test/manydb.test 8de36b8d33aab5ef295b11d9e95310aeded31af8
 F test/memdb.test a67bda4ff90a38f2b19f6c7f95aa7289e051d893
 F test/memleak.test d2d2a1ff7105d32dc3fdf691458cf6cba58c7217
-F test/memsubsys1.test bd578712272a3c327873b388396e351586d29e61
+F test/memsubsys1.test c12d1e4ad7f0ec7a45b56b5c49fe7397fa8d6d8f
 F test/memsubsys2.test 72a731225997ad5e8df89fdbeae9224616b6aecc
 F test/minmax.test 722d80816f7e096bf2c04f4111f1a6c1ba65453d
 F test/minmax2.test 33504c01a03bd99226144e4b03f7631a274d66e0
@@ -531,7 +531,7 @@ F test/tkt2285.test cca17be61cf600b397188e77e7143844d2b977e9
 F test/tkt2332.test fc955609b958ca86dfa102832243370a0cc84070
 F test/tkt2339.test 73bd17818924cd2ac442e5fd9916b58565739450
 F test/tkt2391.test ab7a11be7402da8b51a5be603425367aa0684567
-F test/tkt2409.test 695269f90bbd30285fb1dd1499e8cc0d827a647d
+F test/tkt2409.test c9599f296178eb764a7d6260c5403e1e921d9fef
 F test/tkt2450.test 77ed94863f2049c1420288ddfea2d41e5e0971d6
 F test/tkt2640.test 28134f5d1e05658ef182520cf0b680fa3de5211b
 F test/tkt2643.test 3f3ebb743da00d4fed4fcf6daed92a0e18e57813
@@ -624,7 +624,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 1dbced29de5f59ba2ebf877edcadf171540374d1
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
-P 4995a1d1c9530be9ce647d338169620cd95a72eb
-R 53057febb14c48765135eac34d009c5b
-U drh
-Z 0de0cfc7bacf48ec9c7d1199a1a02018
+P c84d46c71233bbf869513f433b1d18cbd7f2a35e
+R 9db6a9b878af9d031aaa0c602a425d48
+U danielk1977
+Z e27423cf6787772e11bb472965b58787
index 7893e42edc51d06752c1dcc09aff4676b1027fa9..039b9423c3c7e29278037e6bd8848042223068e7 100644 (file)
@@ -1 +1 @@
-c84d46c71233bbf869513f433b1d18cbd7f2a35e
\ No newline at end of file
+9e511e161bcb077450d31fca5dd20c2557f103b3
\ No newline at end of file
index cd3f5742641481b358d93113b61ecc864c8f1dd2..69c3ee7c36b1b31fe9a88188596cb8b4b370805e 100644 (file)
@@ -18,7 +18,7 @@
 ** file simultaneously, or one process from reading the database while
 ** another is writing.
 **
-** @(#) $Id: pager.c,v 1.479 2008/08/25 17:23:29 drh Exp $
+** @(#) $Id: pager.c,v 1.480 2008/08/26 18:05:48 danielk1977 Exp $
 */
 #ifndef SQLITE_OMIT_DISKIO
 #include "sqliteInt.h"
@@ -302,18 +302,6 @@ static const unsigned char aJournalMagic[] = {
 */
 #define PAGER_MAX_PGNO 2147483647
 
-/*
-** The following two macros act as a type of recursive mutex. Their
-** only purpose is to provide mutual exclusion between the "normal"
-** users of a pager object (the btree.c module) and the user of the
-** pagerStress() function (the pcache.c module). While the mutex 
-** obtained using pagerEnter() is held, the pcache module guarantees 
-** that the pagerStress() callback will not be invoked from a thread
-** other than the holder of the mutex.
-*/
-#define pagerEnter(p) (sqlite3PcacheLock(p->pPCache))
-#define pagerLeave(p) (sqlite3PcacheUnlock(p->pPCache))
-
 /*
 ** Return true if page *pPg has already been written to the statement
 ** journal (or statement snapshot has been created, if *pPg is part
@@ -1956,7 +1944,6 @@ int sqlite3PagerSetPagesize(Pager *pPager, u16 *pPageSize){
   int rc = SQLITE_OK;
   u16 pageSize = *pPageSize;
   assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) );
-  pagerEnter(pPager);
   if( pageSize && pageSize!=pPager->pageSize 
    && !pPager->memDb && sqlite3PcacheRefCount(pPager->pPCache)==0 
   ){
@@ -1973,7 +1960,6 @@ int sqlite3PagerSetPagesize(Pager *pPager, u16 *pPageSize){
     }
   }
   *pPageSize = pPager->pageSize;
-  pagerLeave(pPager);
   return rc;
 }
 
@@ -2042,7 +2028,6 @@ int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){
   int rc = SQLITE_OK;
   memset(pDest, 0, N);
   assert(MEMDB||pPager->fd->pMethods||pPager->tempFile);
-  pagerEnter(pPager);
   if( pPager->fd->pMethods ){
     IOTRACE(("DBHDR %p 0 %d\n", pPager, N))
     rc = sqlite3OsRead(pPager->fd, pDest, N, 0);
@@ -2050,7 +2035,6 @@ int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){
       rc = SQLITE_OK;
     }
   }
-  pagerLeave(pPager);
   return rc;
 }
 
@@ -2067,10 +2051,8 @@ int sqlite3PagerPagecount(Pager *pPager, int *pnPage){
   i64 n = 0;
   int rc;
   assert( pPager!=0 );
-  pagerEnter(pPager);
   if( pPager->errCode ){
     rc = pPager->errCode;
-    pagerLeave(pPager);
     return rc;
   }
   if( pPager->dbSize>=0 ){
@@ -2080,7 +2062,6 @@ int sqlite3PagerPagecount(Pager *pPager, int *pnPage){
     if( (pPager->fd->pMethods)
      && (rc = sqlite3OsFileSize(pPager->fd, &n))!=SQLITE_OK ){
       pager_error(pPager, rc);
-      pagerLeave(pPager);
       return rc;
     }
     if( n>0 && n<pPager->pageSize ){
@@ -2101,7 +2082,6 @@ int sqlite3PagerPagecount(Pager *pPager, int *pnPage){
   if( pnPage ){
     *pnPage = n;
   }
-  pagerLeave(pPager);
   return SQLITE_OK;
 }
 
@@ -2167,7 +2147,6 @@ int sqlite3PagerTruncate(Pager *pPager, Pgno nPage){
   int rc = SQLITE_OK;
   assert( pPager->state>=PAGER_SHARED || MEMDB );
 
-  pagerEnter(pPager);
 
   sqlite3PagerPagecount(pPager, 0);
   if( pPager->errCode ){
@@ -2188,7 +2167,6 @@ int sqlite3PagerTruncate(Pager *pPager, Pgno nPage){
     }
   }
 
-  pagerLeave(pPager);
   return rc;
 }
 
@@ -2207,7 +2185,6 @@ int sqlite3PagerTruncate(Pager *pPager, Pgno nPage){
 ** to the caller.
 */
 int sqlite3PagerClose(Pager *pPager){
-  pagerEnter(pPager);
 
   disable_simulated_io_errors();
   sqlite3BeginBenignMalloc();
@@ -2253,9 +2230,7 @@ Pgno sqlite3PagerPagenumber(DbPage *p){
 ** a reference to the page data.
 */
 int sqlite3PagerRef(DbPage *pPg){
-  pagerEnter(pPg->pPager);
   sqlite3PcacheRef(pPg);
-  pagerLeave(pPg->pPager);
   return SQLITE_OK;
 }
 
@@ -2916,9 +2891,7 @@ int sqlite3PagerAcquire(
   int noContent       /* Do not bother reading content from disk if true */
 ){
   int rc;
-  pagerEnter(pPager);
   rc = pagerAcquire(pPager, pgno, ppPage, noContent);
-  pagerLeave(pPager);
   return rc;
 }
 
@@ -2939,13 +2912,11 @@ DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
   assert( pPager!=0 );
   assert( pgno!=0 );
 
-  pagerEnter(pPager);
   if( (pPager->state!=PAGER_UNLOCK)
    && (pPager->errCode==SQLITE_OK || pPager->errCode==SQLITE_FULL)
   ){
     sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg);
   }
-  pagerLeave(pPager);
 
   return pPg;
 }
@@ -2961,10 +2932,8 @@ DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
 int sqlite3PagerUnref(DbPage *pPg){
   if( pPg ){
     Pager *pPager = pPg->pPager;
-    pagerEnter(pPager);
     sqlite3PcacheRelease(pPg);
     pagerUnlockIfUnused(pPager);
-    pagerLeave(pPager);
   }
   return SQLITE_OK;
 }
@@ -2986,9 +2955,7 @@ static int pager_open_journal(Pager *pPager){
   assert( pPager->useJournal );
   assert( pPager->pInJournal==0 );
   sqlite3PagerPagecount(pPager, 0);
-  pagerLeave(pPager);
   pPager->pInJournal = sqlite3BitvecCreate(pPager->dbSize);
-  pagerEnter(pPager);
   if( pPager->pInJournal==0 ){
     rc = SQLITE_NOMEM;
     goto failed_to_open_journal;
@@ -3078,7 +3045,6 @@ failed_to_open_journal:
 int sqlite3PagerBegin(DbPage *pPg, int exFlag){
   Pager *pPager = pPg->pPager;
   int rc = SQLITE_OK;
-  pagerEnter(pPager);
   assert( pPg->nRef>0 );
   assert( pPager->state!=PAGER_UNLOCK );
   if( pPager->state==PAGER_SHARED ){
@@ -3096,7 +3062,6 @@ int sqlite3PagerBegin(DbPage *pPg, int exFlag){
         }
       }
       if( rc!=SQLITE_OK ){
-        pagerLeave(pPager);
         return rc;
       }
       pPager->dirtyCache = 0;
@@ -3126,7 +3091,6 @@ int sqlite3PagerBegin(DbPage *pPg, int exFlag){
     }
   }
   assert( !pPager->journalOpen || pPager->journalOff>0 || rc!=SQLITE_OK );
-  pagerLeave(pPager);
   return rc;
 }
 
@@ -3362,7 +3326,6 @@ int sqlite3PagerWrite(DbPage *pDbPage){
   Pager *pPager = pPg->pPager;
   Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
 
-  pagerEnter(pPager);
   if( !MEMDB && nPagePerSector>1 ){
     Pgno nPageCount;          /* Total number of pages in database file */
     Pgno pg1;                 /* First page of the sector pPg is located on. */
@@ -3436,7 +3399,6 @@ int sqlite3PagerWrite(DbPage *pDbPage){
   }else{
     rc = pager_write(pDbPage);
   }
-  pagerLeave(pPager);
   return rc;
 }
 
@@ -3480,7 +3442,6 @@ void sqlite3PagerDontWrite(DbPage *pDbPage){
   Pager *pPager = pPg->pPager;
 
   if( MEMDB ) return;
-  pagerEnter(pPager);
   pPg->flags |= PGHDR_ALWAYS_ROLLBACK;
   if( (pPg->flags&PGHDR_DIRTY) && !pPager->stmtInUse ){
     assert( pPager->state>=PAGER_SHARED );
@@ -3502,7 +3463,6 @@ void sqlite3PagerDontWrite(DbPage *pDbPage){
 #endif
     }
   }
-  pagerLeave(pPager);
 }
 
 /*
@@ -3519,7 +3479,6 @@ void sqlite3PagerDontWrite(DbPage *pDbPage){
 void sqlite3PagerDontRollback(DbPage *pPg){
   Pager *pPager = pPg->pPager;
 
-  pagerEnter(pPager);
   assert( pPager->state>=PAGER_RESERVED );
 
   /* If the journal file is not open, or DontWrite() has been called on
@@ -3529,14 +3488,12 @@ void sqlite3PagerDontRollback(DbPage *pPg){
   if( pPager->journalOpen==0 || (pPg->flags&PGHDR_ALWAYS_ROLLBACK) 
    || pPager->alwaysRollback 
   ){
-    pagerLeave(pPager);
     return;
   }
   assert( !MEMDB );    /* For a memdb, pPager->journalOpen is always 0 */
 
 #ifdef SQLITE_SECURE_DELETE
   if( pPg->inJournal || (int)pPg->pgno > pPager->origDbSize ){
-    pagerLeave(pPager);
     return;
   }
 #endif
@@ -3563,7 +3520,6 @@ void sqlite3PagerDontRollback(DbPage *pPg){
   }
   PAGERTRACE3("DONT_ROLLBACK page %d of %d\n", pPg->pgno, PAGERID(pPager));
   IOTRACE(("GARBAGE %p %d\n", pPager, pPg->pgno))
-  pagerLeave(pPager);
 }
 
 
@@ -3616,9 +3572,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirect){
 */
 int sqlite3PagerSync(Pager *pPager){
   int rc;
-  pagerEnter(pPager);
   rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
-  pagerLeave(pPager);
   return rc;
 }
 
@@ -3667,7 +3621,6 @@ int sqlite3PagerCommitPhaseOne(
 
   PAGERTRACE4("DATABASE SYNC: File=%s zMaster=%s nTrunc=%d\n", 
       pPager->zFilename, zMaster, nTrunc);
-  pagerEnter(pPager);
 
   /* If this is an in-memory db, or no pages have been written to, or this
   ** function has already been called, it is a no-op.
@@ -3797,7 +3750,6 @@ sync_exit:
      */
     rc = SQLITE_BUSY;
   }
-  pagerLeave(pPager);
   return rc;
 }
 
@@ -3824,7 +3776,6 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){
     assert( pPager->dirtyCache==0 || pPager->journalOpen==0 );
     return SQLITE_OK;
   }
-  pagerEnter(pPager);
   PAGERTRACE2("COMMIT %d\n", PAGERID(pPager));
   if( MEMDB ){
     sqlite3PcacheCommit(pPager->pPCache, 0);
@@ -3838,7 +3789,6 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){
     rc = pager_end_transaction(pPager, pPager->setMaster);
     rc = pager_error(pPager, rc);
   }
-  pagerLeave(pPager);
   return rc;
 }
 
@@ -3857,7 +3807,6 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){
 int sqlite3PagerRollback(Pager *pPager){
   int rc = SQLITE_OK;
   PAGERTRACE2("ROLLBACK %d\n", PAGERID(pPager));
-  pagerEnter(pPager);
   if( MEMDB ){
     sqlite3PcacheRollback(pPager->pPCache, 1);
     sqlite3PcacheRollback(pPager->pPCache, 0);
@@ -3896,7 +3845,6 @@ int sqlite3PagerRollback(Pager *pPager){
     */
     rc = pager_error(pPager, rc);
   }
-  pagerLeave(pPager);
   return rc;
 }
 
@@ -3921,7 +3869,6 @@ int sqlite3PagerRefcount(Pager *pPager){
 */
 int *sqlite3PagerStats(Pager *pPager){
   static int a[11];
-  pagerEnter(pPager);
   a[0] = sqlite3PcacheRefCount(pPager->pPCache);
   a[1] = sqlite3PcachePagecount(pPager->pPCache);
   a[2] = sqlite3PcacheGetCachesize(pPager->pPCache);
@@ -3933,7 +3880,6 @@ int *sqlite3PagerStats(Pager *pPager){
   a[8] = 0;  /* Used to be pPager->nOvfl */
   a[9] = pPager->nRead;
   a[10] = pPager->nWrite;
-  pagerLeave(pPager);
   return a;
 }
 int sqlite3PagerIsMemdb(Pager *pPager){
@@ -3994,9 +3940,7 @@ stmt_begin_failed:
 }
 int sqlite3PagerStmtBegin(Pager *pPager){
   int rc;
-  pagerEnter(pPager);
   rc = pagerStmtBegin(pPager);
-  pagerLeave(pPager);
   return rc;
 }
 
@@ -4004,7 +3948,6 @@ int sqlite3PagerStmtBegin(Pager *pPager){
 ** Commit a statement.
 */
 int sqlite3PagerStmtCommit(Pager *pPager){
-  pagerEnter(pPager);
   if( pPager->stmtInUse ){
     PAGERTRACE2("STMT-COMMIT %d\n", PAGERID(pPager));
     if( !MEMDB ){
@@ -4018,7 +3961,6 @@ int sqlite3PagerStmtCommit(Pager *pPager){
     pPager->stmtInUse = 0;
   }
   pPager->stmtAutoopen = 0;
-  pagerLeave(pPager);
   return SQLITE_OK;
 }
 
@@ -4027,7 +3969,6 @@ int sqlite3PagerStmtCommit(Pager *pPager){
 */
 int sqlite3PagerStmtRollback(Pager *pPager){
   int rc;
-  pagerEnter(pPager);
   if( pPager->stmtInUse ){
     PAGERTRACE2("STMT-ROLLBACK %d\n", PAGERID(pPager));
     if( MEMDB ){
@@ -4043,7 +3984,6 @@ int sqlite3PagerStmtRollback(Pager *pPager){
     rc = SQLITE_OK;
   }
   pPager->stmtAutoopen = 0;
-  pagerLeave(pPager);
   return rc;
 }
 
@@ -4139,8 +4079,6 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
       PAGERID(pPager), pPg->pgno, (pPg->flags&PGHDR_NEED_SYNC)?1:0, pgno);
   IOTRACE(("MOVE %p %d %d\n", pPager, pPg->pgno, pgno))
 
-  pagerEnter(pPager);
-
   pager_get_content(pPg);
 
   /* If the journal needs to be sync()ed before page pPg->pgno can
@@ -4208,7 +4146,6 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
       if( pPager->pInJournal && (int)needSyncPgno<=pPager->origDbSize ){
         sqlite3BitvecClear(pPager->pInJournal, needSyncPgno);
       }
-      pagerLeave(pPager);
       return rc;
     }
     pPager->needSync = 1;
@@ -4218,7 +4155,6 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
     sqlite3PagerUnref(pPgHdr);
   }
 
-  pagerLeave(pPager);
   return SQLITE_OK;
 }
 #endif
index fd0f6ee20c013d9e761cd4309bee18034178b788..09ab28722b440b7aaff98131832095863c28bf4d 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file implements that page cache.
 **
-** @(#) $Id: pcache.c,v 1.13 2008/08/25 14:49:42 danielk1977 Exp $
+** @(#) $Id: pcache.c,v 1.14 2008/08/26 18:05:48 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
 ** A complete page cache is an instance of this structure.
 */
 struct PCache {
-  PCache *pNextAll, *pPrevAll;        /* List of all page caches */
   int szPage;                         /* Size of every page in this cache */
   int szExtra;                        /* Size of extra space for each page */
   int nHash;                          /* Number of slots in apHash[] */
   int nPage;                          /* Total number of pages in apHash */
   int nMax;                           /* Configured cache size */
+  int nMin;                           /* Configured minimum cache size */
   PgHdr **apHash;                     /* Hash table for fast lookup by pgno */
   int bPurgeable;                     /* True if pages are on backing store */
   void (*xDestroy)(PgHdr*);           /* Called when refcnt goes 1->0 */
   int (*xStress)(void*,PgHdr*);       /* Call to try make a page clean */
   void *pStress;                      /* Argument to xStress */
   PgHdr *pClean;                      /* List of clean pages in use */
-  PgHdr *pDirty;                      /* List of dirty pages */
-  int nRef;                           /* Number of outstanding page refs */
-
-  int iInUseMM;
-  int iInUseDB;
+  PgHdr *pDirty, *pDirtyTail;         /* List of dirty pages in LRU order */
+  PgHdr *pSynced;                     /* Last synced page in dirty page list */
+  int nRef;                           /* Number of pinned pages */
+  int nPinned;                        /* Number of pinned and/or dirty pages */
 };
 
 /*
@@ -59,23 +58,18 @@ struct PgFreeslot {
 ** pages held by purgable caches to mxPagePurgeable.
 **
 ** The doubly-linked list that runs between pcache.pLruHead and 
-** pcache.pLruTail contains all pages in the system with a zero 
-** reference count. The pcache.pLruSynced variable points to the last
-** (closest to pcache.pLruTail) entry in this list that does not have
-** the PGHDR_NEED_SYNC flag set. This is the page that the pcacheRecycle()
-** function will try to recycle.
+** pcache.pLruTail contains all clean purgable pages in the system 
+** with a zero reference count. pcache.pLruTail is the next page to
+** be recycled.
 */
 static struct PCacheGlobal {
   int isInit;                         /* True when initialized */
-  sqlite3_mutex *mutex_mem2;          /* static mutex MUTEX_STATIC_MEM2 */
-  sqlite3_mutex *mutex_lru;           /* static mutex MUTEX_STATIC_LRU */
-  PCache *pAll;                       /* list of all page caches */
-  int nPage;                          /* Number of pages */
-  int nPurgeable;                     /* Number of pages in purgable caches */
-  int mxPage;                         /* Globally configured page maximum */
-  int mxPagePurgeable;                /* Purgeable page maximum */
-  PgHdr *pLruHead, *pLruTail;         /* Global LRU list of unused pages */
-  PgHdr *pLruSynced;                  /* Last synced entry in LRU list  */
+  sqlite3_mutex *mutex;               /* static mutex MUTEX_STATIC_LRU */
+
+  int nMaxPage;                       /* Sum of nMaxPage for purgeable caches */
+  int nMinPage;                       /* Sum of nMinPage for purgeable caches */
+  int nCurrentPage;                   /* Number of purgeable pages allocated */
+  PgHdr *pLruHead, *pLruTail;         /* LRU list of unused clean pgs */
 
   /* Variables related to SQLITE_CONFIG_PAGECACHE settings. */
   int szSlot;                         /* Size of each free slot */
@@ -85,31 +79,17 @@ static struct PCacheGlobal {
 
 /*
 ** All global variables used by this module (most of which are grouped 
-** together in global structure "pcache" above) except the list of all
-** pager-caches starting with pcache.pAll, are protected by the static 
+** together in global structure "pcache" above) are protected by the static 
 ** SQLITE_MUTEX_STATIC_LRU mutex. A pointer to this mutex is stored in
-** variable "pcache.mutex_lru".
+** variable "pcache.mutex".
 **
 ** Access to the contents of the individual PCache structures is not 
 ** protected. It is the job of the caller to ensure that these structures
-** are accessed in a thread-safe manner. However, this module provides the
-** functions sqlite3PcacheLock() and sqlite3PcacheUnlock() that may be used
-** by the caller to increment/decrement a lock-count on an individual 
-** pager-cache object. This module guarantees that the xStress() callback
-** will not be invoked on a pager-cache with a non-zero lock-count except
-** from within a call to sqlite3PcacheFetch() on the same pager. A call
-** to sqlite3PcacheLock() may block if such an xStress() call is currently 
-** underway.
-**
-** Before the xStress callback of a pager-cache (PCache) is invoked, the
-** SQLITE_MUTEX_STATIC_MEM2 mutex is obtained.
-**
-** Deadlock within the module is avoided by never blocking on the MEM2 
-** mutex while the LRU mutex is held.
+** are accessed in a thread-safe manner.
 */
 
-#define pcacheEnterGlobal() sqlite3_mutex_enter(pcache.mutex_lru)
-#define pcacheExitGlobal()  sqlite3_mutex_leave(pcache.mutex_lru)
+#define pcacheEnterGlobal() sqlite3_mutex_enter(pcache.mutex)
+#define pcacheExitGlobal()  sqlite3_mutex_leave(pcache.mutex)
 
 /********************************** Linked List Management ********************/
 
@@ -131,6 +111,40 @@ static int pcacheCheckHashCount(PCache *pCache){
   assert( nPage==pCache->nPage );
   return 1;
 }
+
+/*
+** Based on the current value of PCache.nRef and the contents of the
+** PCache.pDirty list, return the expected value of the PCache.nPinned
+** counter. This is only used in debugging builds, as follows:
+**
+**   assert( pCache->nPinned==pcachePinnedCount(pCache) );
+*/
+static int pcachePinnedCount(PCache *pCache){
+  PgHdr *p;
+  int nPinned = pCache->nRef;
+  for(p=pCache->pDirty; p; p=p->pNext){
+    if( p->nRef==0 ){
+      nPinned++;
+    }
+  }
+  return nPinned;
+}
+
+/*
+** Check that the pCache->pSynced variable is set correctly. If it
+** is not, either fail an assert or return zero. Otherwise, return
+** non-zero. This is only used in debugging builds, as follows:
+**
+**   assert( pcacheCheckSynced(pCache) );
+*/
+static int pcacheCheckSynced(PCache *pCache){
+  PgHdr *p = pCache->pDirtyTail;
+  for(p=pCache->pDirtyTail; p!=pCache->pSynced; p=p->pPrev){
+    assert( p->nRef || (p->flags&PGHDR_NEED_SYNC) );
+  }
+  return (p==0 || p->nRef || (p->flags&PGHDR_NEED_SYNC)==0);
+}
+
 #endif
 
 /*
@@ -205,6 +219,9 @@ static int pcacheResizeHash(PCache *pCache, int nHash){
 ** *ppHead is either PCache.pClean or PCache.pDirty.
 */
 static void pcacheRemoveFromList(PgHdr **ppHead, PgHdr *pPage){
+  int isDirtyList = (ppHead==&pPage->pCache->pDirty);
+  assert( ppHead==&pPage->pCache->pClean || ppHead==&pPage->pCache->pDirty );
+
   if( pPage->pPrev ){
     pPage->pPrev->pNext = pPage->pNext;
   }else{
@@ -214,6 +231,21 @@ static void pcacheRemoveFromList(PgHdr **ppHead, PgHdr *pPage){
   if( pPage->pNext ){
     pPage->pNext->pPrev = pPage->pPrev;
   }
+
+  if( isDirtyList ){
+    PCache *pCache = pPage->pCache;
+    assert( pPage->pNext || pCache->pDirtyTail==pPage );
+    if( !pPage->pNext ){
+      pCache->pDirtyTail = pPage->pPrev;
+    }
+    if( pCache->pSynced==pPage ){
+      PgHdr *pSynced = pPage->pPrev;
+      while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){
+        pSynced = pSynced->pPrev;
+      }
+      pCache->pSynced = pSynced;
+    }
+  }
 }
 
 /*
@@ -221,32 +253,44 @@ static void pcacheRemoveFromList(PgHdr **ppHead, PgHdr *pPage){
 ** *ppHead is either PCache.pClean or PCache.pDirty.
 */
 static void pcacheAddToList(PgHdr **ppHead, PgHdr *pPage){
+  int isDirtyList = (ppHead==&pPage->pCache->pDirty);
+  assert( ppHead==&pPage->pCache->pClean || ppHead==&pPage->pCache->pDirty );
+
   if( (*ppHead) ){
     (*ppHead)->pPrev = pPage;
   }
   pPage->pNext = *ppHead;
   pPage->pPrev = 0;
   *ppHead = pPage;
+
+  if( isDirtyList ){
+    PCache *pCache = pPage->pCache;
+    if( !pCache->pDirtyTail ){
+      assert( pPage->pNext==0 );
+      pCache->pDirtyTail = pPage;
+    }
+    if( !pCache->pSynced && 0==(pPage->flags&PGHDR_NEED_SYNC) ){
+      pCache->pSynced = pPage;
+    }
+  }
 }
 
 /*
 ** Remove a page from the global LRU list
 */
 static void pcacheRemoveFromLruList(PgHdr *pPage){
-  assert( sqlite3_mutex_held(pcache.mutex_lru) );
+  assert( sqlite3_mutex_held(pcache.mutex) );
+  assert( (pPage->flags&PGHDR_DIRTY)==0 );
   if( pPage->pCache->bPurgeable==0 ) return;
-  if( pPage==pcache.pLruSynced ){
-    PgHdr *p;
-    for(p=pPage->pPrevLru; p && (p->flags&PGHDR_NEED_SYNC); p=p->pPrevLru);
-    pcache.pLruSynced = p;
-  }
   if( pPage->pNextLru ){
+    assert( pcache.pLruTail!=pPage );
     pPage->pNextLru->pPrevLru = pPage->pPrevLru;
   }else{
     assert( pcache.pLruTail==pPage );
     pcache.pLruTail = pPage->pPrevLru;
   }
   if( pPage->pPrevLru ){
+    assert( pcache.pLruHead!=pPage );
     pPage->pPrevLru->pNextLru = pPage->pNextLru;
   }else{
     assert( pcache.pLruHead==pPage );
@@ -261,7 +305,8 @@ static void pcacheRemoveFromLruList(PgHdr *pPage){
 ** to the end of the LRU list so that it will be the next to be recycled.
 */
 static void pcacheAddToLruList(PgHdr *pPage){
-  assert( sqlite3_mutex_held(pcache.mutex_lru) );
+  assert( sqlite3_mutex_held(pcache.mutex) );
+  assert( (pPage->flags&PGHDR_DIRTY)==0 );
   if( pPage->pCache->bPurgeable==0 ) return;
   if( pcache.pLruTail && (pPage->flags & PGHDR_REUSE_UNLIKELY)!=0 ){
     /* If reuse is unlikely.  Put the page at the end of the LRU list
@@ -273,9 +318,6 @@ static void pcacheAddToLruList(PgHdr *pPage){
     pcache.pLruTail->pNextLru = pPage;
     pcache.pLruTail = pPage;
     pPage->flags &= ~PGHDR_REUSE_UNLIKELY;
-    if( 0==(pPage->flags&PGHDR_NEED_SYNC) ){
-      pcache.pLruSynced = pPage;
-    }
   }else{
     /* If reuse is possible. the page goes at the beginning of the LRU
     ** list so that it will be the last to be recycled.
@@ -289,9 +331,6 @@ static void pcacheAddToLruList(PgHdr *pPage){
     if( pcache.pLruTail==0 ){
       pcache.pLruTail = pPage;
     }
-    if( pcache.pLruSynced==0 && 0==(pPage->flags&PGHDR_NEED_SYNC) ){
-      pcache.pLruSynced = pPage;
-    }
   }
 }
 
@@ -323,7 +362,7 @@ void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
 ** in the page cache memory pool, go to the general purpose memory allocator.
 */
 void *pcacheMalloc(int sz, PCache *pCache){
-  assert( sqlite3_mutex_held(pcache.mutex_lru) );
+  assert( sqlite3_mutex_held(pcache.mutex) );
   if( sz<=pcache.szSlot && pcache.pFree ){
     PgFreeslot *p = pcache.pFree;
     pcache.pFree = p->pNext;
@@ -342,9 +381,7 @@ void *pcacheMalloc(int sz, PCache *pCache){
     ** the global LRU mutex.
     */
     pcacheExitGlobal();
-    sqlite3PcacheUnlock(pCache);
     p = sqlite3Malloc(sz);
-    sqlite3PcacheLock(pCache);
     pcacheEnterGlobal();
 
     if( p ){
@@ -366,7 +403,7 @@ void *sqlite3PageMalloc(sz){
 ** Release a pager memory allocation
 */
 void pcacheFree(void *p){
-  assert( sqlite3_mutex_held(pcache.mutex_lru) );
+  assert( sqlite3_mutex_held(pcache.mutex) );
   if( p==0 ) return;
   if( p>=pcache.pStart && p<pcache.pEnd ){
     PgFreeslot *pSlot;
@@ -392,17 +429,15 @@ void sqlite3PageFree(void *p){
 static PgHdr *pcachePageAlloc(PCache *pCache){
   PgHdr *p;
   int sz = sizeof(*p) + pCache->szPage + pCache->szExtra;
-  assert( sqlite3_mutex_held(pcache.mutex_lru) );
+  assert( sqlite3_mutex_held(pcache.mutex) );
   p = pcacheMalloc(sz, pCache);
   if( p==0 ) return 0;
   memset(p, 0, sizeof(PgHdr));
   p->pData = (void*)&p[1];
   p->pExtra = (void*)&((char*)p->pData)[pCache->szPage];
-  pcache.nPage++;
   if( pCache->bPurgeable ){
-    pcache.nPurgeable++;
+    pcache.nCurrentPage++;
   }
-
   return p;
 }
 
@@ -410,10 +445,9 @@ static PgHdr *pcachePageAlloc(PCache *pCache){
 ** Deallocate a page
 */
 static void pcachePageFree(PgHdr *p){
-  assert( sqlite3_mutex_held(pcache.mutex_lru) );
-  pcache.nPage--;
+  assert( sqlite3_mutex_held(pcache.mutex) );
   if( p->pCache->bPurgeable ){
-    pcache.nPurgeable--;
+    pcache.nCurrentPage--;
   }
   pcacheFree(p->apSave[0]);
   pcacheFree(p->apSave[1]);
@@ -426,7 +460,7 @@ static void pcachePageFree(PgHdr *p){
 ** the argument is passed to pcachePageFree().
 */
 static int pcachePageSize(PgHdr *p){
-  assert( sqlite3_mutex_held(pcache.mutex_lru) );
+  assert( sqlite3_mutex_held(pcache.mutex) );
   assert( !pcache.pStart );
   assert( p->apSave[0]==0 );
   assert( p->apSave[1]==0 );
@@ -435,58 +469,30 @@ static int pcachePageSize(PgHdr *p){
 }
 #endif
 
-static int pcacheRecyclePage(PgHdr *p, PCache *pCache){
-  assert( sqlite3_mutex_held(pcache.mutex_lru) );
-  assert( sqlite3_mutex_held(pcache.mutex_mem2) );
-
-  PCache *pC = p->pCache;
-  assert( pC->iInUseMM==0 );
-  pC->iInUseMM = 1;
-  if( pC->xStress && (pC->iInUseDB==0 || pC==pCache) ){
-    pcacheExitGlobal();
-    pC->xStress(pC->pStress, p);
-    pcacheEnterGlobal();
-  }
-  pC->iInUseMM = 0;
-
-  return (p->flags&PGHDR_DIRTY);
-}
-
 /*
-** Recycle a page from the global LRU list. If no page can be recycled, 
-** return NULL. Otherwise, the pointer returned points to a PgHdr 
-** object that has been removed from all lists and hash tables in
-** which is was referenced. The caller may reuse the allocation directly
-** or may pass it to pcachePageFree() to return the memory to the heap
-** (or pcache.pFree list).
-*/ 
-static PgHdr *pcacheRecycle(PCache *pCache){
+** Attempt to 'recycle' a page from the global LRU list. Only clean,
+** unreferenced pages from purgeable caches are eligible for recycling.
+**
+** This function removes page pcache.pLruTail from the global LRU list,
+** and from the hash-table and PCache.pClean list of the owner pcache.
+** There should be no other references to the page.
+**
+** A pointer to the recycled page is returned, or NULL if no page is
+** eligible for recycling.
+*/
+static PgHdr *pcacheRecyclePage(){
   PgHdr *p = 0;
+  assert( sqlite3_mutex_held(pcache.mutex) );
 
-  assert( pcache.isInit );
-  assert( sqlite3_mutex_held(pcache.mutex_lru) );
-
-  if( SQLITE_OK==sqlite3_mutex_try(pcache.mutex_mem2) ){
-    p = pcache.pLruSynced;
-    while( p && (p->flags&PGHDR_DIRTY) && pcacheRecyclePage(p, pCache) ){
-      do { p = p->pPrevLru; } while( p && (p->flags&PGHDR_NEED_SYNC) );
-    }
-    if( !p ){
-      p = pcache.pLruTail;
-      while( p && (p->flags&PGHDR_DIRTY) && pcacheRecyclePage(p, pCache) ){
-        do { p = p->pPrevLru; } while( p && 0==(p->flags&PGHDR_NEED_SYNC) );
-      }
-    }
-    sqlite3_mutex_leave(pcache.mutex_mem2);
-  }
-
-  if( p ){
+  if( (p=pcache.pLruTail) ){
+    assert( (p->flags&PGHDR_DIRTY)==0 );
     pcacheRemoveFromLruList(p);
     pcacheRemoveFromHash(p);
     pcacheRemoveFromList(&p->pCache->pClean, p);
 
     /* If the always-rollback flag is set on the page being recycled, set 
-    ** the always-rollback flag on the corresponding pager.
+    ** the always-rollback flag on the corresponding pager. TODO: This is
+    ** a thread-safety problem.
     */
     if( p->flags&PGHDR_ALWAYS_ROLLBACK ){
       assert(p->pPager);
@@ -509,21 +515,45 @@ static PgHdr *pcacheRecycleOrAlloc(PCache *pCache){
 
   int szPage = pCache->szPage;
   int szExtra = pCache->szExtra;
-  int bPurg = pCache->bPurgeable;
 
   assert( pcache.isInit );
-  assert( sqlite3_mutex_notheld(pcache.mutex_lru) );
+  assert( sqlite3_mutex_notheld(pcache.mutex) );
 
   pcacheEnterGlobal();
 
-  if( (pcache.mxPage && pcache.nPage>=pcache.mxPage) 
-   || (!pcache.mxPage && bPurg && pcache.nPurgeable>=pcache.mxPagePurgeable)
+  /* If we have reached the limit for pinned/dirty pages, and there is at
+  ** least one dirty page, invoke the xStress callback to cause a page to
+  ** become clean.
+  */
+  assert( pCache->nPinned==pcachePinnedCount(pCache) );
+  assert( pcacheCheckSynced(pCache) );
+  if( pCache->xStress
+   && pCache->pDirty
+   && pCache->nPinned>=(pcache.nMaxPage+pCache->nMin-pcache.nMinPage)
   ){
-    /* If the above test succeeds, then try to obtain a buffer by recycling
-    ** an existing page. */
-    p = pcacheRecycle(pCache);
+    PgHdr *pPg;
+    assert(pCache->pDirtyTail);
+
+    for(pPg=pCache->pSynced; 
+        pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC)); 
+        pPg=pPg->pPrev
+    );
+    if( !pPg ){
+      for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pPrev);
+    }
+    if( pPg ){
+      pcacheExitGlobal();
+      pCache->xStress(pCache->pStress, pPg);
+      pcacheEnterGlobal();
+    }
+  }
+
+  /* If the global page limit has been reached, try to recycle a page. */
+  if( pcache.nCurrentPage>=pcache.nMaxPage ){
+    p = pcacheRecyclePage();
   }
 
+  /* If a page has been recycled but it is the wrong size, free it. */
   if( p && (p->pCache->szPage!=szPage || p->pCache->szExtra!=szExtra) ){
     pcachePageFree(p);
     p = 0;
@@ -546,9 +576,8 @@ int sqlite3PcacheInitialize(void){
   assert( pcache.isInit==0 );
   memset(&pcache, 0, sizeof(pcache));
   if( sqlite3Config.bCoreMutex ){
-    pcache.mutex_lru = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU);
-    pcache.mutex_mem2 = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM2);
-    if( pcache.mutex_lru==0 || pcache.mutex_mem2==0 ){
+    pcache.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU);
+    if( pcache.mutex==0 ){
       return SQLITE_NOMEM;
     }
   }
@@ -586,21 +615,14 @@ void sqlite3PcacheOpen(
   p->xStress = xStress;
   p->pStress = pStress;
   p->nMax = 100;
+  p->nMin = 20;
 
+  pcacheEnterGlobal();
   if( bPurgeable ){
-    pcacheEnterGlobal();
-    pcache.mxPagePurgeable += p->nMax;
-    pcacheExitGlobal();
+    pcache.nMaxPage += p->nMax;
+    pcache.nMinPage += p->nMin;
   }
 
-  /* Add the new pager-cache to the list of caches starting at pcache.pAll */
-  pcacheEnterGlobal();
-  p->pNextAll = pcache.pAll;
-  if( pcache.pAll ){
-    pcache.pAll->pPrevAll = p;
-  }
-  p->pPrevAll = 0;
-  pcache.pAll = p;
   pcacheExitGlobal();
 }
 
@@ -626,21 +648,23 @@ int sqlite3PcacheFetch(
   assert( pcache.isInit );
   assert( pCache!=0 );
   assert( pgno>0 );
-  assert( pCache->iInUseDB || pCache->iInUseMM );
+  assert( pCache->nPinned==pcachePinnedCount(pCache) );
 
   /* Search the hash table for the requested page. Exit early if it is found. */
   if( pCache->apHash ){
     u32 h = pgno % pCache->nHash;
     for(pPage=pCache->apHash[h]; pPage; pPage=pPage->pNextHash){
       if( pPage->pgno==pgno ){
-        if( pPage->nRef==0 /* && (pPage->flags & PGHDR_DIRTY)==0 */ ){
-          pcacheEnterGlobal();
-          pcacheRemoveFromLruList(pPage);
-          pcacheExitGlobal();
-        }
-        if( (pPage->nRef++)==0 ){
+        if( pPage->nRef==0 ){
+          if( 0==(pPage->flags&PGHDR_DIRTY) ){
+            pcacheEnterGlobal();
+            pcacheRemoveFromLruList(pPage);
+            pcacheExitGlobal();
+            pCache->nPinned++;
+          }
           pCache->nRef++;
         }
+        pPage->nRef++;
         *ppPage = pPage;
         return SQLITE_OK;
       }
@@ -668,12 +692,14 @@ int sqlite3PcacheFetch(
     pPage->pCache = pCache;
     pPage->nRef = 1;
     pCache->nRef++;
+    pCache->nPinned++;
     pcacheAddToList(&pCache->pClean, pPage);
     pcacheAddToHash(pPage);
   }else{
     *ppPage = 0;
   }
 
+  assert( pCache->nPinned==pcachePinnedCount(pCache) );
   return SQLITE_OK;
 }
 
@@ -683,20 +709,23 @@ int sqlite3PcacheFetch(
 */
 void sqlite3PcacheRelease(PgHdr *p){
   assert( p->nRef>0 );
-  assert( p->pCache->iInUseDB || p->pCache->iInUseMM );
   p->nRef--;
   if( p->nRef==0 ){
     PCache *pCache = p->pCache;
-    pCache->nRef--;
     if( p->pCache->xDestroy ){
       p->pCache->xDestroy(p);
     }
-#if 0
-    if( (p->flags & PGHDR_DIRTY)!=0 ) return;
-#endif
-    pcacheEnterGlobal();
-    pcacheAddToLruList(p);
-    pcacheExitGlobal();
+    pCache->nRef--;
+    if( (p->flags&PGHDR_DIRTY)==0 ){
+      pCache->nPinned--;
+      pcacheEnterGlobal();
+      pcacheAddToLruList(p);
+      pcacheExitGlobal();
+    }else{
+      /* Move the page to the head of the caches dirty list. */
+      pcacheRemoveFromList(&pCache->pDirty, p);
+      pcacheAddToList(&pCache->pDirty, p);
+    }
   }
 }
 
@@ -711,10 +740,10 @@ void sqlite3PcacheRef(PgHdr *p){
 */
 void sqlite3PcacheDrop(PgHdr *p){
   PCache *pCache;
-  assert( p->pCache->iInUseDB );
   assert( p->nRef==1 );
   pCache = p->pCache;
   pCache->nRef--;
+  pCache->nPinned--;
   if( p->flags & PGHDR_DIRTY ){
     pcacheRemoveFromList(&pCache->pDirty, p);
   }else{
@@ -732,7 +761,6 @@ void sqlite3PcacheDrop(PgHdr *p){
 */
 void sqlite3PcacheMakeDirty(PgHdr *p){
   PCache *pCache;
-  assert( p->pCache->iInUseDB );
   p->flags &= ~PGHDR_DONT_WRITE;
   if( p->flags & PGHDR_DIRTY ) return;
   assert( (p->flags & PGHDR_DIRTY)==0 );
@@ -749,14 +777,20 @@ void sqlite3PcacheMakeDirty(PgHdr *p){
 */
 void sqlite3PcacheMakeClean(PgHdr *p){
   PCache *pCache;
-  assert( p->pCache->iInUseDB || p->pCache->iInUseMM );
   if( (p->flags & PGHDR_DIRTY)==0 ) return;
   assert( p->apSave[0]==0 && p->apSave[1]==0 );
   assert( p->flags & PGHDR_DIRTY );
   pCache = p->pCache;
   pcacheRemoveFromList(&pCache->pDirty, p);
+  pcacheEnterGlobal();
   pcacheAddToList(&pCache->pClean, p);
   p->flags &= ~PGHDR_DIRTY;
+  if( p->nRef==0 ){
+    pcacheAddToLruList(p);
+    pCache->nPinned--;
+  }
+  assert( pCache->nPinned==pcachePinnedCount(pCache) );
+  pcacheExitGlobal();
 }
 
 /*
@@ -764,15 +798,19 @@ void sqlite3PcacheMakeClean(PgHdr *p){
 */
 void sqlite3PcacheCleanAll(PCache *pCache){
   PgHdr *p;
-  assert( pCache->iInUseDB );
   pcacheEnterGlobal();
   while( (p = pCache->pDirty)!=0 ){
     assert( p->apSave[0]==0 && p->apSave[1]==0 );
     pcacheRemoveFromList(&pCache->pDirty, p);
-    pcacheAddToList(&pCache->pClean, p);
     p->flags &= ~PGHDR_DIRTY;
+    pcacheAddToList(&pCache->pClean, p);
+    if( p->nRef==0 ){
+      pcacheAddToLruList(p);
+      pCache->nPinned--;
+    }
   }
   sqlite3PcacheAssertFlags(pCache, 0, PGHDR_DIRTY);
+  assert( pCache->nPinned==pcachePinnedCount(pCache) );
   pcacheExitGlobal();
 }
 
@@ -782,7 +820,7 @@ void sqlite3PcacheCleanAll(PCache *pCache){
 ** flag set.
 */
 void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
-  assert( p->pCache->iInUseDB );
+  assert( p->nRef>0 );
   pcacheRemoveFromHash(p);
   p->pgno = newPgno;
   if( newPgno==0 ){
@@ -798,21 +836,12 @@ void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
   pcacheAddToHash(p);
 }
 
-/*
-** Set the global maximum number of pages. Return the previous value.
-*/
-void sqlite3PcacheGlobalMax(int mx){
-  pcacheEnterGlobal();
-  pcache.mxPage = mx;
-  pcacheExitGlobal();
-}
-
 /*
 ** Remove all content from a page cache
 */
 void pcacheClear(PCache *pCache){
   PgHdr *p, *pNext;
-  assert( sqlite3_mutex_held(pcache.mutex_lru) );
+  assert( sqlite3_mutex_held(pcache.mutex) );
   for(p=pCache->pClean; p; p=pNext){
     pNext = p->pNext;
     pcacheRemoveFromLruList(p);
@@ -820,12 +849,13 @@ void pcacheClear(PCache *pCache){
   }
   for(p=pCache->pDirty; p; p=pNext){
     pNext = p->pNext;
-    pcacheRemoveFromLruList(p);
     pcachePageFree(p);
   }
   pCache->pClean = 0;
   pCache->pDirty = 0;
+  pCache->pDirtyTail = 0;
   pCache->nPage = 0;
+  pCache->nPinned = 0;
   memset(pCache->apHash, 0, pCache->nHash*sizeof(pCache->apHash[0]));
 }
 
@@ -836,7 +866,6 @@ void pcacheClear(PCache *pCache){
 void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
   PgHdr *p, *pNext;
   PgHdr *pDirty = pCache->pDirty;
-  assert( pCache->iInUseDB );
   pcacheEnterGlobal();
   for(p=pCache->pClean; p||pDirty; p=pNext){
     if( !p ){
@@ -849,10 +878,11 @@ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
         pcacheRemoveFromHash(p);
         if( p->flags&PGHDR_DIRTY ){
           pcacheRemoveFromList(&pCache->pDirty, p);
+          pCache->nPinned--;
         }else{
           pcacheRemoveFromList(&pCache->pClean, p);
+          pcacheRemoveFromLruList(p);
         }
-        pcacheRemoveFromLruList(p);
         pcachePageFree(p);
       }else{
         /* If there are references to the page, it cannot be freed. In this
@@ -870,31 +900,16 @@ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
 ** Close a cache.
 */
 void sqlite3PcacheClose(PCache *pCache){
-  assert( pCache->iInUseDB==1 );
   pcacheEnterGlobal();
 
   /* Free all the pages used by this pager and remove them from the LRU list. */
   pcacheClear(pCache);
   if( pCache->bPurgeable ){
-    pcache.mxPagePurgeable -= pCache->nMax;
+    pcache.nMaxPage -= pCache->nMax;
+    pcache.nMinPage -= pCache->nMin;
   }
   sqlite3_free(pCache->apHash);
 
-  /* Now remove the pager-cache structure itself from the list of
-  ** all such structures headed by pcache.pAll. 
-  */
-  assert(pCache==pcache.pAll || pCache->pPrevAll);
-  assert(pCache->pNextAll==0 || pCache->pNextAll->pPrevAll==pCache);
-  assert(pCache->pPrevAll==0 || pCache->pPrevAll->pNextAll==pCache);
-  if( pCache->pPrevAll ){
-    pCache->pPrevAll->pNextAll = pCache->pNextAll;
-  }else{
-    pcache.pAll = pCache->pNextAll;
-  }
-  if( pCache->pNextAll ){
-    pCache->pNextAll->pPrevAll = pCache->pPrevAll;
-  }
-
   pcacheExitGlobal();
 }
 
@@ -910,7 +925,6 @@ void sqlite3PcacheClose(PCache *pCache){
 int sqlite3PcachePreserve(PgHdr *p, int idJournal){
   void *x;
   int sz;
-  assert( p->pCache->iInUseDB );
   assert( p->pCache->bPurgeable==0 );
   if( !p->apSave[idJournal] ){
     sz = p->pCache->szPage;
@@ -926,7 +940,6 @@ int sqlite3PcachePreserve(PgHdr *p, int idJournal){
 */
 void sqlite3PcacheCommit(PCache *pCache, int idJournal){
   PgHdr *p;
-  assert( pCache->iInUseDB );
   pcacheEnterGlobal();     /* Mutex is required to call pcacheFree() */
   for(p=pCache->pDirty; p; p=p->pNext){
     if( p->apSave[idJournal] ){
@@ -943,7 +956,6 @@ void sqlite3PcacheCommit(PCache *pCache, int idJournal){
 void sqlite3PcacheRollback(PCache *pCache, int idJournal){
   PgHdr *p;
   int sz;
-  assert( pCache->iInUseDB );
   pcacheEnterGlobal();     /* Mutex is required to call pcacheFree() */
   sz = pCache->szPage;
   for(p=pCache->pDirty; p; p=p->pNext){
@@ -961,7 +973,6 @@ void sqlite3PcacheRollback(PCache *pCache, int idJournal){
 */
 void sqlite3PcacheAssertFlags(PCache *pCache, int trueMask, int falseMask){
   PgHdr *p;
-  assert( pCache->iInUseDB || pCache->iInUseMM );
   for(p=pCache->pDirty; p; p=p->pNext){
     assert( (p->flags&trueMask)==trueMask );
     assert( (p->flags&falseMask)==0 );
@@ -976,7 +987,6 @@ void sqlite3PcacheAssertFlags(PCache *pCache, int trueMask, int falseMask){
 ** Discard the contents of the cache.
 */
 int sqlite3PcacheClear(PCache *pCache){
-  assert( pCache->iInUseDB );
   assert(pCache->nRef==0);
   pcacheEnterGlobal();
   pcacheClear(pCache);
@@ -1063,7 +1073,6 @@ static PgHdr *pcacheSortDirtyList(PgHdr *pIn){
 */
 PgHdr *sqlite3PcacheDirtyList(PCache *pCache){
   PgHdr *p;
-  assert( pCache->iInUseDB );
   for(p=pCache->pDirty; p; p=p->pNext){
     p->pDirty = p->pNext;
   }
@@ -1081,7 +1090,6 @@ int sqlite3PcacheRefCount(PCache *pCache){
 ** Return the total number of pages in the cache.
 */
 int sqlite3PcachePagecount(PCache *pCache){
-  assert( pCache->iInUseDB || pCache->iInUseMM );
   assert( pCache->nPage>=0 );
   return pCache->nPage;
 }
@@ -1094,7 +1102,6 @@ int sqlite3PcachePagecount(PCache *pCache){
 */
 void sqlite3PcacheIterate(PCache *pCache, void (*xIter)(PgHdr *)){
   PgHdr *p;
-  assert( pCache->iInUseDB || pCache->iInUseMM );
   for(p=pCache->pClean; p; p=p->pNext){
     xIter(p);
   }
@@ -1111,13 +1118,11 @@ void sqlite3PcacheSetFlags(PCache *pCache, int andMask, int orMask){
   PgHdr *p;
 
   assert( (orMask&PGHDR_NEED_SYNC)==0 );
-  assert( pCache->iInUseDB || pCache->iInUseMM );
 
   /* Obtain the global mutex before modifying any PgHdr.flags variables 
   ** or traversing the LRU list.
   */ 
   pcacheEnterGlobal();
-  assert( sqlite3_mutex_held(pcache.mutex_lru) );
 
   for(p=pCache->pDirty; p; p=p->pNext){
     p->flags = (p->flags&andMask)|orMask;
@@ -1127,8 +1132,11 @@ void sqlite3PcacheSetFlags(PCache *pCache, int andMask, int orMask){
   }
 
   if( 0==(andMask&PGHDR_NEED_SYNC) ){
-    for(p=pcache.pLruTail; p && (p->flags&PGHDR_NEED_SYNC); p=p->pPrevLru);
-    pcache.pLruSynced = p;
+    PgHdr *pSynced = pCache->pDirtyTail;
+    while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){
+      pSynced = pSynced->pPrev;
+    }
+    pCache->pSynced = pSynced;
   }
 
   pcacheExitGlobal();
@@ -1150,40 +1158,13 @@ void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){
   }
   if( pCache->bPurgeable ){
     pcacheEnterGlobal();
-    pcache.mxPagePurgeable -= pCache->nMax;
-    pcache.mxPagePurgeable += mxPage;
+    pcache.nMaxPage -= pCache->nMax;
+    pcache.nMaxPage += mxPage;
     pcacheExitGlobal();
   }
   pCache->nMax = mxPage;
 }
 
-/*
-** Lock a pager-cache.
-*/
-void sqlite3PcacheLock(PCache *pCache){
-  if( pCache ){
-    assert( sqlite3_mutex_notheld(pcache.mutex_lru) );
-    pCache->iInUseDB++;
-    if( pCache->iInUseMM && pCache->iInUseDB==1 ){
-      pCache->iInUseDB = 0;
-      sqlite3_mutex_enter(pcache.mutex_mem2);
-      assert( pCache->iInUseMM==0 && pCache->iInUseDB==0 );
-      pCache->iInUseDB = 1;
-      sqlite3_mutex_leave(pcache.mutex_mem2);
-    }
-  }
-}
-
-/*
-** Unlock a pager-cache.
-*/
-void sqlite3PcacheUnlock(PCache *pCache){
-  if( pCache ){
-    pCache->iInUseDB--;
-    assert( pCache->iInUseDB>=0 );
-  }
-}
-
 #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
 /*
 ** This function is called to free superfluous dynamically allocated memory
@@ -1199,7 +1180,7 @@ int sqlite3PcacheReleaseMemory(int nReq){
   if( pcache.pStart==0 ){
     PgHdr *p;
     pcacheEnterGlobal();
-    while( (nReq<0 || nFree<nReq) && (p=pcacheRecycle(0)) ){
+    while( (nReq<0 || nFree<nReq) && (p=pcacheRecyclePage()) ){
       nFree += pcachePageSize(p);
       pcachePageFree(p);
     }
index 0a59d1d8b7259168afaac0195d25c681b20daffe..0fc3b7df3cea46195f842afc74d00bd5f76786b0 100644 (file)
@@ -12,7 +12,7 @@
 ** This header file defines the interface that the sqlite page cache
 ** subsystem. 
 **
-** @(#) $Id: pcache.h,v 1.5 2008/08/23 18:53:08 danielk1977 Exp $
+** @(#) $Id: pcache.h,v 1.6 2008/08/26 18:05:48 danielk1977 Exp $
 */
 
 #ifndef _PCACHE_H_
@@ -101,12 +101,6 @@ void sqlite3PcacheCleanAll(PCache*);    /* Mark all dirty list pages as clean */
 /* Change a page number.  Used by incr-vacuum. */
 void sqlite3PcacheMove(PgHdr*, Pgno);
 
-/* Set a global maximum page count for all page caches. 
-** If the sum of individual cache maxes exceed the global max, the
-** individuals are scaled down proportionally. 
-*/
-void sqlite3PcacheGlobalMax(int N);
-
 /* Remove all pages with pgno>x.  Reset the cache if x==0 */
 void sqlite3PcacheTruncate(PCache*, Pgno x);
 
@@ -148,10 +142,7 @@ int sqlite3PcachePagecount(PCache*);
 */
 void sqlite3PcacheIterate(PCache *pCache, void (*xIter)(PgHdr *));
 
-/* Set and get the suggested cache-size for the specified pager-cache. If
-** a global maximum on the number of pages cached by the system is 
-** configured via the sqlite3PcacheGlobalMax() API, then the suggested
-** cache-sizes are not used at all.
+/* Set and get the suggested cache-size for the specified pager-cache.
 **
 ** If no global maximum is configured, then the system attempts to limit
 ** the total number of pages cached by purgeable pager-caches to the sum
@@ -160,14 +151,7 @@ void sqlite3PcacheIterate(PCache *pCache, void (*xIter)(PgHdr *));
 int sqlite3PcacheGetCachesize(PCache *);
 void sqlite3PcacheSetCachesize(PCache *, int);
 
-/* Lock and unlock a pager-cache object. The PcacheLock() function may 
-** block if the lock is temporarily available. While a pager-cache is locked,
-** the system guarantees that any configured xStress() callback will not
-** be invoked by any thread other than the one holding the lock.
-*/
-void sqlite3PcacheLock(PCache *);
-void sqlite3PcacheUnlock(PCache *);
-
+/* Try to return memory used by the pcache module to the main memory heap */
 int sqlite3PcacheReleaseMemory(int);
 
 #endif /* _PCACHE_H_ */
index 18b128b7b1b67ad1dea7275f3035f4dc582d4892..5188257d439a8aa57f81e598aca45f8fe8ccc4cc 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test2.c,v 1.60 2008/08/20 14:49:25 danielk1977 Exp $
+** $Id: test2.c,v 1.61 2008/08/26 18:05:48 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -382,26 +382,6 @@ static int page_lookup(
   return TCL_OK;
 }
 
-/*
-** Usage:   pcache_global_max NPAGE
-*/
-static int pcache_global_max(
-  void *NotUsed,
-  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
-  int argc,              /* Number of arguments */
-  const char **argv      /* Text of each argument */
-){
-  int nPage;
-  if( argc!=2 ){
-    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-       " NPAGE\"", 0);
-    return TCL_ERROR;
-  }
-  if( Tcl_GetInt(interp, argv[1], &nPage) ) return TCL_ERROR;
-  sqlite3PcacheGlobalMax(nPage);
-  return TCL_OK;
-}
-
 /*
 ** Usage:   pager_truncate ID PGNO
 */
@@ -650,7 +630,6 @@ int Sqlitetest2_Init(Tcl_Interp *interp){
     { "page_write",              (Tcl_CmdProc*)page_write          },
     { "page_number",             (Tcl_CmdProc*)page_number         },
     { "pager_truncate",          (Tcl_CmdProc*)pager_truncate      },
-    { "pcache_global_max",       (Tcl_CmdProc*)pcache_global_max   },
 #ifndef SQLITE_OMIT_DISKIO
     { "fake_big_file",           (Tcl_CmdProc*)fake_big_file       },
 #endif
index 5cd93f3f589b7bb84c672b0a83fff694b3e2ee61..32fa873fa453e46a877ece6311396ed23778c3b8 100644 (file)
@@ -11,7 +11,7 @@
 #
 # This file contains tests of the memory allocation subsystem
 #
-# $Id: memsubsys1.test,v 1.10 2008/08/20 14:49:25 danielk1977 Exp $
+# $Id: memsubsys1.test,v 1.11 2008/08/26 18:05:48 danielk1977 Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -228,10 +228,11 @@ sqlite3_shutdown
 sqlite3_config_pagecache [expr 4096+$xtra_size] 24
 sqlite3_config_scratch 25000 1
 sqlite3_initialize
-pcache_global_max 15
 reset_highwater_marks
 build_test_db memsubsys1-7 {
   PRAGMA page_size=4096;
+  PRAGMA cache_size=10;
+  PRAGMA temp_store=memory;
 }
 #show_memstats
 do_test memsubsys1-7.3 {
@@ -251,7 +252,6 @@ do_test memsubsys1-7.6 {
 do_test memsubsys1-7.7 {
   set s_ovfl [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_OVERFLOW 0] 2]
 } 0
-pcache_global_max 0
 
 db close
 sqlite3_shutdown
index fcf180cf3a06c4bc6c4505e6f3608aced46c5f6d..83b4c59b31b169f8ace0769f98afb9aaef37c80b 100644 (file)
@@ -16,7 +16,7 @@
 # any statement other than a COMMIT, an I/O error is returned instead
 # of SQLITE_BUSY.
 #
-# $Id: tkt2409.test,v 1.4 2008/08/20 14:49:25 danielk1977 Exp $
+# $Id: tkt2409.test,v 1.5 2008/08/26 18:05:48 danielk1977 Exp $
 
 # Test Outline:
 #
@@ -79,8 +79,6 @@ proc unread_lock_db {} {
   }
 }
 
-pcache_global_max 10
-
 # Open the db handle used by [read_lock_db].
 #
 sqlite3 db2 test.db
@@ -223,8 +221,6 @@ do_test tkt2409-4.4 {
   catchsql { ROLLBACK }
 } {0 {}}
 
-pcache_global_max 0
-
 unread_lock_db
 db2 close
 unset -nocomplain t1