Simplify the pcache module by only recycling clean pages from 'other' caches. This commit causes errors in test files ioerr5.test and malloc5.test because they test recycling dirty pages from other caches. (CVS 5615)

FossilOrigin-Name: 9e511e161bcb077450d31fca5dd20c2557f103b3
This commit is contained in:
danielk1977 2008-08-26 18:05:48 +00:00
parent 1a4e3162d9
commit d491e1bfd1
8 changed files with 227 additions and 351 deletions

View File

@ -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

View File

@ -1 +1 @@
c84d46c71233bbf869513f433b1d18cbd7f2a35e
9e511e161bcb077450d31fca5dd20c2557f103b3

View 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

View 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"
@ -19,23 +19,22 @@
** 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;
if( bPurgeable ){
pcacheEnterGlobal();
pcache.mxPagePurgeable += p->nMax;
pcacheExitGlobal();
}
/* 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;
if( bPurgeable ){
pcache.nMaxPage += p->nMax;
pcache.nMinPage += p->nMin;
}
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);
}

View 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_ */

View 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

View 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

View 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