提交 7184778c 编写于 作者: H Hongze Cheng

more page cache

上级 7248dc68
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
** **
** The PCache.pSynced variable is used to optimize searching for a dirty ** The PCache.pSynced variable is used to optimize searching for a dirty
** page to eject from the cache mid-transaction. It is better to eject ** page to eject from the cache mid-transaction. It is better to eject
** a page that does not require a journal sync than one that does. ** a page that does not require a journal sync than one that does.
** Therefore, pSynced is maintained so that it *almost* always points ** Therefore, pSynced is maintained so that it *almost* always points
** to either the oldest page in the pDirty/pDirtyTail list that has a ** to either the oldest page in the pDirty/pDirtyTail list that has a
** clear PGHDR_NEED_SYNC flag or to a page that is older than this one ** clear PGHDR_NEED_SYNC flag or to a page that is older than this one
...@@ -39,18 +39,18 @@ ...@@ -39,18 +39,18 @@
** pointers). ** pointers).
*/ */
struct PCache { struct PCache {
PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */ PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */
PgHdr *pSynced; /* Last synced page in dirty page list */ PgHdr *pSynced; /* Last synced page in dirty page list */
int nRefSum; /* Sum of ref counts over all pages */ int nRefSum; /* Sum of ref counts over all pages */
int szCache; /* Configured cache size */ int szCache; /* Configured cache size */
int szSpill; /* Size before spilling occurs */ int szSpill; /* Size before spilling occurs */
int szPage; /* Size of every page in this cache */ int szPage; /* Size of every page in this cache */
int szExtra; /* Size of extra space for each page */ int szExtra; /* Size of extra space for each page */
u8 bPurgeable; /* True if pages are on backing store */ u8 bPurgeable; /* True if pages are on backing store */
u8 eCreate; /* eCreate value for for xFetch() */ u8 eCreate; /* eCreate value for for xFetch() */
int (*xStress)(void *, PgHdr *); /* Call to try make a page clean */ int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */
void * pStress; /* Argument to xStress */ void *pStress; /* Argument to xStress */
sqlite3_pcache *pCache; /* Pluggable cache module */ sqlite3_pcache *pCache; /* Pluggable cache module */
}; };
/********************************** Test and Debug Logic **********************/ /********************************** Test and Debug Logic **********************/
...@@ -63,39 +63,36 @@ struct PCache { ...@@ -63,39 +63,36 @@ struct PCache {
** is displayed for many operations, resulting in a lot of output. ** is displayed for many operations, resulting in a lot of output.
*/ */
#if defined(SQLITE_DEBUG) && 0 #if defined(SQLITE_DEBUG) && 0
int sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */ int sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */
int sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */ int sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */
#define pcacheTrace(X) \ # define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;}
if (sqlite3PcacheTrace) { \ void pcacheDump(PCache *pCache){
sqlite3DebugPrintf X; \ int N;
} int i, j;
void pcacheDump(PCache *pCache) { sqlite3_pcache_page *pLower;
int N; PgHdr *pPg;
int i, j; unsigned char *a;
sqlite3_pcache_page *pLower;
PgHdr * pPg; if( sqlite3PcacheTrace<2 ) return;
unsigned char * a; if( pCache->pCache==0 ) return;
N = sqlite3PcachePagecount(pCache);
if (sqlite3PcacheTrace < 2) return; if( N>sqlite3PcacheMxDump ) N = sqlite3PcacheMxDump;
if (pCache->pCache == 0) return; for(i=1; i<=N; i++){
N = sqlite3PcachePagecount(pCache); pLower = pcache2.xFetch(pCache->pCache, i, 0);
if (N > sqlite3PcacheMxDump) N = sqlite3PcacheMxDump; if( pLower==0 ) continue;
for (i = 1; i <= N; i++) { pPg = (PgHdr*)pLower->pExtra;
pLower = pcache2.xFetch(pCache->pCache, i, 0); printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags);
if (pLower == 0) continue; a = (unsigned char *)pLower->pBuf;
pPg = (PgHdr *)pLower->pExtra; for(j=0; j<12; j++) printf("%02x", a[j]);
printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags); printf("\n");
a = (unsigned char *)pLower->pBuf; if( pPg->pPage==0 ){
for (j = 0; j < 12; j++) printf("%02x", a[j]); pcache2.xUnpin(pCache->pCache, pLower, 0);
printf("\n"); }
if (pPg->pPage == 0) {
pcache2.xUnpin(pCache->pCache, pLower, 0);
} }
} }
} #else
#else # define pcacheTrace(X)
#define pcacheTrace(X) # define pcacheDump(X)
#define pcacheDump(X)
#endif #endif
/* /*
...@@ -108,20 +105,20 @@ void pcacheDump(PCache *pCache) { ...@@ -108,20 +105,20 @@ void pcacheDump(PCache *pCache) {
** assert( sqlite3PcachePageSanity(pPg) ); ** assert( sqlite3PcachePageSanity(pPg) );
*/ */
#ifdef SQLITE_DEBUG #ifdef SQLITE_DEBUG
int sqlite3PcachePageSanity(PgHdr *pPg) { int sqlite3PcachePageSanity(PgHdr *pPg){
PCache *pCache; PCache *pCache;
assert(pPg != 0); assert( pPg!=0 );
assert(pPg->pgno > 0 || pPg->pPager == 0); /* Page number is 1 or more */ assert( pPg->pgno>0 || pPg->pPager==0 ); /* Page number is 1 or more */
pCache = pPg->pCache; pCache = pPg->pCache;
assert(pCache != 0); /* Every page has an associated PCache */ assert( pCache!=0 ); /* Every page has an associated PCache */
if (pPg->flags & PGHDR_CLEAN) { if( pPg->flags & PGHDR_CLEAN ){
assert((pPg->flags & PGHDR_DIRTY) == 0); /* Cannot be both CLEAN and DIRTY */ assert( (pPg->flags & PGHDR_DIRTY)==0 );/* Cannot be both CLEAN and DIRTY */
assert(pCache->pDirty != pPg); /* CLEAN pages not on dirty list */ assert( pCache->pDirty!=pPg ); /* CLEAN pages not on dirty list */
assert(pCache->pDirtyTail != pPg); assert( pCache->pDirtyTail!=pPg );
} }
/* WRITEABLE pages must also be DIRTY */ /* WRITEABLE pages must also be DIRTY */
if (pPg->flags & PGHDR_WRITEABLE) { if( pPg->flags & PGHDR_WRITEABLE ){
assert(pPg->flags & PGHDR_DIRTY); /* WRITEABLE implies DIRTY */ assert( pPg->flags & PGHDR_DIRTY ); /* WRITEABLE implies DIRTY */
} }
/* NEED_SYNC can be set independently of WRITEABLE. This can happen, /* NEED_SYNC can be set independently of WRITEABLE. This can happen,
** for example, when using the sqlite3PagerDontWrite() optimization: ** for example, when using the sqlite3PagerDontWrite() optimization:
...@@ -144,12 +141,13 @@ int sqlite3PcachePageSanity(PgHdr *pPg) { ...@@ -144,12 +141,13 @@ int sqlite3PcachePageSanity(PgHdr *pPg) {
} }
#endif /* SQLITE_DEBUG */ #endif /* SQLITE_DEBUG */
/********************************** Linked List Management ********************/ /********************************** Linked List Management ********************/
/* Allowed values for second argument to pcacheManageDirtyList() */ /* Allowed values for second argument to pcacheManageDirtyList() */
#define PCACHE_DIRTYLIST_REMOVE 1 /* Remove pPage from dirty list */ #define PCACHE_DIRTYLIST_REMOVE 1 /* Remove pPage from dirty list */
#define PCACHE_DIRTYLIST_ADD 2 /* Add pPage to the dirty list */ #define PCACHE_DIRTYLIST_ADD 2 /* Add pPage to the dirty list */
#define PCACHE_DIRTYLIST_FRONT 3 /* Move pPage to the front of the list */ #define PCACHE_DIRTYLIST_FRONT 3 /* Move pPage to the front of the list */
/* /*
** Manage pPage's participation on the dirty list. Bits of the addRemove ** Manage pPage's participation on the dirty list. Bits of the addRemove
...@@ -157,63 +155,66 @@ int sqlite3PcachePageSanity(PgHdr *pPg) { ...@@ -157,63 +155,66 @@ int sqlite3PcachePageSanity(PgHdr *pPg) {
** remove pPage from the dirty list. The 0x02 means add pPage back to ** remove pPage from the dirty list. The 0x02 means add pPage back to
** the dirty list. Doing both moves pPage to the front of the dirty list. ** the dirty list. Doing both moves pPage to the front of the dirty list.
*/ */
static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove) { static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
PCache *p = pPage->pCache; PCache *p = pPage->pCache;
pcacheTrace(("%p.DIRTYLIST.%s %d\n", p, addRemove == 1 ? "REMOVE" : addRemove == 2 ? "ADD" : "FRONT", pPage->pgno)); pcacheTrace(("%p.DIRTYLIST.%s %d\n", p,
if (addRemove & PCACHE_DIRTYLIST_REMOVE) { addRemove==1 ? "REMOVE" : addRemove==2 ? "ADD" : "FRONT",
assert(pPage->pDirtyNext || pPage == p->pDirtyTail); pPage->pgno));
assert(pPage->pDirtyPrev || pPage == p->pDirty); if( addRemove & PCACHE_DIRTYLIST_REMOVE ){
assert( pPage->pDirtyNext || pPage==p->pDirtyTail );
assert( pPage->pDirtyPrev || pPage==p->pDirty );
/* Update the PCache1.pSynced variable if necessary. */ /* Update the PCache1.pSynced variable if necessary. */
if (p->pSynced == pPage) { if( p->pSynced==pPage ){
p->pSynced = pPage->pDirtyPrev; p->pSynced = pPage->pDirtyPrev;
} }
if (pPage->pDirtyNext) { if( pPage->pDirtyNext ){
pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev; pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev;
} else { }else{
assert(pPage == p->pDirtyTail); assert( pPage==p->pDirtyTail );
p->pDirtyTail = pPage->pDirtyPrev; p->pDirtyTail = pPage->pDirtyPrev;
} }
if (pPage->pDirtyPrev) { if( pPage->pDirtyPrev ){
pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext; pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext;
} else { }else{
/* If there are now no dirty pages in the cache, set eCreate to 2. /* If there are now no dirty pages in the cache, set eCreate to 2.
** This is an optimization that allows sqlite3PcacheFetch() to skip ** This is an optimization that allows sqlite3PcacheFetch() to skip
** searching for a dirty page to eject from the cache when it might ** searching for a dirty page to eject from the cache when it might
** otherwise have to. */ ** otherwise have to. */
assert(pPage == p->pDirty); assert( pPage==p->pDirty );
p->pDirty = pPage->pDirtyNext; p->pDirty = pPage->pDirtyNext;
assert(p->bPurgeable || p->eCreate == 2); assert( p->bPurgeable || p->eCreate==2 );
if (p->pDirty == 0) { /*OPTIMIZATION-IF-TRUE*/ if( p->pDirty==0 ){ /*OPTIMIZATION-IF-TRUE*/
assert(p->bPurgeable == 0 || p->eCreate == 1); assert( p->bPurgeable==0 || p->eCreate==1 );
p->eCreate = 2; p->eCreate = 2;
} }
} }
} }
if (addRemove & PCACHE_DIRTYLIST_ADD) { if( addRemove & PCACHE_DIRTYLIST_ADD ){
pPage->pDirtyPrev = 0; pPage->pDirtyPrev = 0;
pPage->pDirtyNext = p->pDirty; pPage->pDirtyNext = p->pDirty;
if (pPage->pDirtyNext) { if( pPage->pDirtyNext ){
assert(pPage->pDirtyNext->pDirtyPrev == 0); assert( pPage->pDirtyNext->pDirtyPrev==0 );
pPage->pDirtyNext->pDirtyPrev = pPage; pPage->pDirtyNext->pDirtyPrev = pPage;
} else { }else{
p->pDirtyTail = pPage; p->pDirtyTail = pPage;
if (p->bPurgeable) { if( p->bPurgeable ){
assert(p->eCreate == 2); assert( p->eCreate==2 );
p->eCreate = 1; p->eCreate = 1;
} }
} }
p->pDirty = pPage; p->pDirty = pPage;
/* If pSynced is NULL and this page has a clear NEED_SYNC flag, set /* If pSynced is NULL and this page has a clear NEED_SYNC flag, set
** pSynced to point to it. Checking the NEED_SYNC flag is an ** pSynced to point to it. Checking the NEED_SYNC flag is an
** optimization, as if pSynced points to a page with the NEED_SYNC ** optimization, as if pSynced points to a page with the NEED_SYNC
** flag set sqlite3PcacheFetchStress() searches through all newer ** flag set sqlite3PcacheFetchStress() searches through all newer
** entries of the dirty-list for a page with NEED_SYNC clear anyway. */ ** entries of the dirty-list for a page with NEED_SYNC clear anyway. */
if (!p->pSynced && 0 == (pPage->flags & PGHDR_NEED_SYNC) /*OPTIMIZATION-IF-FALSE*/ if( !p->pSynced
) { && 0==(pPage->flags&PGHDR_NEED_SYNC) /*OPTIMIZATION-IF-FALSE*/
){
p->pSynced = pPage; p->pSynced = pPage;
} }
} }
...@@ -224,8 +225,8 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove) { ...@@ -224,8 +225,8 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove) {
** Wrapper around the pluggable caches xUnpin method. If the cache is ** Wrapper around the pluggable caches xUnpin method. If the cache is
** being used for an in-memory database, this function is a no-op. ** being used for an in-memory database, this function is a no-op.
*/ */
static void pcacheUnpin(PgHdr *p) { static void pcacheUnpin(PgHdr *p){
if (p->pCache->bPurgeable) { if( p->pCache->bPurgeable ){
pcacheTrace(("%p.UNPIN %d\n", p->pCache, p->pgno)); pcacheTrace(("%p.UNPIN %d\n", p->pCache, p->pgno));
pcache2.xUnpin(p->pCache->pCache, p->pPage, 0); pcache2.xUnpin(p->pCache->pCache, p->pPage, 0);
pcacheDump(p->pCache); pcacheDump(p->pCache);
...@@ -236,31 +237,33 @@ static void pcacheUnpin(PgHdr *p) { ...@@ -236,31 +237,33 @@ static void pcacheUnpin(PgHdr *p) {
** Compute the number of pages of cache requested. p->szCache is the ** Compute the number of pages of cache requested. p->szCache is the
** cache size requested by the "PRAGMA cache_size" statement. ** cache size requested by the "PRAGMA cache_size" statement.
*/ */
static int numberOfCachePages(PCache *p) { static int numberOfCachePages(PCache *p){
if (p->szCache >= 0) { if( p->szCache>=0 ){
/* IMPLEMENTATION-OF: R-42059-47211 If the argument N is positive then the /* IMPLEMENTATION-OF: R-42059-47211 If the argument N is positive then the
** suggested cache size is set to N. */ ** suggested cache size is set to N. */
return p->szCache; return p->szCache;
} else { }else{
i64 n; i64 n;
/* IMPLEMANTATION-OF: R-59858-46238 If the argument N is negative, then the /* IMPLEMANTATION-OF: R-59858-46238 If the argument N is negative, then the
** number of cache pages is adjusted to be a number of pages that would ** number of cache pages is adjusted to be a number of pages that would
** use approximately abs(N*1024) bytes of memory based on the current ** use approximately abs(N*1024) bytes of memory based on the current
** page size. */ ** page size. */
n = ((-1024 * (i64)p->szCache) / (p->szPage + p->szExtra)); n = ((-1024*(i64)p->szCache)/(p->szPage+p->szExtra));
if (n > 1000000000) n = 1000000000; if( n>1000000000 ) n = 1000000000;
return (int)n; return (int)n;
} }
} }
/*************************************************** General Interfaces ****** /*************************************************** General Interfaces ******
** **
** Initialize and shutdown the page cache subsystem. Neither of these ** Initialize and shutdown the page cache subsystem. Neither of these
** functions are threadsafe. ** functions are threadsafe.
*/ */
int sqlite3PcacheInitialize(void) { return pcache2.xInit(pcache2.pArg); } int sqlite3PcacheInitialize(void){
void sqlite3PcacheShutdown(void) { return pcache2.xInit(pcache2.pArg);
if (pcache2.xShutdown) { }
void sqlite3PcacheShutdown(void){
if( pcache2.xShutdown ){
/* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */ /* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */
pcache2.xShutdown(pcache2.pArg); pcache2.xShutdown(pcache2.pArg);
} }
...@@ -269,12 +272,12 @@ void sqlite3PcacheShutdown(void) { ...@@ -269,12 +272,12 @@ void sqlite3PcacheShutdown(void) {
/* /*
** Return the size in bytes of a PCache object. ** Return the size in bytes of a PCache object.
*/ */
int sqlite3PcacheSize(void) { return sizeof(PCache); } int sqlite3PcacheSize(void){ return sizeof(PCache); }
/* /*
** Create a new PCache object. Storage space to hold the object ** Create a new PCache object. Storage space to hold the object
** has already been allocated and is passed in as the p pointer. ** has already been allocated and is passed in as the p pointer.
** The caller discovers how much space needs to be allocated by ** The caller discovers how much space needs to be allocated by
** calling sqlite3PcacheSize(). ** calling sqlite3PcacheSize().
** **
** szExtra is some extra space allocated for each page. The first ** szExtra is some extra space allocated for each page. The first
...@@ -283,24 +286,25 @@ int sqlite3PcacheSize(void) { return sizeof(PCache); } ...@@ -283,24 +286,25 @@ int sqlite3PcacheSize(void) { return sizeof(PCache); }
** to this module, the extra space really ends up being the MemPage ** to this module, the extra space really ends up being the MemPage
** structure in the pager. ** structure in the pager.
*/ */
int sqlite3PcacheOpen(int szPage, /* Size of every page */ int sqlite3PcacheOpen(
int szExtra, /* Extra space associated with each page */ int szPage, /* Size of every page */
int bPurgeable, /* True if pages are on backing store */ int szExtra, /* Extra space associated with each page */
int (*xStress)(void *, PgHdr *), /* Call to try to make pages clean */ int bPurgeable, /* True if pages are on backing store */
void * pStress, /* Argument to xStress */ int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */
PCache *p /* Preallocated space for the PCache */ void *pStress, /* Argument to xStress */
) { PCache *p /* Preallocated space for the PCache */
){
memset(p, 0, sizeof(PCache)); memset(p, 0, sizeof(PCache));
p->szPage = 1; p->szPage = 1;
p->szExtra = szExtra; p->szExtra = szExtra;
assert(szExtra >= 8); /* First 8 bytes will be zeroed */ assert( szExtra>=8 ); /* First 8 bytes will be zeroed */
p->bPurgeable = bPurgeable; p->bPurgeable = bPurgeable;
p->eCreate = 2; p->eCreate = 2;
p->xStress = xStress; p->xStress = xStress;
p->pStress = pStress; p->pStress = pStress;
p->szCache = 100; p->szCache = 100;
p->szSpill = 1; p->szSpill = 1;
pcacheTrace(("%p.OPEN szPage %d bPurgeable %d\n", p, szPage, bPurgeable)); pcacheTrace(("%p.OPEN szPage %d bPurgeable %d\n",p,szPage,bPurgeable));
return sqlite3PcacheSetPageSize(p, szPage); return sqlite3PcacheSetPageSize(p, szPage);
} }
...@@ -308,21 +312,24 @@ int sqlite3PcacheOpen(int szPage, /* Size of every page */ ...@@ -308,21 +312,24 @@ int sqlite3PcacheOpen(int szPage, /* Size of every page */
** Change the page size for PCache object. The caller must ensure that there ** Change the page size for PCache object. The caller must ensure that there
** are no outstanding page references when this function is called. ** are no outstanding page references when this function is called.
*/ */
int sqlite3PcacheSetPageSize(PCache *pCache, int szPage) { int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
assert(pCache->nRefSum == 0 && pCache->pDirty == 0); assert( pCache->nRefSum==0 && pCache->pDirty==0 );
if (pCache->szPage) { if( pCache->szPage ){
sqlite3_pcache *pNew; sqlite3_pcache *pNew;
pNew = pcache2.xCreate(szPage, pCache->szExtra + ROUND8(sizeof(PgHdr)), pCache->bPurgeable); pNew = pcache2.xCreate(
if (pNew == 0) return SQLITE_NOMEM_BKPT; szPage, pCache->szExtra + ROUND8(sizeof(PgHdr)),
pCache->bPurgeable
);
if( pNew==0 ) return SQLITE_NOMEM_BKPT;
pcache2.xCachesize(pNew, numberOfCachePages(pCache)); pcache2.xCachesize(pNew, numberOfCachePages(pCache));
if (pCache->pCache) { if( pCache->pCache ){
pcache2.xDestroy(pCache->pCache); pcache2.xDestroy(pCache->pCache);
} }
pCache->pCache = pNew; pCache->pCache = pNew;
pCache->szPage = szPage; pCache->szPage = szPage;
pcacheTrace(("%p.PAGESIZE %d\n", pCache, szPage)); pcacheTrace(("%p.PAGESIZE %d\n",pCache,szPage));
} }
return SQLITE_OK; return 0;
} }
/* /*
...@@ -349,17 +356,18 @@ int sqlite3PcacheSetPageSize(PCache *pCache, int szPage) { ...@@ -349,17 +356,18 @@ int sqlite3PcacheSetPageSize(PCache *pCache, int szPage) {
** the stack on entry and pop them back off on exit, which saves a ** the stack on entry and pop them back off on exit, which saves a
** lot of pushing and popping. ** lot of pushing and popping.
*/ */
sqlite3_pcache_page *sqlite3PcacheFetch(PCache *pCache, /* Obtain the page from this cache */ sqlite3_pcache_page *sqlite3PcacheFetch(
Pgno pgno, /* Page number to obtain */ PCache *pCache, /* Obtain the page from this cache */
int createFlag /* If true, create page if it does not exist already */ Pgno pgno, /* Page number to obtain */
) { int createFlag /* If true, create page if it does not exist already */
int eCreate; ){
int eCreate;
sqlite3_pcache_page *pRes; sqlite3_pcache_page *pRes;
assert(pCache != 0); assert( pCache!=0 );
assert(pCache->pCache != 0); assert( pCache->pCache!=0 );
assert(createFlag == 3 || createFlag == 0); assert( createFlag==3 || createFlag==0 );
assert(pCache->eCreate == ((pCache->bPurgeable && pCache->pDirty) ? 1 : 2)); assert( pCache->eCreate==((pCache->bPurgeable && pCache->pDirty) ? 1 : 2) );
/* eCreate defines what to do if the page does not exist. /* eCreate defines what to do if the page does not exist.
** 0 Do not allocate a new page. (createFlag==0) ** 0 Do not allocate a new page. (createFlag==0)
...@@ -369,18 +377,19 @@ sqlite3_pcache_page *sqlite3PcacheFetch(PCache *pCache, /* Obtain the page fr ...@@ -369,18 +377,19 @@ sqlite3_pcache_page *sqlite3PcacheFetch(PCache *pCache, /* Obtain the page fr
** (createFlag==1 AND !(bPurgeable AND pDirty) ** (createFlag==1 AND !(bPurgeable AND pDirty)
*/ */
eCreate = createFlag & pCache->eCreate; eCreate = createFlag & pCache->eCreate;
assert(eCreate == 0 || eCreate == 1 || eCreate == 2); assert( eCreate==0 || eCreate==1 || eCreate==2 );
assert(createFlag == 0 || pCache->eCreate == eCreate); assert( createFlag==0 || pCache->eCreate==eCreate );
assert(createFlag == 0 || eCreate == 1 + (!pCache->bPurgeable || !pCache->pDirty)); assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) );
pRes = pcache2.xFetch(pCache->pCache, pgno, eCreate); pRes = pcache2.xFetch(pCache->pCache, pgno, eCreate);
pcacheTrace(("%p.FETCH %d%s (result: %p)\n", pCache, pgno, createFlag ? " create" : "", pRes)); pcacheTrace(("%p.FETCH %d%s (result: %p)\n",pCache,pgno,
createFlag?" create":"",pRes));
return pRes; return pRes;
} }
/* /*
** If the sqlite3PcacheFetch() routine is unable to allocate a new ** If the sqlite3PcacheFetch() routine is unable to allocate a new
** page because no clean pages are available for reuse and the cache ** page because no clean pages are available for reuse and the cache
** size limit has been reached, then this routine can be invoked to ** size limit has been reached, then this routine can be invoked to
** try harder to allocate a page. This routine might invoke the stress ** try harder to allocate a page. This routine might invoke the stress
** callback to spill dirty pages to the journal. It will then try to ** callback to spill dirty pages to the journal. It will then try to
** allocate the new page and will only fail to allocate a new page on ** allocate the new page and will only fail to allocate a new page on
...@@ -388,46 +397,51 @@ sqlite3_pcache_page *sqlite3PcacheFetch(PCache *pCache, /* Obtain the page fr ...@@ -388,46 +397,51 @@ sqlite3_pcache_page *sqlite3PcacheFetch(PCache *pCache, /* Obtain the page fr
** **
** This routine should be invoked only after sqlite3PcacheFetch() fails. ** This routine should be invoked only after sqlite3PcacheFetch() fails.
*/ */
int sqlite3PcacheFetchStress(PCache * pCache, /* Obtain the page from this cache */ int sqlite3PcacheFetchStress(
Pgno pgno, /* Page number to obtain */ PCache *pCache, /* Obtain the page from this cache */
sqlite3_pcache_page **ppPage /* Write result here */ Pgno pgno, /* Page number to obtain */
) { sqlite3_pcache_page **ppPage /* Write result here */
){
PgHdr *pPg; PgHdr *pPg;
if (pCache->eCreate == 2) return 0; if( pCache->eCreate==2 ) return 0;
if (sqlite3PcachePagecount(pCache) > pCache->szSpill) { if( sqlite3PcachePagecount(pCache)>pCache->szSpill ){
/* Find a dirty page to write-out and recycle. First try to find a /* Find a dirty page to write-out and recycle. First try to find a
** page that does not require a journal-sync (one with PGHDR_NEED_SYNC ** page that does not require a journal-sync (one with PGHDR_NEED_SYNC
** cleared), but if that is not possible settle for any other ** cleared), but if that is not possible settle for any other
** unreferenced dirty page. ** unreferenced dirty page.
** **
** If the LRU page in the dirty list that has a clear PGHDR_NEED_SYNC ** If the LRU page in the dirty list that has a clear PGHDR_NEED_SYNC
** flag is currently referenced, then the following may leave pSynced ** flag is currently referenced, then the following may leave pSynced
** set incorrectly (pointing to other than the LRU page with NEED_SYNC ** set incorrectly (pointing to other than the LRU page with NEED_SYNC
** cleared). This is Ok, as pSynced is just an optimization. */ ** cleared). This is Ok, as pSynced is just an optimization. */
for (pPg = pCache->pSynced; pPg && (pPg->nRef || (pPg->flags & PGHDR_NEED_SYNC)); pPg = pPg->pDirtyPrev) for(pPg=pCache->pSynced;
; pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC));
pPg=pPg->pDirtyPrev
);
pCache->pSynced = pPg; pCache->pSynced = pPg;
if (!pPg) { if( !pPg ){
for (pPg = pCache->pDirtyTail; pPg && pPg->nRef; pPg = pPg->pDirtyPrev) for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev);
;
} }
if (pPg) { if( pPg ){
int rc; int rc;
#ifdef SQLITE_LOG_CACHE_SPILL #ifdef SQLITE_LOG_CACHE_SPILL
sqlite3_log(SQLITE_FULL, "spill page %d making room for %d - cache used: %d/%d", pPg->pgno, pgno, sqlite3_log(SQLITE_FULL,
pcache2.xPagecount(pCache->pCache), numberOfCachePages(pCache)); "spill page %d making room for %d - cache used: %d/%d",
pPg->pgno, pgno,
pcache2.xPagecount(pCache->pCache),
numberOfCachePages(pCache));
#endif #endif
pcacheTrace(("%p.SPILL %d\n", pCache, pPg->pgno)); pcacheTrace(("%p.SPILL %d\n",pCache,pPg->pgno));
rc = pCache->xStress(pCache->pStress, pPg); rc = pCache->xStress(pCache->pStress, pPg);
pcacheDump(pCache); pcacheDump(pCache);
if (rc != SQLITE_OK && rc != SQLITE_BUSY) { if( rc!=0 && rc!=SQLITE_BUSY ){
return rc; return rc;
} }
} }
} }
*ppPage = pcache2.xFetch(pCache->pCache, pgno, 2); *ppPage = pcache2.xFetch(pCache->pCache, pgno, 2);
return *ppPage == 0 ? SQLITE_NOMEM_BKPT : SQLITE_OK; return *ppPage==0 ? SQLITE_NOMEM_BKPT : 0;
} }
/* /*
...@@ -440,15 +454,15 @@ int sqlite3PcacheFetchStress(PCache * pCache, /* Obtain the page fr ...@@ -440,15 +454,15 @@ int sqlite3PcacheFetchStress(PCache * pCache, /* Obtain the page fr
** case. ** case.
*/ */
static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit( static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit(
PCache * pCache, /* Obtain the page from this cache */ PCache *pCache, /* Obtain the page from this cache */
Pgno pgno, /* Page number obtained */ Pgno pgno, /* Page number obtained */
sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */ sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */
) { ){
PgHdr *pPgHdr; PgHdr *pPgHdr;
assert(pPage != 0); assert( pPage!=0 );
pPgHdr = (PgHdr *)pPage->pExtra; pPgHdr = (PgHdr*)pPage->pExtra;
assert(pPgHdr->pPage == 0); assert( pPgHdr->pPage==0 );
memset(&pPgHdr->pDirty, 0, sizeof(PgHdr) - offsetof(PgHdr, pDirty)); memset(&pPgHdr->pDirty, 0, sizeof(PgHdr) - offsetof(PgHdr,pDirty));
pPgHdr->pPage = pPage; pPgHdr->pPage = pPage;
pPgHdr->pData = pPage->pBuf; pPgHdr->pData = pPage->pBuf;
pPgHdr->pExtra = (void *)&pPgHdr[1]; pPgHdr->pExtra = (void *)&pPgHdr[1];
...@@ -456,7 +470,7 @@ static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit( ...@@ -456,7 +470,7 @@ static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit(
pPgHdr->pCache = pCache; pPgHdr->pCache = pCache;
pPgHdr->pgno = pgno; pPgHdr->pgno = pgno;
pPgHdr->flags = PGHDR_CLEAN; pPgHdr->flags = PGHDR_CLEAN;
return sqlite3PcacheFetchFinish(pCache, pgno, pPage); return sqlite3PcacheFetchFinish(pCache,pgno,pPage);
} }
/* /*
...@@ -465,21 +479,22 @@ static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit( ...@@ -465,21 +479,22 @@ static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit(
** must be called after sqlite3PcacheFetch() in order to get a usable ** must be called after sqlite3PcacheFetch() in order to get a usable
** result. ** result.
*/ */
PgHdr *sqlite3PcacheFetchFinish(PCache * pCache, /* Obtain the page from this cache */ PgHdr *sqlite3PcacheFetchFinish(
Pgno pgno, /* Page number obtained */ PCache *pCache, /* Obtain the page from this cache */
sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */ Pgno pgno, /* Page number obtained */
) { sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */
){
PgHdr *pPgHdr; PgHdr *pPgHdr;
assert(pPage != 0); assert( pPage!=0 );
pPgHdr = (PgHdr *)pPage->pExtra; pPgHdr = (PgHdr *)pPage->pExtra;
if (!pPgHdr->pPage) { if( !pPgHdr->pPage ){
return pcacheFetchFinishWithInit(pCache, pgno, pPage); return pcacheFetchFinishWithInit(pCache, pgno, pPage);
} }
pCache->nRefSum++; pCache->nRefSum++;
pPgHdr->nRef++; pPgHdr->nRef++;
assert(sqlite3PcachePageSanity(pPgHdr)); assert( sqlite3PcachePageSanity(pPgHdr) );
return pPgHdr; return pPgHdr;
} }
...@@ -487,13 +502,13 @@ PgHdr *sqlite3PcacheFetchFinish(PCache * pCache, /* Obtain the page ...@@ -487,13 +502,13 @@ PgHdr *sqlite3PcacheFetchFinish(PCache * pCache, /* Obtain the page
** Decrement the reference count on a page. If the page is clean and the ** Decrement the reference count on a page. If the page is clean and the
** reference count drops to 0, then it is made eligible for recycling. ** reference count drops to 0, then it is made eligible for recycling.
*/ */
void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p) { void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){
assert(p->nRef > 0); assert( p->nRef>0 );
p->pCache->nRefSum--; p->pCache->nRefSum--;
if ((--p->nRef) == 0) { if( (--p->nRef)==0 ){
if (p->flags & PGHDR_CLEAN) { if( p->flags&PGHDR_CLEAN ){
pcacheUnpin(p); pcacheUnpin(p);
} else { }else{
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
} }
} }
...@@ -502,9 +517,9 @@ void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p) { ...@@ -502,9 +517,9 @@ void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p) {
/* /*
** Increase the reference count of a supplied page by 1. ** Increase the reference count of a supplied page by 1.
*/ */
void sqlite3PcacheRef(PgHdr *p) { void sqlite3PcacheRef(PgHdr *p){
assert(p->nRef > 0); assert(p->nRef>0);
assert(sqlite3PcachePageSanity(p)); assert( sqlite3PcachePageSanity(p) );
p->nRef++; p->nRef++;
p->pCache->nRefSum++; p->pCache->nRefSum++;
} }
...@@ -514,10 +529,10 @@ void sqlite3PcacheRef(PgHdr *p) { ...@@ -514,10 +529,10 @@ void sqlite3PcacheRef(PgHdr *p) {
** page. This function deletes that reference, so after it returns the ** page. This function deletes that reference, so after it returns the
** page pointed to by p is invalid. ** page pointed to by p is invalid.
*/ */
void sqlite3PcacheDrop(PgHdr *p) { void sqlite3PcacheDrop(PgHdr *p){
assert(p->nRef == 1); assert( p->nRef==1 );
assert(sqlite3PcachePageSanity(p)); assert( sqlite3PcachePageSanity(p) );
if (p->flags & PGHDR_DIRTY) { if( p->flags&PGHDR_DIRTY ){
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE); pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
} }
p->pCache->nRefSum--; p->pCache->nRefSum--;
...@@ -528,18 +543,18 @@ void sqlite3PcacheDrop(PgHdr *p) { ...@@ -528,18 +543,18 @@ void sqlite3PcacheDrop(PgHdr *p) {
** Make sure the page is marked as dirty. If it isn't dirty already, ** Make sure the page is marked as dirty. If it isn't dirty already,
** make it so. ** make it so.
*/ */
void sqlite3PcacheMakeDirty(PgHdr *p) { void sqlite3PcacheMakeDirty(PgHdr *p){
assert(p->nRef > 0); assert( p->nRef>0 );
assert(sqlite3PcachePageSanity(p)); assert( sqlite3PcachePageSanity(p) );
if (p->flags & (PGHDR_CLEAN | PGHDR_DONT_WRITE)) { /*OPTIMIZATION-IF-FALSE*/ if( p->flags & (PGHDR_CLEAN|PGHDR_DONT_WRITE) ){ /*OPTIMIZATION-IF-FALSE*/
p->flags &= ~PGHDR_DONT_WRITE; p->flags &= ~PGHDR_DONT_WRITE;
if (p->flags & PGHDR_CLEAN) { if( p->flags & PGHDR_CLEAN ){
p->flags ^= (PGHDR_DIRTY | PGHDR_CLEAN); p->flags ^= (PGHDR_DIRTY|PGHDR_CLEAN);
pcacheTrace(("%p.DIRTY %d\n", p->pCache, p->pgno)); pcacheTrace(("%p.DIRTY %d\n",p->pCache,p->pgno));
assert((p->flags & (PGHDR_DIRTY | PGHDR_CLEAN)) == PGHDR_DIRTY); assert( (p->flags & (PGHDR_DIRTY|PGHDR_CLEAN))==PGHDR_DIRTY );
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD); pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD);
} }
assert(sqlite3PcachePageSanity(p)); assert( sqlite3PcachePageSanity(p) );
} }
} }
...@@ -547,16 +562,16 @@ void sqlite3PcacheMakeDirty(PgHdr *p) { ...@@ -547,16 +562,16 @@ void sqlite3PcacheMakeDirty(PgHdr *p) {
** Make sure the page is marked as clean. If it isn't clean already, ** Make sure the page is marked as clean. If it isn't clean already,
** make it so. ** make it so.
*/ */
void sqlite3PcacheMakeClean(PgHdr *p) { void sqlite3PcacheMakeClean(PgHdr *p){
assert(sqlite3PcachePageSanity(p)); assert( sqlite3PcachePageSanity(p) );
assert((p->flags & PGHDR_DIRTY) != 0); assert( (p->flags & PGHDR_DIRTY)!=0 );
assert((p->flags & PGHDR_CLEAN) == 0); assert( (p->flags & PGHDR_CLEAN)==0 );
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE); pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
p->flags &= ~(PGHDR_DIRTY | PGHDR_NEED_SYNC | PGHDR_WRITEABLE); p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC|PGHDR_WRITEABLE);
p->flags |= PGHDR_CLEAN; p->flags |= PGHDR_CLEAN;
pcacheTrace(("%p.CLEAN %d\n", p->pCache, p->pgno)); pcacheTrace(("%p.CLEAN %d\n",p->pCache,p->pgno));
assert(sqlite3PcachePageSanity(p)); assert( sqlite3PcachePageSanity(p) );
if (p->nRef == 0) { if( p->nRef==0 ){
pcacheUnpin(p); pcacheUnpin(p);
} }
} }
...@@ -564,10 +579,10 @@ void sqlite3PcacheMakeClean(PgHdr *p) { ...@@ -564,10 +579,10 @@ void sqlite3PcacheMakeClean(PgHdr *p) {
/* /*
** Make every page in the cache clean. ** Make every page in the cache clean.
*/ */
void sqlite3PcacheCleanAll(PCache *pCache) { void sqlite3PcacheCleanAll(PCache *pCache){
PgHdr *p; PgHdr *p;
pcacheTrace(("%p.CLEAN-ALL\n", pCache)); pcacheTrace(("%p.CLEAN-ALL\n",pCache));
while ((p = pCache->pDirty) != 0) { while( (p = pCache->pDirty)!=0 ){
sqlite3PcacheMakeClean(p); sqlite3PcacheMakeClean(p);
} }
} }
...@@ -575,11 +590,11 @@ void sqlite3PcacheCleanAll(PCache *pCache) { ...@@ -575,11 +590,11 @@ void sqlite3PcacheCleanAll(PCache *pCache) {
/* /*
** Clear the PGHDR_NEED_SYNC and PGHDR_WRITEABLE flag from all dirty pages. ** Clear the PGHDR_NEED_SYNC and PGHDR_WRITEABLE flag from all dirty pages.
*/ */
void sqlite3PcacheClearWritable(PCache *pCache) { void sqlite3PcacheClearWritable(PCache *pCache){
PgHdr *p; PgHdr *p;
pcacheTrace(("%p.CLEAR-WRITEABLE\n", pCache)); pcacheTrace(("%p.CLEAR-WRITEABLE\n",pCache));
for (p = pCache->pDirty; p; p = p->pDirtyNext) { for(p=pCache->pDirty; p; p=p->pDirtyNext){
p->flags &= ~(PGHDR_NEED_SYNC | PGHDR_WRITEABLE); p->flags &= ~(PGHDR_NEED_SYNC|PGHDR_WRITEABLE);
} }
pCache->pSynced = pCache->pDirtyTail; pCache->pSynced = pCache->pDirtyTail;
} }
...@@ -587,26 +602,26 @@ void sqlite3PcacheClearWritable(PCache *pCache) { ...@@ -587,26 +602,26 @@ void sqlite3PcacheClearWritable(PCache *pCache) {
/* /*
** Clear the PGHDR_NEED_SYNC flag from all dirty pages. ** Clear the PGHDR_NEED_SYNC flag from all dirty pages.
*/ */
void sqlite3PcacheClearSyncFlags(PCache *pCache) { void sqlite3PcacheClearSyncFlags(PCache *pCache){
PgHdr *p; PgHdr *p;
for (p = pCache->pDirty; p; p = p->pDirtyNext) { for(p=pCache->pDirty; p; p=p->pDirtyNext){
p->flags &= ~PGHDR_NEED_SYNC; p->flags &= ~PGHDR_NEED_SYNC;
} }
pCache->pSynced = pCache->pDirtyTail; pCache->pSynced = pCache->pDirtyTail;
} }
/* /*
** Change the page number of page p to newPgno. ** Change the page number of page p to newPgno.
*/ */
void sqlite3PcacheMove(PgHdr *p, Pgno newPgno) { void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
PCache *pCache = p->pCache; PCache *pCache = p->pCache;
assert(p->nRef > 0); assert( p->nRef>0 );
assert(newPgno > 0); assert( newPgno>0 );
assert(sqlite3PcachePageSanity(p)); assert( sqlite3PcachePageSanity(p) );
pcacheTrace(("%p.MOVE %d -> %d\n", pCache, p->pgno, newPgno)); pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno));
pcache2.xRekey(pCache->pCache, p->pPage, p->pgno, newPgno); pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno);
p->pgno = newPgno; p->pgno = newPgno;
if ((p->flags & PGHDR_DIRTY) && (p->flags & PGHDR_NEED_SYNC)) { if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
} }
} }
...@@ -620,72 +635,74 @@ void sqlite3PcacheMove(PgHdr *p, Pgno newPgno) { ...@@ -620,72 +635,74 @@ void sqlite3PcacheMove(PgHdr *p, Pgno newPgno) {
** function is 0, then the data area associated with page 1 is zeroed, but ** function is 0, then the data area associated with page 1 is zeroed, but
** the page object is not dropped. ** the page object is not dropped.
*/ */
void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno) { void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
if (pCache->pCache) { if( pCache->pCache ){
PgHdr *p; PgHdr *p;
PgHdr *pNext; PgHdr *pNext;
pcacheTrace(("%p.TRUNCATE %d\n", pCache, pgno)); pcacheTrace(("%p.TRUNCATE %d\n",pCache,pgno));
for (p = pCache->pDirty; p; p = pNext) { for(p=pCache->pDirty; p; p=pNext){
pNext = p->pDirtyNext; pNext = p->pDirtyNext;
/* This routine never gets call with a positive pgno except right /* This routine never gets call with a positive pgno except right
** after sqlite3PcacheCleanAll(). So if there are dirty pages, ** after sqlite3PcacheCleanAll(). So if there are dirty pages,
** it must be that pgno==0. ** it must be that pgno==0.
*/ */
assert(p->pgno > 0); assert( p->pgno>0 );
if (p->pgno > pgno) { if( p->pgno>pgno ){
assert(p->flags & PGHDR_DIRTY); assert( p->flags&PGHDR_DIRTY );
sqlite3PcacheMakeClean(p); sqlite3PcacheMakeClean(p);
} }
} }
if (pgno == 0 && pCache->nRefSum) { if( pgno==0 && pCache->nRefSum ){
sqlite3_pcache_page *pPage1; sqlite3_pcache_page *pPage1;
pPage1 = pcache2.xFetch(pCache->pCache, 1, 0); pPage1 = pcache2.xFetch(pCache->pCache,1,0);
if (ALWAYS(pPage1)) { /* Page 1 is always available in cache, because if( ALWAYS(pPage1) ){ /* Page 1 is always available in cache, because
** pCache->nRefSum>0 */ ** pCache->nRefSum>0 */
memset(pPage1->pBuf, 0, pCache->szPage); memset(pPage1->pBuf, 0, pCache->szPage);
pgno = 1; pgno = 1;
} }
} }
pcache2.xTruncate(pCache->pCache, pgno + 1); pcache2.xTruncate(pCache->pCache, pgno+1);
} }
} }
/* /*
** Close a cache. ** Close a cache.
*/ */
void sqlite3PcacheClose(PCache *pCache) { void sqlite3PcacheClose(PCache *pCache){
assert(pCache->pCache != 0); assert( pCache->pCache!=0 );
pcacheTrace(("%p.CLOSE\n", pCache)); pcacheTrace(("%p.CLOSE\n",pCache));
pcache2.xDestroy(pCache->pCache); pcache2.xDestroy(pCache->pCache);
} }
/* /*
** Discard the contents of the cache. ** Discard the contents of the cache.
*/ */
void sqlite3PcacheClear(PCache *pCache) { sqlite3PcacheTruncate(pCache, 0); } void sqlite3PcacheClear(PCache *pCache){
sqlite3PcacheTruncate(pCache, 0);
}
/* /*
** Merge two lists of pages connected by pDirty and in pgno order. ** Merge two lists of pages connected by pDirty and in pgno order.
** Do not bother fixing the pDirtyPrev pointers. ** Do not bother fixing the pDirtyPrev pointers.
*/ */
static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB) { static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){
PgHdr result, *pTail; PgHdr result, *pTail;
pTail = &result; pTail = &result;
assert(pA != 0 && pB != 0); assert( pA!=0 && pB!=0 );
for (;;) { for(;;){
if (pA->pgno < pB->pgno) { if( pA->pgno<pB->pgno ){
pTail->pDirty = pA; pTail->pDirty = pA;
pTail = pA; pTail = pA;
pA = pA->pDirty; pA = pA->pDirty;
if (pA == 0) { if( pA==0 ){
pTail->pDirty = pB; pTail->pDirty = pB;
break; break;
} }
} else { }else{
pTail->pDirty = pB; pTail->pDirty = pB;
pTail = pB; pTail = pB;
pB = pB->pDirty; pB = pB->pDirty;
if (pB == 0) { if( pB==0 ){
pTail->pDirty = pA; pTail->pDirty = pA;
break; break;
} }
...@@ -704,25 +721,25 @@ static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB) { ...@@ -704,25 +721,25 @@ static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB) {
** One extra bucket is added to catch overflow in case something ** One extra bucket is added to catch overflow in case something
** ever changes to make the previous sentence incorrect. ** ever changes to make the previous sentence incorrect.
*/ */
#define N_SORT_BUCKET 32 #define N_SORT_BUCKET 32
static PgHdr *pcacheSortDirtyList(PgHdr *pIn) { static PgHdr *pcacheSortDirtyList(PgHdr *pIn){
PgHdr *a[N_SORT_BUCKET], *p; PgHdr *a[N_SORT_BUCKET], *p;
int i; int i;
memset(a, 0, sizeof(a)); memset(a, 0, sizeof(a));
while (pIn) { while( pIn ){
p = pIn; p = pIn;
pIn = p->pDirty; pIn = p->pDirty;
p->pDirty = 0; p->pDirty = 0;
for (i = 0; ALWAYS(i < N_SORT_BUCKET - 1); i++) { for(i=0; ALWAYS(i<N_SORT_BUCKET-1); i++){
if (a[i] == 0) { if( a[i]==0 ){
a[i] = p; a[i] = p;
break; break;
} else { }else{
p = pcacheMergeDirtyList(a[i], p); p = pcacheMergeDirtyList(a[i], p);
a[i] = 0; a[i] = 0;
} }
} }
if (NEVER(i == N_SORT_BUCKET - 1)) { if( NEVER(i==N_SORT_BUCKET-1) ){
/* To get here, there need to be 2^(N_SORT_BUCKET) elements in /* To get here, there need to be 2^(N_SORT_BUCKET) elements in
** the input list. But that is impossible. ** the input list. But that is impossible.
*/ */
...@@ -730,8 +747,8 @@ static PgHdr *pcacheSortDirtyList(PgHdr *pIn) { ...@@ -730,8 +747,8 @@ static PgHdr *pcacheSortDirtyList(PgHdr *pIn) {
} }
} }
p = a[0]; p = a[0];
for (i = 1; i < N_SORT_BUCKET; i++) { for(i=1; i<N_SORT_BUCKET; i++){
if (a[i] == 0) continue; if( a[i]==0 ) continue;
p = p ? pcacheMergeDirtyList(p, a[i]) : a[i]; p = p ? pcacheMergeDirtyList(p, a[i]) : a[i];
} }
return p; return p;
...@@ -740,32 +757,36 @@ static PgHdr *pcacheSortDirtyList(PgHdr *pIn) { ...@@ -740,32 +757,36 @@ static PgHdr *pcacheSortDirtyList(PgHdr *pIn) {
/* /*
** Return a list of all dirty pages in the cache, sorted by page number. ** Return a list of all dirty pages in the cache, sorted by page number.
*/ */
PgHdr *sqlite3PcacheDirtyList(PCache *pCache) { PgHdr *sqlite3PcacheDirtyList(PCache *pCache){
PgHdr *p; PgHdr *p;
for (p = pCache->pDirty; p; p = p->pDirtyNext) { for(p=pCache->pDirty; p; p=p->pDirtyNext){
p->pDirty = p->pDirtyNext; p->pDirty = p->pDirtyNext;
} }
return pcacheSortDirtyList(pCache->pDirty); return pcacheSortDirtyList(pCache->pDirty);
} }
/* /*
** Return the total number of references to all pages held by the cache. ** Return the total number of references to all pages held by the cache.
** **
** This is not the total number of pages referenced, but the sum of the ** This is not the total number of pages referenced, but the sum of the
** reference count for all pages. ** reference count for all pages.
*/ */
int sqlite3PcacheRefCount(PCache *pCache) { return pCache->nRefSum; } int sqlite3PcacheRefCount(PCache *pCache){
return pCache->nRefSum;
}
/* /*
** Return the number of references to the page supplied as an argument. ** Return the number of references to the page supplied as an argument.
*/ */
int sqlite3PcachePageRefcount(PgHdr *p) { return p->nRef; } int sqlite3PcachePageRefcount(PgHdr *p){
return p->nRef;
}
/* /*
** Return the total number of pages in the cache. ** Return the total number of pages in the cache.
*/ */
int sqlite3PcachePagecount(PCache *pCache) { int sqlite3PcachePagecount(PCache *pCache){
assert(pCache->pCache != 0); assert( pCache->pCache!=0 );
return pcache2.xPagecount(pCache->pCache); return pcache2.xPagecount(pCache->pCache);
} }
...@@ -773,16 +794,19 @@ int sqlite3PcachePagecount(PCache *pCache) { ...@@ -773,16 +794,19 @@ int sqlite3PcachePagecount(PCache *pCache) {
/* /*
** Get the suggested cache-size value. ** Get the suggested cache-size value.
*/ */
int sqlite3PcacheGetCachesize(PCache *pCache) { return numberOfCachePages(pCache); } int sqlite3PcacheGetCachesize(PCache *pCache){
return numberOfCachePages(pCache);
}
#endif #endif
/* /*
** Set the suggested cache-size value. ** Set the suggested cache-size value.
*/ */
void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage) { void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){
assert(pCache->pCache != 0); assert( pCache->pCache!=0 );
pCache->szCache = mxPage; pCache->szCache = mxPage;
pcache2.xCachesize(pCache->pCache, numberOfCachePages(pCache)); pcache2.xCachesize(pCache->pCache,
numberOfCachePages(pCache));
} }
/* /*
...@@ -790,25 +814,25 @@ void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage) { ...@@ -790,25 +814,25 @@ void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage) {
** argument is zero. Return the effective cache-spill size, which will ** argument is zero. Return the effective cache-spill size, which will
** be the larger of the szSpill and szCache. ** be the larger of the szSpill and szCache.
*/ */
int sqlite3PcacheSetSpillsize(PCache *p, int mxPage) { int sqlite3PcacheSetSpillsize(PCache *p, int mxPage){
int res; int res;
assert(p->pCache != 0); assert( p->pCache!=0 );
if (mxPage) { if( mxPage ){
if (mxPage < 0) { if( mxPage<0 ){
mxPage = (int)((-1024 * (i64)mxPage) / (p->szPage + p->szExtra)); mxPage = (int)((-1024*(i64)mxPage)/(p->szPage+p->szExtra));
} }
p->szSpill = mxPage; p->szSpill = mxPage;
} }
res = numberOfCachePages(p); res = numberOfCachePages(p);
if (res < p->szSpill) res = p->szSpill; if( res<p->szSpill ) res = p->szSpill;
return res; return res;
} }
/* /*
** Free up as much memory as possible from the page cache. ** Free up as much memory as possible from the page cache.
*/ */
void sqlite3PcacheShrink(PCache *pCache) { void sqlite3PcacheShrink(PCache *pCache){
assert(pCache->pCache != 0); assert( pCache->pCache!=0 );
pcache2.xShrink(pCache->pCache); pcache2.xShrink(pCache->pCache);
} }
...@@ -816,25 +840,27 @@ void sqlite3PcacheShrink(PCache *pCache) { ...@@ -816,25 +840,27 @@ void sqlite3PcacheShrink(PCache *pCache) {
** Return the size of the header added by this middleware layer ** Return the size of the header added by this middleware layer
** in the page-cache hierarchy. ** in the page-cache hierarchy.
*/ */
int sqlite3HeaderSizePcache(void) { return ROUND8(sizeof(PgHdr)); } int sqlite3HeaderSizePcache(void){ return ROUND8(sizeof(PgHdr)); }
/* /*
** Return the number of dirty pages currently in the cache, as a percentage ** Return the number of dirty pages currently in the cache, as a percentage
** of the configured cache size. ** of the configured cache size.
*/ */
int sqlite3PCachePercentDirty(PCache *pCache) { int sqlite3PCachePercentDirty(PCache *pCache){
PgHdr *pDirty; PgHdr *pDirty;
int nDirty = 0; int nDirty = 0;
int nCache = numberOfCachePages(pCache); int nCache = numberOfCachePages(pCache);
for (pDirty = pCache->pDirty; pDirty; pDirty = pDirty->pDirtyNext) nDirty++; for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext) nDirty++;
return nCache ? (int)(((i64)nDirty * 100) / nCache) : 0; return nCache ? (int)(((i64)nDirty * 100) / nCache) : 0;
} }
#ifdef SQLITE_DIRECT_OVERFLOW_READ #ifdef SQLITE_DIRECT_OVERFLOW_READ
/* /*
** Return true if there are one or more dirty pages in the cache. Else false. ** Return true if there are one or more dirty pages in the cache. Else false.
*/ */
int sqlite3PCacheIsDirty(PCache *pCache) { return (pCache->pDirty != 0); } int sqlite3PCacheIsDirty(PCache *pCache){
return (pCache->pDirty!=0);
}
#endif #endif
#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) #if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG)
...@@ -843,9 +869,9 @@ int sqlite3PCacheIsDirty(PCache *pCache) { return (pCache->pDirty != 0); } ...@@ -843,9 +869,9 @@ int sqlite3PCacheIsDirty(PCache *pCache) { return (pCache->pDirty != 0); }
** callback. This is only used if the SQLITE_CHECK_PAGES macro is ** callback. This is only used if the SQLITE_CHECK_PAGES macro is
** defined. ** defined.
*/ */
void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)) { void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)){
PgHdr *pDirty; PgHdr *pDirty;
for (pDirty = pCache->pDirty; pDirty; pDirty = pDirty->pDirtyNext) { for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext){
xIter(pDirty); xIter(pDirty);
} }
} }
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
** **
** The third case is a chunk of heap memory (defaulting to 100 pages worth) ** The third case is a chunk of heap memory (defaulting to 100 pages worth)
** that is allocated when the page cache is created. The size of the local ** that is allocated when the page cache is created. The size of the local
** bulk allocation can be adjusted using ** bulk allocation can be adjusted using
** **
** sqlite3_config(SQLITE_CONFIG_PAGECACHE, (void*)0, 0, N). ** sqlite3_config(SQLITE_CONFIG_PAGECACHE, (void*)0, 0, N).
** **
...@@ -82,47 +82,47 @@ ...@@ -82,47 +82,47 @@
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
typedef struct PCache1 PCache1; typedef struct PCache1 PCache1;
typedef struct PgHdr1 PgHdr1; typedef struct PgHdr1 PgHdr1;
typedef struct PgFreeslot PgFreeslot; typedef struct PgFreeslot PgFreeslot;
typedef struct PGroup PGroup; typedef struct PGroup PGroup;
/* /*
** Each cache entry is represented by an instance of the following ** Each cache entry is represented by an instance of the following
** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of ** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of
** PgHdr1.pCache->szPage bytes is allocated directly before this structure ** PgHdr1.pCache->szPage bytes is allocated directly before this structure
** in memory. ** in memory.
** **
** Note: Variables isBulkLocal and isAnchor were once type "u8". That works, ** Note: Variables isBulkLocal and isAnchor were once type "u8". That works,
** but causes a 2-byte gap in the structure for most architectures (since ** but causes a 2-byte gap in the structure for most architectures (since
** pointers must be either 4 or 8-byte aligned). As this structure is located ** pointers must be either 4 or 8-byte aligned). As this structure is located
** in memory directly after the associated page data, if the database is ** in memory directly after the associated page data, if the database is
** corrupt, code at the b-tree layer may overread the page buffer and ** corrupt, code at the b-tree layer may overread the page buffer and
** read part of this structure before the corruption is detected. This ** read part of this structure before the corruption is detected. This
** can cause a valgrind error if the unitialized gap is accessed. Using u16 ** can cause a valgrind error if the unitialized gap is accessed. Using u16
** ensures there is no such gap, and therefore no bytes of unitialized memory ** ensures there is no such gap, and therefore no bytes of unitialized memory
** in the structure. ** in the structure.
*/ */
struct PgHdr1 { struct PgHdr1 {
sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */ sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */
unsigned int iKey; /* Key value (page number) */ unsigned int iKey; /* Key value (page number) */
u16 isBulkLocal; /* This page from bulk local storage */ u16 isBulkLocal; /* This page from bulk local storage */
u16 isAnchor; /* This is the PGroup.lru element */ u16 isAnchor; /* This is the PGroup.lru element */
PgHdr1 *pNext; /* Next in hash table chain */ PgHdr1 * pNext; /* Next in hash table chain */
PCache1 *pCache; /* Cache that currently owns this page */ PCache1 * pCache; /* Cache that currently owns this page */
PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ PgHdr1 * pLruNext; /* Next in LRU list of unpinned pages */
PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ PgHdr1 * pLruPrev; /* Previous in LRU list of unpinned pages */
/* NB: pLruPrev is only valid if pLruNext!=0 */ /* NB: pLruPrev is only valid if pLruNext!=0 */
}; };
/* /*
** A page is pinned if it is not on the LRU list. To be "pinned" means ** A page is pinned if it is not on the LRU list. To be "pinned" means
** that the page is in active use and must not be deallocated. ** that the page is in active use and must not be deallocated.
*/ */
#define PAGE_IS_PINNED(p) ((p)->pLruNext==0) #define PAGE_IS_PINNED(p) ((p)->pLruNext == 0)
#define PAGE_IS_UNPINNED(p) ((p)->pLruNext!=0) #define PAGE_IS_UNPINNED(p) ((p)->pLruNext != 0)
/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set /* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
** of one or more PCaches that are able to recycle each other's unpinned ** of one or more PCaches that are able to recycle each other's unpinned
** pages when they are under memory pressure. A PGroup is an instance of ** pages when they are under memory pressure. A PGroup is an instance of
** the following object. ** the following object.
...@@ -145,12 +145,12 @@ struct PgHdr1 { ...@@ -145,12 +145,12 @@ struct PgHdr1 {
** SQLITE_MUTEX_STATIC_LRU. ** SQLITE_MUTEX_STATIC_LRU.
*/ */
struct PGroup { struct PGroup {
sqlite3_mutex *mutex; /* MUTEX_STATIC_LRU or NULL */ pthread_mutex_t mutex; /* MUTEX_STATIC_LRU or NULL */
unsigned int nMaxPage; /* Sum of nMax for purgeable caches */ unsigned int nMaxPage; /* Sum of nMax for purgeable caches */
unsigned int nMinPage; /* Sum of nMin for purgeable caches */ unsigned int nMinPage; /* Sum of nMin for purgeable caches */
unsigned int mxPinned; /* nMaxpage + 10 - nMinPage */ unsigned int mxPinned; /* nMaxpage + 10 - nMinPage */
unsigned int nPurgeable; /* Number of purgeable pages allocated */ unsigned int nPurgeable; /* Number of purgeable pages allocated */
PgHdr1 lru; /* The beginning and end of the LRU list */ PgHdr1 lru; /* The beginning and end of the LRU list */
}; };
/* Each page cache is an instance of the following object. Every /* Each page cache is an instance of the following object. Every
...@@ -158,37 +158,37 @@ struct PGroup { ...@@ -158,37 +158,37 @@ struct PGroup {
** temporary or transient database) has a single page cache which ** temporary or transient database) has a single page cache which
** is an instance of this object. ** is an instance of this object.
** **
** Pointers to structures of this type are cast and returned as ** Pointers to structures of this type are cast and returned as
** opaque sqlite3_pcache* handles. ** opaque sqlite3_pcache* handles.
*/ */
struct PCache1 { struct PCache1 {
/* Cache configuration parameters. Page size (szPage) and the purgeable /* Cache configuration parameters. Page size (szPage) and the purgeable
** flag (bPurgeable) and the pnPurgeable pointer are all set when the ** flag (bPurgeable) and the pnPurgeable pointer are all set when the
** cache is created and are never changed thereafter. nMax may be ** cache is created and are never changed thereafter. nMax may be
** modified at any time by a call to the pcache1Cachesize() method. ** modified at any time by a call to the pcache1Cachesize() method.
** The PGroup mutex must be held when accessing nMax. ** The PGroup mutex must be held when accessing nMax.
*/ */
PGroup *pGroup; /* PGroup this cache belongs to */ PGroup * pGroup; /* PGroup this cache belongs to */
unsigned int *pnPurgeable; /* Pointer to pGroup->nPurgeable */ unsigned int *pnPurgeable; /* Pointer to pGroup->nPurgeable */
int szPage; /* Size of database content section */ int szPage; /* Size of database content section */
int szExtra; /* sizeof(MemPage)+sizeof(PgHdr) */ int szExtra; /* sizeof(MemPage)+sizeof(PgHdr) */
int szAlloc; /* Total size of one pcache line */ int szAlloc; /* Total size of one pcache line */
int bPurgeable; /* True if cache is purgeable */ int bPurgeable; /* True if cache is purgeable */
unsigned int nMin; /* Minimum number of pages reserved */ unsigned int nMin; /* Minimum number of pages reserved */
unsigned int nMax; /* Configured "cache_size" value */ unsigned int nMax; /* Configured "cache_size" value */
unsigned int n90pct; /* nMax*9/10 */ unsigned int n90pct; /* nMax*9/10 */
unsigned int iMaxKey; /* Largest key seen since xTruncate() */ unsigned int iMaxKey; /* Largest key seen since xTruncate() */
unsigned int nPurgeableDummy; /* pnPurgeable points here when not used*/ unsigned int nPurgeableDummy; /* pnPurgeable points here when not used*/
/* Hash table of all pages. The following variables may only be accessed /* Hash table of all pages. The following variables may only be accessed
** when the accessor is holding the PGroup mutex. ** when the accessor is holding the PGroup mutex.
*/ */
unsigned int nRecyclable; /* Number of pages in the LRU list */ unsigned int nRecyclable; /* Number of pages in the LRU list */
unsigned int nPage; /* Total number of pages in apHash */ unsigned int nPage; /* Total number of pages in apHash */
unsigned int nHash; /* Number of slots in apHash[] */ unsigned int nHash; /* Number of slots in apHash[] */
PgHdr1 **apHash; /* Hash table for fast lookup by key */ PgHdr1 ** apHash; /* Hash table for fast lookup by key */
PgHdr1 *pFree; /* List of unused pcache-local pages */ PgHdr1 * pFree; /* List of unused pcache-local pages */
void *pBulk; /* Bulk memory used by pcache-local */ void * pBulk; /* Bulk memory used by pcache-local */
}; };
/* /*
...@@ -196,73 +196,56 @@ struct PCache1 { ...@@ -196,73 +196,56 @@ struct PCache1 {
** buffer provided using the SQLITE_CONFIG_PAGECACHE mechanism. ** buffer provided using the SQLITE_CONFIG_PAGECACHE mechanism.
*/ */
struct PgFreeslot { struct PgFreeslot {
PgFreeslot *pNext; /* Next free slot */ PgFreeslot *pNext; /* Next free slot */
};
sqlite3_pcache_methods2 pcache2 = {
1, /* iVersion */
0, /* pArg */
pcache1Init, /* xInit */
pcache1Shutdown, /* xShutdown */
pcache1Create, /* xCreate */
pcache1Cachesize, /* xCachesize */
pcache1Pagecount, /* xPagecount */
pcache1Fetch, /* xFetch */
pcache1Unpin, /* xUnpin */
pcache1Rekey, /* xRekey */
pcache1Truncate, /* xTruncate */
pcache1Destroy, /* xDestroy */
pcache1Shrink /* xShrink */
}; };
/* /*
** Global data used by this cache. ** Global data used by this cache.
*/ */
static struct PCacheGlobal { static struct PCacheGlobal {
PGroup grp; /* The global PGroup for mode (2) */ PGroup grp; /* The global PGroup for mode (2) */
/* Variables related to SQLITE_CONFIG_PAGECACHE settings. The /* Variables related to SQLITE_CONFIG_PAGECACHE settings. The
** szSlot, nSlot, pStart, pEnd, nReserve, and isInit values are all ** szSlot, nSlot, pStart, pEnd, nReserve, and isInit values are all
** fixed at sqlite3_initialize() time and do not require mutex protection. ** fixed at sqlite3_initialize() time and do not require mutex protection.
** The nFreeSlot and pFree values do require mutex protection. ** The nFreeSlot and pFree values do require mutex protection.
*/ */
int isInit; /* True if initialized */ int isInit; /* True if initialized */
int separateCache; /* Use a new PGroup for each PCache */ int separateCache; /* Use a new PGroup for each PCache */
int nInitPage; /* Initial bulk allocation size */ int nInitPage; /* Initial bulk allocation size */
int szSlot; /* Size of each free slot */ int szSlot; /* Size of each free slot */
int nSlot; /* The number of pcache slots */ int nSlot; /* The number of pcache slots */
int nReserve; /* Try to keep nFreeSlot above this */ int nReserve; /* Try to keep nFreeSlot above this */
void *pStart, *pEnd; /* Bounds of global page cache memory */ void *pStart, *pEnd; /* Bounds of global page cache memory */
/* Above requires no mutex. Use mutex below for variable that follow. */ /* Above requires no mutex. Use mutex below for variable that follow. */
sqlite3_mutex *mutex; /* Mutex for accessing the following: */ pthread_mutex_t mutex; /* Mutex for accessing the following: */
PgFreeslot *pFree; /* Free page blocks */ PgFreeslot * pFree; /* Free page blocks */
int nFreeSlot; /* Number of unused pcache slots */ int nFreeSlot; /* Number of unused pcache slots */
/* The following value requires a mutex to change. We skip the mutex on /* The following value requires a mutex to change. We skip the mutex on
** reading because (1) most platforms read a 32-bit integer atomically and ** reading because (1) most platforms read a 32-bit integer atomically and
** (2) even if an incorrect value is read, no great harm is done since this ** (2) even if an incorrect value is read, no great harm is done since this
** is really just an optimization. */ ** is really just an optimization. */
int bUnderPressure; /* True if low on PAGECACHE memory */ int bUnderPressure; /* True if low on PAGECACHE memory */
} pcache1; } pcache1;
/* /*
** Macros to enter and leave the PCache LRU mutex. ** Macros to enter and leave the PCache LRU mutex.
*/ */
#if !defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0 #if !defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE == 0
# define pcache1EnterMutex(X) assert((X)->mutex==0) #define pcache1EnterMutex(X) assert((X)->mutex == 0)
# define pcache1LeaveMutex(X) assert((X)->mutex==0) #define pcache1LeaveMutex(X) assert((X)->mutex == 0)
# define PCACHE1_MIGHT_USE_GROUP_MUTEX 0 #define PCACHE1_MIGHT_USE_GROUP_MUTEX 0
#else #else
# define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex) #define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex)
# define pcache1LeaveMutex(X) sqlite3_mutex_leave((X)->mutex) #define pcache1LeaveMutex(X) sqlite3_mutex_leave((X)->mutex)
# define PCACHE1_MIGHT_USE_GROUP_MUTEX 1 #define PCACHE1_MIGHT_USE_GROUP_MUTEX 1
#endif #endif
/******************************************************************************/ /******************************************************************************/
/******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************/ /******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************/
/* /*
** This function is called during initialization if a static buffer is ** This function is called during initialization if a static buffer is
** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE ** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE
** verb to sqlite3_config(). Parameter pBuf points to an allocation large ** verb to sqlite3_config(). Parameter pBuf points to an allocation large
** enough to contain 'n' buffers of 'sz' bytes each. ** enough to contain 'n' buffers of 'sz' bytes each.
...@@ -270,23 +253,23 @@ static struct PCacheGlobal { ...@@ -270,23 +253,23 @@ static struct PCacheGlobal {
** This routine is called from sqlite3_initialize() and so it is guaranteed ** This routine is called from sqlite3_initialize() and so it is guaranteed
** to be serialized already. There is no need for further mutexing. ** to be serialized already. There is no need for further mutexing.
*/ */
void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n) {
if( pcache1.isInit ){ if (pcache1.isInit) {
PgFreeslot *p; PgFreeslot *p;
if( pBuf==0 ) sz = n = 0; if (pBuf == 0) sz = n = 0;
if( n==0 ) sz = 0; if (n == 0) sz = 0;
sz = ROUNDDOWN8(sz); sz = ROUNDDOWN8(sz);
pcache1.szSlot = sz; pcache1.szSlot = sz;
pcache1.nSlot = pcache1.nFreeSlot = n; pcache1.nSlot = pcache1.nFreeSlot = n;
pcache1.nReserve = n>90 ? 10 : (n/10 + 1); pcache1.nReserve = n > 90 ? 10 : (n / 10 + 1);
pcache1.pStart = pBuf; pcache1.pStart = pBuf;
pcache1.pFree = 0; pcache1.pFree = 0;
pcache1.bUnderPressure = 0; pcache1.bUnderPressure = 0;
while( n-- ){ while (n--) {
p = (PgFreeslot*)pBuf; p = (PgFreeslot *)pBuf;
p->pNext = pcache1.pFree; p->pNext = pcache1.pFree;
pcache1.pFree = p; pcache1.pFree = p;
pBuf = (void*)&((char*)pBuf)[sz]; pBuf = (void *)&((char *)pBuf)[sz];
} }
pcache1.pEnd = pBuf; pcache1.pEnd = pBuf;
} }
...@@ -296,72 +279,72 @@ void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ ...@@ -296,72 +279,72 @@ void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
** Try to initialize the pCache->pFree and pCache->pBulk fields. Return ** Try to initialize the pCache->pFree and pCache->pBulk fields. Return
** true if pCache->pFree ends up containing one or more free pages. ** true if pCache->pFree ends up containing one or more free pages.
*/ */
static int pcache1InitBulk(PCache1 *pCache){ static int pcache1InitBulk(PCache1 *pCache) {
i64 szBulk; i64 szBulk;
char *zBulk; char *zBulk;
if( pcache1.nInitPage==0 ) return 0; if (pcache1.nInitPage == 0) return 0;
/* Do not bother with a bulk allocation if the cache size very small */ /* Do not bother with a bulk allocation if the cache size very small */
if( pCache->nMax<3 ) return 0; if (pCache->nMax < 3) return 0;
sqlite3BeginBenignMalloc(); sqlite3BeginBenignMalloc();
if( pcache1.nInitPage>0 ){ if (pcache1.nInitPage > 0) {
szBulk = pCache->szAlloc * (i64)pcache1.nInitPage; szBulk = pCache->szAlloc * (i64)pcache1.nInitPage;
}else{ } else {
szBulk = -1024 * (i64)pcache1.nInitPage; szBulk = -1024 * (i64)pcache1.nInitPage;
} }
if( szBulk > pCache->szAlloc*(i64)pCache->nMax ){ if (szBulk > pCache->szAlloc * (i64)pCache->nMax) {
szBulk = pCache->szAlloc*(i64)pCache->nMax; szBulk = pCache->szAlloc * (i64)pCache->nMax;
} }
zBulk = pCache->pBulk = sqlite3Malloc( szBulk ); zBulk = pCache->pBulk = sqlite3Malloc(szBulk);
sqlite3EndBenignMalloc(); sqlite3EndBenignMalloc();
if( zBulk ){ if (zBulk) {
int nBulk = sqlite3MallocSize(zBulk)/pCache->szAlloc; int nBulk = sqlite3MallocSize(zBulk) / pCache->szAlloc;
do{ do {
PgHdr1 *pX = (PgHdr1*)&zBulk[pCache->szPage]; PgHdr1 *pX = (PgHdr1 *)&zBulk[pCache->szPage];
pX->page.pBuf = zBulk; pX->page.pBuf = zBulk;
pX->page.pExtra = &pX[1]; pX->page.pExtra = &pX[1];
pX->isBulkLocal = 1; pX->isBulkLocal = 1;
pX->isAnchor = 0; pX->isAnchor = 0;
pX->pNext = pCache->pFree; pX->pNext = pCache->pFree;
pX->pLruPrev = 0; /* Initializing this saves a valgrind error */ pX->pLruPrev = 0; /* Initializing this saves a valgrind error */
pCache->pFree = pX; pCache->pFree = pX;
zBulk += pCache->szAlloc; zBulk += pCache->szAlloc;
}while( --nBulk ); } while (--nBulk);
} }
return pCache->pFree!=0; return pCache->pFree != 0;
} }
/* /*
** Malloc function used within this file to allocate space from the buffer ** Malloc function used within this file to allocate space from the buffer
** configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no ** configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no
** such buffer exists or there is no space left in it, this function falls ** such buffer exists or there is no space left in it, this function falls
** back to sqlite3Malloc(). ** back to sqlite3Malloc().
** **
** Multiple threads can run this routine at the same time. Global variables ** Multiple threads can run this routine at the same time. Global variables
** in pcache1 need to be protected via mutex. ** in pcache1 need to be protected via mutex.
*/ */
static void *pcache1Alloc(int nByte){ static void *pcache1Alloc(int nByte) {
void *p = 0; void *p = 0;
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) ); assert(sqlite3_mutex_notheld(pcache1.grp.mutex));
if( nByte<=pcache1.szSlot ){ if (nByte <= pcache1.szSlot) {
sqlite3_mutex_enter(pcache1.mutex); sqlite3_mutex_enter(pcache1.mutex);
p = (PgHdr1 *)pcache1.pFree; p = (PgHdr1 *)pcache1.pFree;
if( p ){ if (p) {
pcache1.pFree = pcache1.pFree->pNext; pcache1.pFree = pcache1.pFree->pNext;
pcache1.nFreeSlot--; pcache1.nFreeSlot--;
pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve; pcache1.bUnderPressure = pcache1.nFreeSlot < pcache1.nReserve;
assert( pcache1.nFreeSlot>=0 ); assert(pcache1.nFreeSlot >= 0);
sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte); sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1); sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1);
} }
sqlite3_mutex_leave(pcache1.mutex); sqlite3_mutex_leave(pcache1.mutex);
} }
if( p==0 ){ if (p == 0) {
/* Memory is not available in the SQLITE_CONFIG_PAGECACHE pool. Get /* Memory is not available in the SQLITE_CONFIG_PAGECACHE pool. Get
** it from sqlite3Malloc instead. ** it from sqlite3Malloc instead.
*/ */
p = sqlite3Malloc(nByte); p = sqlite3Malloc(nByte);
#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS #ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
if( p ){ if (p) {
int sz = sqlite3MallocSize(p); int sz = sqlite3MallocSize(p);
sqlite3_mutex_enter(pcache1.mutex); sqlite3_mutex_enter(pcache1.mutex);
sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte); sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
...@@ -377,21 +360,21 @@ static void *pcache1Alloc(int nByte){ ...@@ -377,21 +360,21 @@ static void *pcache1Alloc(int nByte){
/* /*
** Free an allocated buffer obtained from pcache1Alloc(). ** Free an allocated buffer obtained from pcache1Alloc().
*/ */
static void pcache1Free(void *p){ static void pcache1Free(void *p) {
if( p==0 ) return; if (p == 0) return;
if( SQLITE_WITHIN(p, pcache1.pStart, pcache1.pEnd) ){ if (SQLITE_WITHIN(p, pcache1.pStart, pcache1.pEnd)) {
PgFreeslot *pSlot; PgFreeslot *pSlot;
sqlite3_mutex_enter(pcache1.mutex); sqlite3_mutex_enter(pcache1.mutex);
sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1); sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1);
pSlot = (PgFreeslot*)p; pSlot = (PgFreeslot *)p;
pSlot->pNext = pcache1.pFree; pSlot->pNext = pcache1.pFree;
pcache1.pFree = pSlot; pcache1.pFree = pSlot;
pcache1.nFreeSlot++; pcache1.nFreeSlot++;
pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve; pcache1.bUnderPressure = pcache1.nFreeSlot < pcache1.nReserve;
assert( pcache1.nFreeSlot<=pcache1.nSlot ); assert(pcache1.nFreeSlot <= pcache1.nSlot);
sqlite3_mutex_leave(pcache1.mutex); sqlite3_mutex_leave(pcache1.mutex);
}else{ } else {
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); assert(sqlite3MemdebugHasType(p, MEMTYPE_PCACHE));
sqlite3MemdebugSetType(p, MEMTYPE_HEAP); sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS #ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
{ {
...@@ -410,12 +393,12 @@ static void pcache1Free(void *p){ ...@@ -410,12 +393,12 @@ static void pcache1Free(void *p){
/* /*
** Return the size of a pcache allocation ** Return the size of a pcache allocation
*/ */
static int pcache1MemSize(void *p){ static int pcache1MemSize(void *p) {
if( p>=pcache1.pStart && p<pcache1.pEnd ){ if (p >= pcache1.pStart && p < pcache1.pEnd) {
return pcache1.szSlot; return pcache1.szSlot;
}else{ } else {
int iSize; int iSize;
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); assert(sqlite3MemdebugHasType(p, MEMTYPE_PCACHE));
sqlite3MemdebugSetType(p, MEMTYPE_HEAP); sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
iSize = sqlite3MallocSize(p); iSize = sqlite3MallocSize(p);
sqlite3MemdebugSetType(p, MEMTYPE_PCACHE); sqlite3MemdebugSetType(p, MEMTYPE_PCACHE);
...@@ -427,30 +410,32 @@ static int pcache1MemSize(void *p){ ...@@ -427,30 +410,32 @@ static int pcache1MemSize(void *p){
/* /*
** Allocate a new page object initially associated with cache pCache. ** Allocate a new page object initially associated with cache pCache.
*/ */
static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc) {
PgHdr1 *p = 0; PgHdr1 *p = 0;
void *pPg; void * pPg;
assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); assert(sqlite3_mutex_held(pCache->pGroup->mutex));
if( pCache->pFree || (pCache->nPage==0 && pcache1InitBulk(pCache)) ){ if (pCache->pFree || (pCache->nPage == 0 && pcache1InitBulk(pCache))) {
assert( pCache->pFree!=0 ); assert(pCache->pFree != 0);
p = pCache->pFree; p = pCache->pFree;
pCache->pFree = p->pNext; pCache->pFree = p->pNext;
p->pNext = 0; p->pNext = 0;
}else{ } else {
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
/* The group mutex must be released before pcache1Alloc() is called. This /* The group mutex must be released before pcache1Alloc() is called. This
** is because it might call sqlite3_release_memory(), which assumes that ** is because it might call sqlite3_release_memory(), which assumes that
** this mutex is not held. */ ** this mutex is not held. */
assert( pcache1.separateCache==0 ); assert(pcache1.separateCache == 0);
assert( pCache->pGroup==&pcache1.grp ); assert(pCache->pGroup == &pcache1.grp);
pcache1LeaveMutex(pCache->pGroup); pcache1LeaveMutex(pCache->pGroup);
#endif #endif
if( benignMalloc ){ sqlite3BeginBenignMalloc(); } if (benignMalloc) {
sqlite3BeginBenignMalloc();
}
#ifdef SQLITE_PCACHE_SEPARATE_HEADER #ifdef SQLITE_PCACHE_SEPARATE_HEADER
pPg = pcache1Alloc(pCache->szPage); pPg = pcache1Alloc(pCache->szPage);
p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra); p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra);
if( !pPg || !p ){ if (!pPg || !p) {
pcache1Free(pPg); pcache1Free(pPg);
sqlite3_free(p); sqlite3_free(p);
pPg = 0; pPg = 0;
...@@ -458,11 +443,13 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){ ...@@ -458,11 +443,13 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
#else #else
pPg = pcache1Alloc(pCache->szAlloc); pPg = pcache1Alloc(pCache->szAlloc);
#endif #endif
if( benignMalloc ){ sqlite3EndBenignMalloc(); } if (benignMalloc) {
sqlite3EndBenignMalloc();
}
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
pcache1EnterMutex(pCache->pGroup); pcache1EnterMutex(pCache->pGroup);
#endif #endif
if( pPg==0 ) return 0; if (pPg == 0) return 0;
#ifndef SQLITE_PCACHE_SEPARATE_HEADER #ifndef SQLITE_PCACHE_SEPARATE_HEADER
p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
#endif #endif
...@@ -470,7 +457,7 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){ ...@@ -470,7 +457,7 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
p->page.pExtra = &p[1]; p->page.pExtra = &p[1];
p->isBulkLocal = 0; p->isBulkLocal = 0;
p->isAnchor = 0; p->isAnchor = 0;
p->pLruPrev = 0; /* Initializing this saves a valgrind error */ p->pLruPrev = 0; /* Initializing this saves a valgrind error */
} }
(*pCache->pnPurgeable)++; (*pCache->pnPurgeable)++;
return p; return p;
...@@ -479,15 +466,15 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){ ...@@ -479,15 +466,15 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
/* /*
** Free a page object allocated by pcache1AllocPage(). ** Free a page object allocated by pcache1AllocPage().
*/ */
static void pcache1FreePage(PgHdr1 *p){ static void pcache1FreePage(PgHdr1 *p) {
PCache1 *pCache; PCache1 *pCache;
assert( p!=0 ); assert(p != 0);
pCache = p->pCache; pCache = p->pCache;
assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) ); assert(sqlite3_mutex_held(p->pCache->pGroup->mutex));
if( p->isBulkLocal ){ if (p->isBulkLocal) {
p->pNext = pCache->pFree; p->pNext = pCache->pFree;
pCache->pFree = p; pCache->pFree = p;
}else{ } else {
pcache1Free(p->page.pBuf); pcache1Free(p->page.pBuf);
#ifdef SQLITE_PCACHE_SEPARATE_HEADER #ifdef SQLITE_PCACHE_SEPARATE_HEADER
sqlite3_free(p); sqlite3_free(p);
...@@ -501,18 +488,15 @@ static void pcache1FreePage(PgHdr1 *p){ ...@@ -501,18 +488,15 @@ static void pcache1FreePage(PgHdr1 *p){
** using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no such buffer ** using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no such buffer
** exists, this function falls back to sqlite3Malloc(). ** exists, this function falls back to sqlite3Malloc().
*/ */
void *sqlite3PageMalloc(int sz){ void *sqlite3PageMalloc(int sz) {
assert( sz<=65536+8 ); /* These allocations are never very large */ assert(sz <= 65536 + 8); /* These allocations are never very large */
return pcache1Alloc(sz); return pcache1Alloc(sz);
} }
/* /*
** Free an allocated buffer obtained from sqlite3PageMalloc(). ** Free an allocated buffer obtained from sqlite3PageMalloc().
*/ */
void sqlite3PageFree(void *p){ void sqlite3PageFree(void *p) { pcache1Free(p); }
pcache1Free(p);
}
/* /*
** Return true if it desirable to avoid allocating a new page cache ** Return true if it desirable to avoid allocating a new page cache
...@@ -530,10 +514,10 @@ void sqlite3PageFree(void *p){ ...@@ -530,10 +514,10 @@ void sqlite3PageFree(void *p){
** allocating a new page cache entry in order to avoid stressing ** allocating a new page cache entry in order to avoid stressing
** the heap even further. ** the heap even further.
*/ */
static int pcache1UnderMemoryPressure(PCache1 *pCache){ static int pcache1UnderMemoryPressure(PCache1 *pCache) {
if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){ if (pcache1.nSlot && (pCache->szPage + pCache->szExtra) <= pcache1.szSlot) {
return pcache1.bUnderPressure; return pcache1.bUnderPressure;
}else{ } else {
return sqlite3HeapNearlyFull(); return sqlite3HeapNearlyFull();
} }
} }
...@@ -547,28 +531,32 @@ static int pcache1UnderMemoryPressure(PCache1 *pCache){ ...@@ -547,28 +531,32 @@ static int pcache1UnderMemoryPressure(PCache1 *pCache){
** **
** The PCache mutex must be held when this function is called. ** The PCache mutex must be held when this function is called.
*/ */
static void pcache1ResizeHash(PCache1 *p){ static void pcache1ResizeHash(PCache1 *p) {
PgHdr1 **apNew; PgHdr1 ** apNew;
unsigned int nNew; unsigned int nNew;
unsigned int i; unsigned int i;
assert( sqlite3_mutex_held(p->pGroup->mutex) ); assert(sqlite3_mutex_held(p->pGroup->mutex));
nNew = p->nHash*2; nNew = p->nHash * 2;
if( nNew<256 ){ if (nNew < 256) {
nNew = 256; nNew = 256;
} }
pcache1LeaveMutex(p->pGroup); pcache1LeaveMutex(p->pGroup);
if( p->nHash ){ sqlite3BeginBenignMalloc(); } if (p->nHash) {
apNew = (PgHdr1 **)sqlite3MallocZero(sizeof(PgHdr1 *)*nNew); sqlite3BeginBenignMalloc();
if( p->nHash ){ sqlite3EndBenignMalloc(); } }
apNew = (PgHdr1 **)sqlite3MallocZero(sizeof(PgHdr1 *) * nNew);
if (p->nHash) {
sqlite3EndBenignMalloc();
}
pcache1EnterMutex(p->pGroup); pcache1EnterMutex(p->pGroup);
if( apNew ){ if (apNew) {
for(i=0; i<p->nHash; i++){ for (i = 0; i < p->nHash; i++) {
PgHdr1 *pPage; PgHdr1 *pPage;
PgHdr1 *pNext = p->apHash[i]; PgHdr1 *pNext = p->apHash[i];
while( (pPage = pNext)!=0 ){ while ((pPage = pNext) != 0) {
unsigned int h = pPage->iKey % nNew; unsigned int h = pPage->iKey % nNew;
pNext = pPage->pNext; pNext = pPage->pNext;
pPage->pNext = apNew[h]; pPage->pNext = apNew[h];
...@@ -582,123 +570,120 @@ static void pcache1ResizeHash(PCache1 *p){ ...@@ -582,123 +570,120 @@ static void pcache1ResizeHash(PCache1 *p){
} }
/* /*
** This function is used internally to remove the page pPage from the ** This function is used internally to remove the page pPage from the
** PGroup LRU list, if is part of it. If pPage is not part of the PGroup ** PGroup LRU list, if is part of it. If pPage is not part of the PGroup
** LRU list, then this function is a no-op. ** LRU list, then this function is a no-op.
** **
** The PGroup mutex must be held when this function is called. ** The PGroup mutex must be held when this function is called.
*/ */
static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){ static PgHdr1 *pcache1PinPage(PgHdr1 *pPage) {
assert( pPage!=0 ); assert(pPage != 0);
assert( PAGE_IS_UNPINNED(pPage) ); assert(PAGE_IS_UNPINNED(pPage));
assert( pPage->pLruNext ); assert(pPage->pLruNext);
assert( pPage->pLruPrev ); assert(pPage->pLruPrev);
assert( sqlite3_mutex_held(pPage->pCache->pGroup->mutex) ); assert(sqlite3_mutex_held(pPage->pCache->pGroup->mutex));
pPage->pLruPrev->pLruNext = pPage->pLruNext; pPage->pLruPrev->pLruNext = pPage->pLruNext;
pPage->pLruNext->pLruPrev = pPage->pLruPrev; pPage->pLruNext->pLruPrev = pPage->pLruPrev;
pPage->pLruNext = 0; pPage->pLruNext = 0;
/* pPage->pLruPrev = 0; /* pPage->pLruPrev = 0;
** No need to clear pLruPrev as it is never accessed if pLruNext is 0 */ ** No need to clear pLruPrev as it is never accessed if pLruNext is 0 */
assert( pPage->isAnchor==0 ); assert(pPage->isAnchor == 0);
assert( pPage->pCache->pGroup->lru.isAnchor==1 ); assert(pPage->pCache->pGroup->lru.isAnchor == 1);
pPage->pCache->nRecyclable--; pPage->pCache->nRecyclable--;
return pPage; return pPage;
} }
/* /*
** Remove the page supplied as an argument from the hash table ** Remove the page supplied as an argument from the hash table
** (PCache1.apHash structure) that it is currently stored in. ** (PCache1.apHash structure) that it is currently stored in.
** Also free the page if freePage is true. ** Also free the page if freePage is true.
** **
** The PGroup mutex must be held when this function is called. ** The PGroup mutex must be held when this function is called.
*/ */
static void pcache1RemoveFromHash(PgHdr1 *pPage, int freeFlag){ static void pcache1RemoveFromHash(PgHdr1 *pPage, int freeFlag) {
unsigned int h; unsigned int h;
PCache1 *pCache = pPage->pCache; PCache1 * pCache = pPage->pCache;
PgHdr1 **pp; PgHdr1 ** pp;
assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); assert(sqlite3_mutex_held(pCache->pGroup->mutex));
h = pPage->iKey % pCache->nHash; h = pPage->iKey % pCache->nHash;
for(pp=&pCache->apHash[h]; (*pp)!=pPage; pp=&(*pp)->pNext); for (pp = &pCache->apHash[h]; (*pp) != pPage; pp = &(*pp)->pNext)
;
*pp = (*pp)->pNext; *pp = (*pp)->pNext;
pCache->nPage--; pCache->nPage--;
if( freeFlag ) pcache1FreePage(pPage); if (freeFlag) pcache1FreePage(pPage);
} }
/* /*
** If there are currently more than nMaxPage pages allocated, try ** If there are currently more than nMaxPage pages allocated, try
** to recycle pages to reduce the number allocated to nMaxPage. ** to recycle pages to reduce the number allocated to nMaxPage.
*/ */
static void pcache1EnforceMaxPage(PCache1 *pCache){ static void pcache1EnforceMaxPage(PCache1 *pCache) {
PGroup *pGroup = pCache->pGroup; PGroup *pGroup = pCache->pGroup;
PgHdr1 *p; PgHdr1 *p;
assert( sqlite3_mutex_held(pGroup->mutex) ); assert(sqlite3_mutex_held(pGroup->mutex));
while( pGroup->nPurgeable>pGroup->nMaxPage while (pGroup->nPurgeable > pGroup->nMaxPage && (p = pGroup->lru.pLruPrev)->isAnchor == 0) {
&& (p=pGroup->lru.pLruPrev)->isAnchor==0 assert(p->pCache->pGroup == pGroup);
){ assert(PAGE_IS_UNPINNED(p));
assert( p->pCache->pGroup==pGroup );
assert( PAGE_IS_UNPINNED(p) );
pcache1PinPage(p); pcache1PinPage(p);
pcache1RemoveFromHash(p, 1); pcache1RemoveFromHash(p, 1);
} }
if( pCache->nPage==0 && pCache->pBulk ){ if (pCache->nPage == 0 && pCache->pBulk) {
sqlite3_free(pCache->pBulk); sqlite3_free(pCache->pBulk);
pCache->pBulk = pCache->pFree = 0; pCache->pBulk = pCache->pFree = 0;
} }
} }
/* /*
** Discard all pages from cache pCache with a page number (key value) ** Discard all pages from cache pCache with a page number (key value)
** greater than or equal to iLimit. Any pinned pages that meet this ** greater than or equal to iLimit. Any pinned pages that meet this
** criteria are unpinned before they are discarded. ** criteria are unpinned before they are discarded.
** **
** The PCache mutex must be held when this function is called. ** The PCache mutex must be held when this function is called.
*/ */
static void pcache1TruncateUnsafe( static void pcache1TruncateUnsafe(PCache1 * pCache, /* The cache to truncate */
PCache1 *pCache, /* The cache to truncate */ unsigned int iLimit /* Drop pages with this pgno or larger */
unsigned int iLimit /* Drop pages with this pgno or larger */ ) {
){ TESTONLY(int nPage = 0;) /* To assert pCache->nPage is correct */
TESTONLY( int nPage = 0; ) /* To assert pCache->nPage is correct */
unsigned int h, iStop; unsigned int h, iStop;
assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); assert(sqlite3_mutex_held(pCache->pGroup->mutex));
assert( pCache->iMaxKey >= iLimit ); assert(pCache->iMaxKey >= iLimit);
assert( pCache->nHash > 0 ); assert(pCache->nHash > 0);
if( pCache->iMaxKey - iLimit < pCache->nHash ){ if (pCache->iMaxKey - iLimit < pCache->nHash) {
/* If we are just shaving the last few pages off the end of the /* If we are just shaving the last few pages off the end of the
** cache, then there is no point in scanning the entire hash table. ** cache, then there is no point in scanning the entire hash table.
** Only scan those hash slots that might contain pages that need to ** Only scan those hash slots that might contain pages that need to
** be removed. */ ** be removed. */
h = iLimit % pCache->nHash; h = iLimit % pCache->nHash;
iStop = pCache->iMaxKey % pCache->nHash; iStop = pCache->iMaxKey % pCache->nHash;
TESTONLY( nPage = -10; ) /* Disable the pCache->nPage validity check */ TESTONLY(nPage = -10;) /* Disable the pCache->nPage validity check */
}else{ } else {
/* This is the general case where many pages are being removed. /* This is the general case where many pages are being removed.
** It is necessary to scan the entire hash table */ ** It is necessary to scan the entire hash table */
h = pCache->nHash/2; h = pCache->nHash / 2;
iStop = h - 1; iStop = h - 1;
} }
for(;;){ for (;;) {
PgHdr1 **pp; PgHdr1 **pp;
PgHdr1 *pPage; PgHdr1 * pPage;
assert( h<pCache->nHash ); assert(h < pCache->nHash);
pp = &pCache->apHash[h]; pp = &pCache->apHash[h];
while( (pPage = *pp)!=0 ){ while ((pPage = *pp) != 0) {
if( pPage->iKey>=iLimit ){ if (pPage->iKey >= iLimit) {
pCache->nPage--; pCache->nPage--;
*pp = pPage->pNext; *pp = pPage->pNext;
if( PAGE_IS_UNPINNED(pPage) ) pcache1PinPage(pPage); if (PAGE_IS_UNPINNED(pPage)) pcache1PinPage(pPage);
pcache1FreePage(pPage); pcache1FreePage(pPage);
}else{ } else {
pp = &pPage->pNext; pp = &pPage->pNext;
TESTONLY( if( nPage>=0 ) nPage++; ) TESTONLY(if (nPage >= 0) nPage++;)
} }
} }
if( h==iStop ) break; if (h == iStop) break;
h = (h+1) % pCache->nHash; h = (h + 1) % pCache->nHash;
} }
assert( nPage<0 || pCache->nPage==(unsigned)nPage ); assert(nPage < 0 || pCache->nPage == (unsigned)nPage);
} }
/******************************************************************************/ /******************************************************************************/
...@@ -707,62 +692,54 @@ static void pcache1TruncateUnsafe( ...@@ -707,62 +692,54 @@ static void pcache1TruncateUnsafe(
/* /*
** Implementation of the sqlite3_pcache.xInit method. ** Implementation of the sqlite3_pcache.xInit method.
*/ */
static int pcache1Init(void *NotUsed){ static int pcache1Init(void *NotUsed) {
UNUSED_PARAMETER(NotUsed); assert(pcache1.isInit == 0);
assert( pcache1.isInit==0 );
memset(&pcache1, 0, sizeof(pcache1)); memset(&pcache1, 0, sizeof(pcache1));
// /*
/* // ** The pcache1.separateCache variable is true if each PCache has its own
** The pcache1.separateCache variable is true if each PCache has its own // ** private PGroup (mode-1). pcache1.separateCache is false if the single
** private PGroup (mode-1). pcache1.separateCache is false if the single // ** PGroup in pcache1.grp is used for all page caches (mode-2).
** PGroup in pcache1.grp is used for all page caches (mode-2). // **
** // ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT
** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT // **
** // ** * Use a unified cache in single-threaded applications that have
** * Use a unified cache in single-threaded applications that have // ** configured a start-time buffer for use as page-cache memory using
** configured a start-time buffer for use as page-cache memory using // ** sqlite3_config(SQLITE_CONFIG_PAGECACHE, pBuf, sz, N) with non-NULL
** sqlite3_config(SQLITE_CONFIG_PAGECACHE, pBuf, sz, N) with non-NULL // ** pBuf argument.
** pBuf argument. // **
** // ** * Otherwise use separate caches (mode-1)
** * Otherwise use separate caches (mode-1) // */
*/ // #if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT)
#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) // pcache1.separateCache = 0;
pcache1.separateCache = 0; // #elif SQLITE_THREADSAFE
#elif SQLITE_THREADSAFE // pcache1.separateCache = sqlite3GlobalConfig.pPage==0
pcache1.separateCache = sqlite3GlobalConfig.pPage==0 // || sqlite3GlobalConfig.bCoreMutex>0;
|| sqlite3GlobalConfig.bCoreMutex>0; // #else
#else // pcache1.separateCache = sqlite3GlobalConfig.pPage==0;
pcache1.separateCache = sqlite3GlobalConfig.pPage==0; // #endif
#endif pcache1.separateCache = 1;
#if SQLITE_THREADSAFE pthread_mutex_init(&pcache1.grp.mutex, NULL);
if( sqlite3GlobalConfig.bCoreMutex ){ pthread_mutex_init(&pcache1.mutex, NULL);
pcache1.grp.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU);
pcache1.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PMEM); // if (pcache1.separateCache && sqlite3GlobalConfig.nPage != 0 && sqlite3GlobalConfig.pPage == 0) {
} // pcache1.nInitPage = sqlite3GlobalConfig.nPage;
#endif // } else {
if( pcache1.separateCache pcache1.nInitPage = 0;
&& sqlite3GlobalConfig.nPage!=0 // }
&& sqlite3GlobalConfig.pPage==0
){
pcache1.nInitPage = sqlite3GlobalConfig.nPage;
}else{
pcache1.nInitPage = 0;
}
pcache1.grp.mxPinned = 10; pcache1.grp.mxPinned = 10;
pcache1.isInit = 1; pcache1.isInit = 1;
return SQLITE_OK; return 0;
} }
/* /*
** Implementation of the sqlite3_pcache.xShutdown method. ** Implementation of the sqlite3_pcache.xShutdown method.
** Note that the static mutex allocated in xInit does ** Note that the static mutex allocated in xInit does
** not need to be freed. ** not need to be freed.
*/ */
static void pcache1Shutdown(void *NotUsed){ static void pcache1Shutdown(void *NotUsed) {
UNUSED_PARAMETER(NotUsed); assert(pcache1.isInit != 0);
assert( pcache1.isInit!=0 );
memset(&pcache1, 0, sizeof(pcache1)); memset(&pcache1, 0, sizeof(pcache1));
} }
...@@ -774,25 +751,25 @@ static void pcache1Destroy(sqlite3_pcache *p); ...@@ -774,25 +751,25 @@ static void pcache1Destroy(sqlite3_pcache *p);
** **
** Allocate a new cache. ** Allocate a new cache.
*/ */
static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable) {
PCache1 *pCache; /* The newly created page cache */ PCache1 *pCache; /* The newly created page cache */
PGroup *pGroup; /* The group the new page cache will belong to */ PGroup * pGroup; /* The group the new page cache will belong to */
int sz; /* Bytes of memory required to allocate the new cache */ int sz; /* Bytes of memory required to allocate the new cache */
assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 ); assert((szPage & (szPage - 1)) == 0 && szPage >= 512 && szPage <= 65536);
assert( szExtra < 300 ); assert(szExtra < 300);
sz = sizeof(PCache1) + sizeof(PGroup)*pcache1.separateCache; sz = sizeof(PCache1) + sizeof(PGroup) * pcache1.separateCache;
pCache = (PCache1 *)sqlite3MallocZero(sz); pCache = (PCache1 *)sqlite3MallocZero(sz);
if( pCache ){ if (pCache) {
if( pcache1.separateCache ){ if (pcache1.separateCache) {
pGroup = (PGroup*)&pCache[1]; pGroup = (PGroup *)&pCache[1];
pGroup->mxPinned = 10; pGroup->mxPinned = 10;
}else{ } else {
pGroup = &pcache1.grp; pGroup = &pcache1.grp;
} }
pcache1EnterMutex(pGroup); pcache1EnterMutex(pGroup);
if( pGroup->lru.isAnchor==0 ){ if (pGroup->lru.isAnchor == 0) {
pGroup->lru.isAnchor = 1; pGroup->lru.isAnchor = 1;
pGroup->lru.pLruPrev = pGroup->lru.pLruNext = &pGroup->lru; pGroup->lru.pLruPrev = pGroup->lru.pLruNext = &pGroup->lru;
} }
...@@ -802,17 +779,17 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ ...@@ -802,17 +779,17 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
pCache->szAlloc = szPage + szExtra + ROUND8(sizeof(PgHdr1)); pCache->szAlloc = szPage + szExtra + ROUND8(sizeof(PgHdr1));
pCache->bPurgeable = (bPurgeable ? 1 : 0); pCache->bPurgeable = (bPurgeable ? 1 : 0);
pcache1ResizeHash(pCache); pcache1ResizeHash(pCache);
if( bPurgeable ){ if (bPurgeable) {
pCache->nMin = 10; pCache->nMin = 10;
pGroup->nMinPage += pCache->nMin; pGroup->nMinPage += pCache->nMin;
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pCache->pnPurgeable = &pGroup->nPurgeable; pCache->pnPurgeable = &pGroup->nPurgeable;
}else{ } else {
pCache->pnPurgeable = &pCache->nPurgeableDummy; pCache->pnPurgeable = &pCache->nPurgeableDummy;
} }
pcache1LeaveMutex(pGroup); pcache1LeaveMutex(pGroup);
if( pCache->nHash==0 ){ if (pCache->nHash == 0) {
pcache1Destroy((sqlite3_pcache*)pCache); pcache1Destroy((sqlite3_pcache *)pCache);
pCache = 0; pCache = 0;
} }
} }
...@@ -820,39 +797,39 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ ...@@ -820,39 +797,39 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
} }
/* /*
** Implementation of the sqlite3_pcache.xCachesize method. ** Implementation of the sqlite3_pcache.xCachesize method.
** **
** Configure the cache_size limit for a cache. ** Configure the cache_size limit for a cache.
*/ */
static void pcache1Cachesize(sqlite3_pcache *p, int nMax){ static void pcache1Cachesize(sqlite3_pcache *p, int nMax) {
PCache1 *pCache = (PCache1 *)p; PCache1 *pCache = (PCache1 *)p;
u32 n; u32 n;
assert( nMax>=0 ); assert(nMax >= 0);
if( pCache->bPurgeable ){ if (pCache->bPurgeable) {
PGroup *pGroup = pCache->pGroup; PGroup *pGroup = pCache->pGroup;
pcache1EnterMutex(pGroup); pcache1EnterMutex(pGroup);
n = (u32)nMax; n = (u32)nMax;
if( n > 0x7fff0000 - pGroup->nMaxPage + pCache->nMax ){ if (n > 0x7fff0000 - pGroup->nMaxPage + pCache->nMax) {
n = 0x7fff0000 - pGroup->nMaxPage + pCache->nMax; n = 0x7fff0000 - pGroup->nMaxPage + pCache->nMax;
} }
pGroup->nMaxPage += (n - pCache->nMax); pGroup->nMaxPage += (n - pCache->nMax);
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pCache->nMax = n; pCache->nMax = n;
pCache->n90pct = pCache->nMax*9/10; pCache->n90pct = pCache->nMax * 9 / 10;
pcache1EnforceMaxPage(pCache); pcache1EnforceMaxPage(pCache);
pcache1LeaveMutex(pGroup); pcache1LeaveMutex(pGroup);
} }
} }
/* /*
** Implementation of the sqlite3_pcache.xShrink method. ** Implementation of the sqlite3_pcache.xShrink method.
** **
** Free up as much memory as possible. ** Free up as much memory as possible.
*/ */
static void pcache1Shrink(sqlite3_pcache *p){ static void pcache1Shrink(sqlite3_pcache *p) {
PCache1 *pCache = (PCache1*)p; PCache1 *pCache = (PCache1 *)p;
if( pCache->bPurgeable ){ if (pCache->bPurgeable) {
PGroup *pGroup = pCache->pGroup; PGroup * pGroup = pCache->pGroup;
unsigned int savedMaxPage; unsigned int savedMaxPage;
pcache1EnterMutex(pGroup); pcache1EnterMutex(pGroup);
savedMaxPage = pGroup->nMaxPage; savedMaxPage = pGroup->nMaxPage;
...@@ -864,18 +841,17 @@ static void pcache1Shrink(sqlite3_pcache *p){ ...@@ -864,18 +841,17 @@ static void pcache1Shrink(sqlite3_pcache *p){
} }
/* /*
** Implementation of the sqlite3_pcache.xPagecount method. ** Implementation of the sqlite3_pcache.xPagecount method.
*/ */
static int pcache1Pagecount(sqlite3_pcache *p){ static int pcache1Pagecount(sqlite3_pcache *p) {
int n; int n;
PCache1 *pCache = (PCache1*)p; PCache1 *pCache = (PCache1 *)p;
pcache1EnterMutex(pCache->pGroup); pcache1EnterMutex(pCache->pGroup);
n = pCache->nPage; n = pCache->nPage;
pcache1LeaveMutex(pCache->pGroup); pcache1LeaveMutex(pCache->pGroup);
return n; return n;
} }
/* /*
** Implement steps 3, 4, and 5 of the pcache1Fetch() algorithm described ** Implement steps 3, 4, and 5 of the pcache1Fetch() algorithm described
** in the header of the pcache1Fetch() procedure. ** in the header of the pcache1Fetch() procedure.
...@@ -884,58 +860,49 @@ static int pcache1Pagecount(sqlite3_pcache *p){ ...@@ -884,58 +860,49 @@ static int pcache1Pagecount(sqlite3_pcache *p){
** usually not needed, and by avoiding the stack initialization required ** usually not needed, and by avoiding the stack initialization required
** for these steps, the main pcache1Fetch() procedure can run faster. ** for these steps, the main pcache1Fetch() procedure can run faster.
*/ */
static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2( static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(PCache1 *pCache, unsigned int iKey, int createFlag) {
PCache1 *pCache,
unsigned int iKey,
int createFlag
){
unsigned int nPinned; unsigned int nPinned;
PGroup *pGroup = pCache->pGroup; PGroup * pGroup = pCache->pGroup;
PgHdr1 *pPage = 0; PgHdr1 * pPage = 0;
/* Step 3: Abort if createFlag is 1 but the cache is nearly full */ /* Step 3: Abort if createFlag is 1 but the cache is nearly full */
assert( pCache->nPage >= pCache->nRecyclable ); assert(pCache->nPage >= pCache->nRecyclable);
nPinned = pCache->nPage - pCache->nRecyclable; nPinned = pCache->nPage - pCache->nRecyclable;
assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage ); assert(pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage);
assert( pCache->n90pct == pCache->nMax*9/10 ); assert(pCache->n90pct == pCache->nMax * 9 / 10);
if( createFlag==1 && ( if (createFlag == 1 && (nPinned >= pGroup->mxPinned || nPinned >= pCache->n90pct ||
nPinned>=pGroup->mxPinned (pcache1UnderMemoryPressure(pCache) && pCache->nRecyclable < nPinned))) {
|| nPinned>=pCache->n90pct
|| (pcache1UnderMemoryPressure(pCache) && pCache->nRecyclable<nPinned)
)){
return 0; return 0;
} }
if( pCache->nPage>=pCache->nHash ) pcache1ResizeHash(pCache); if (pCache->nPage >= pCache->nHash) pcache1ResizeHash(pCache);
assert( pCache->nHash>0 && pCache->apHash ); assert(pCache->nHash > 0 && pCache->apHash);
/* Step 4. Try to recycle a page. */ /* Step 4. Try to recycle a page. */
if( pCache->bPurgeable if (pCache->bPurgeable && !pGroup->lru.pLruPrev->isAnchor &&
&& !pGroup->lru.pLruPrev->isAnchor ((pCache->nPage + 1 >= pCache->nMax) || pcache1UnderMemoryPressure(pCache))) {
&& ((pCache->nPage+1>=pCache->nMax) || pcache1UnderMemoryPressure(pCache))
){
PCache1 *pOther; PCache1 *pOther;
pPage = pGroup->lru.pLruPrev; pPage = pGroup->lru.pLruPrev;
assert( PAGE_IS_UNPINNED(pPage) ); assert(PAGE_IS_UNPINNED(pPage));
pcache1RemoveFromHash(pPage, 0); pcache1RemoveFromHash(pPage, 0);
pcache1PinPage(pPage); pcache1PinPage(pPage);
pOther = pPage->pCache; pOther = pPage->pCache;
if( pOther->szAlloc != pCache->szAlloc ){ if (pOther->szAlloc != pCache->szAlloc) {
pcache1FreePage(pPage); pcache1FreePage(pPage);
pPage = 0; pPage = 0;
}else{ } else {
pGroup->nPurgeable -= (pOther->bPurgeable - pCache->bPurgeable); pGroup->nPurgeable -= (pOther->bPurgeable - pCache->bPurgeable);
} }
} }
/* Step 5. If a usable page buffer has still not been found, /* Step 5. If a usable page buffer has still not been found,
** attempt to allocate a new one. ** attempt to allocate a new one.
*/ */
if( !pPage ){ if (!pPage) {
pPage = pcache1AllocPage(pCache, createFlag==1); pPage = pcache1AllocPage(pCache, createFlag == 1);
} }
if( pPage ){ if (pPage) {
unsigned int h = iKey % pCache->nHash; unsigned int h = iKey % pCache->nHash;
pCache->nPage++; pCache->nPage++;
pPage->iKey = iKey; pPage->iKey = iKey;
...@@ -946,7 +913,7 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2( ...@@ -946,7 +913,7 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
** No need to clear pLruPrev since it is not accessed when pLruNext==0 */ ** No need to clear pLruPrev since it is not accessed when pLruNext==0 */
*(void **)pPage->page.pExtra = 0; *(void **)pPage->page.pExtra = 0;
pCache->apHash[h] = pPage; pCache->apHash[h] = pPage;
if( iKey>pCache->iMaxKey ){ if (iKey > pCache->iMaxKey) {
pCache->iMaxKey = iKey; pCache->iMaxKey = iKey;
} }
} }
...@@ -954,13 +921,13 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2( ...@@ -954,13 +921,13 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
} }
/* /*
** Implementation of the sqlite3_pcache.xFetch method. ** Implementation of the sqlite3_pcache.xFetch method.
** **
** Fetch a page by key value. ** Fetch a page by key value.
** **
** Whether or not a new page may be allocated by this function depends on ** Whether or not a new page may be allocated by this function depends on
** the value of the createFlag argument. 0 means do not allocate a new ** the value of the createFlag argument. 0 means do not allocate a new
** page. 1 means allocate a new page if space is easily available. 2 ** page. 1 means allocate a new page if space is easily available. 2
** means to try really hard to allocate a new page. ** means to try really hard to allocate a new page.
** **
** For a non-purgeable cache (a cache used as the storage for an in-memory ** For a non-purgeable cache (a cache used as the storage for an in-memory
...@@ -971,7 +938,7 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2( ...@@ -971,7 +938,7 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
** There are three different approaches to obtaining space for a page, ** There are three different approaches to obtaining space for a page,
** depending on the value of parameter createFlag (which may be 0, 1 or 2). ** depending on the value of parameter createFlag (which may be 0, 1 or 2).
** **
** 1. Regardless of the value of createFlag, the cache is searched for a ** 1. Regardless of the value of createFlag, the cache is searched for a
** copy of the requested page. If one is found, it is returned. ** copy of the requested page. If one is found, it is returned.
** **
** 2. If createFlag==0 and the page is not already in the cache, NULL is ** 2. If createFlag==0 and the page is not already in the cache, NULL is
...@@ -985,13 +952,13 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2( ...@@ -985,13 +952,13 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
** PCache1.nMax, or ** PCache1.nMax, or
** **
** (b) the number of pages pinned by the cache is greater than ** (b) the number of pages pinned by the cache is greater than
** the sum of nMax for all purgeable caches, less the sum of ** the sum of nMax for all purgeable caches, less the sum of
** nMin for all other purgeable caches, or ** nMin for all other purgeable caches, or
** **
** 4. If none of the first three conditions apply and the cache is marked ** 4. If none of the first three conditions apply and the cache is marked
** as purgeable, and if one of the following is true: ** as purgeable, and if one of the following is true:
** **
** (a) The number of pages allocated for the cache is already ** (a) The number of pages allocated for the cache is already
** PCache1.nMax, or ** PCache1.nMax, or
** **
** (b) The number of pages allocated for all purgeable caches is ** (b) The number of pages allocated for all purgeable caches is
...@@ -1003,7 +970,7 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2( ...@@ -1003,7 +970,7 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
** **
** then attempt to recycle a page from the LRU list. If it is the right ** then attempt to recycle a page from the LRU list. If it is the right
** size, return the recycled buffer. Otherwise, free the buffer and ** size, return the recycled buffer. Otherwise, free the buffer and
** proceed to step 5. ** proceed to step 5.
** **
** 5. Otherwise, allocate and return a new page buffer. ** 5. Otherwise, allocate and return a new page buffer.
** **
...@@ -1012,103 +979,88 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2( ...@@ -1012,103 +979,88 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
** the common case where pGroup->mutex is NULL. The pcache1Fetch() wrapper ** the common case where pGroup->mutex is NULL. The pcache1Fetch() wrapper
** invokes the appropriate routine. ** invokes the appropriate routine.
*/ */
static PgHdr1 *pcache1FetchNoMutex( static PgHdr1 *pcache1FetchNoMutex(sqlite3_pcache *p, unsigned int iKey, int createFlag) {
sqlite3_pcache *p,
unsigned int iKey,
int createFlag
){
PCache1 *pCache = (PCache1 *)p; PCache1 *pCache = (PCache1 *)p;
PgHdr1 *pPage = 0; PgHdr1 * pPage = 0;
/* Step 1: Search the hash table for an existing entry. */ /* Step 1: Search the hash table for an existing entry. */
pPage = pCache->apHash[iKey % pCache->nHash]; pPage = pCache->apHash[iKey % pCache->nHash];
while( pPage && pPage->iKey!=iKey ){ pPage = pPage->pNext; } while (pPage && pPage->iKey != iKey) {
pPage = pPage->pNext;
}
/* Step 2: If the page was found in the hash table, then return it. /* Step 2: If the page was found in the hash table, then return it.
** If the page was not in the hash table and createFlag is 0, abort. ** If the page was not in the hash table and createFlag is 0, abort.
** Otherwise (page not in hash and createFlag!=0) continue with ** Otherwise (page not in hash and createFlag!=0) continue with
** subsequent steps to try to create the page. */ ** subsequent steps to try to create the page. */
if( pPage ){ if (pPage) {
if( PAGE_IS_UNPINNED(pPage) ){ if (PAGE_IS_UNPINNED(pPage)) {
return pcache1PinPage(pPage); return pcache1PinPage(pPage);
}else{ } else {
return pPage; return pPage;
} }
}else if( createFlag ){ } else if (createFlag) {
/* Steps 3, 4, and 5 implemented by this subroutine */ /* Steps 3, 4, and 5 implemented by this subroutine */
return pcache1FetchStage2(pCache, iKey, createFlag); return pcache1FetchStage2(pCache, iKey, createFlag);
}else{ } else {
return 0; return 0;
} }
} }
#if PCACHE1_MIGHT_USE_GROUP_MUTEX #if PCACHE1_MIGHT_USE_GROUP_MUTEX
static PgHdr1 *pcache1FetchWithMutex( static PgHdr1 *pcache1FetchWithMutex(sqlite3_pcache *p, unsigned int iKey, int createFlag) {
sqlite3_pcache *p,
unsigned int iKey,
int createFlag
){
PCache1 *pCache = (PCache1 *)p; PCache1 *pCache = (PCache1 *)p;
PgHdr1 *pPage; PgHdr1 * pPage;
pcache1EnterMutex(pCache->pGroup); pcache1EnterMutex(pCache->pGroup);
pPage = pcache1FetchNoMutex(p, iKey, createFlag); pPage = pcache1FetchNoMutex(p, iKey, createFlag);
assert( pPage==0 || pCache->iMaxKey>=iKey ); assert(pPage == 0 || pCache->iMaxKey >= iKey);
pcache1LeaveMutex(pCache->pGroup); pcache1LeaveMutex(pCache->pGroup);
return pPage; return pPage;
} }
#endif #endif
static sqlite3_pcache_page *pcache1Fetch( static sqlite3_pcache_page *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag) {
sqlite3_pcache *p,
unsigned int iKey,
int createFlag
){
#if PCACHE1_MIGHT_USE_GROUP_MUTEX || defined(SQLITE_DEBUG) #if PCACHE1_MIGHT_USE_GROUP_MUTEX || defined(SQLITE_DEBUG)
PCache1 *pCache = (PCache1 *)p; PCache1 *pCache = (PCache1 *)p;
#endif #endif
assert( offsetof(PgHdr1,page)==0 ); assert(offsetof(PgHdr1, page) == 0);
assert( pCache->bPurgeable || createFlag!=1 ); assert(pCache->bPurgeable || createFlag != 1);
assert( pCache->bPurgeable || pCache->nMin==0 ); assert(pCache->bPurgeable || pCache->nMin == 0);
assert( pCache->bPurgeable==0 || pCache->nMin==10 ); assert(pCache->bPurgeable == 0 || pCache->nMin == 10);
assert( pCache->nMin==0 || pCache->bPurgeable ); assert(pCache->nMin == 0 || pCache->bPurgeable);
assert( pCache->nHash>0 ); assert(pCache->nHash > 0);
#if PCACHE1_MIGHT_USE_GROUP_MUTEX #if PCACHE1_MIGHT_USE_GROUP_MUTEX
if( pCache->pGroup->mutex ){ if (pCache->pGroup->mutex) {
return (sqlite3_pcache_page*)pcache1FetchWithMutex(p, iKey, createFlag); return (sqlite3_pcache_page *)pcache1FetchWithMutex(p, iKey, createFlag);
}else } else
#endif #endif
{ {
return (sqlite3_pcache_page*)pcache1FetchNoMutex(p, iKey, createFlag); return (sqlite3_pcache_page *)pcache1FetchNoMutex(p, iKey, createFlag);
} }
} }
/* /*
** Implementation of the sqlite3_pcache.xUnpin method. ** Implementation of the sqlite3_pcache.xUnpin method.
** **
** Mark a page as unpinned (eligible for asynchronous recycling). ** Mark a page as unpinned (eligible for asynchronous recycling).
*/ */
static void pcache1Unpin( static void pcache1Unpin(sqlite3_pcache *p, sqlite3_pcache_page *pPg, int reuseUnlikely) {
sqlite3_pcache *p,
sqlite3_pcache_page *pPg,
int reuseUnlikely
){
PCache1 *pCache = (PCache1 *)p; PCache1 *pCache = (PCache1 *)p;
PgHdr1 *pPage = (PgHdr1 *)pPg; PgHdr1 * pPage = (PgHdr1 *)pPg;
PGroup *pGroup = pCache->pGroup; PGroup * pGroup = pCache->pGroup;
assert( pPage->pCache==pCache ); assert(pPage->pCache == pCache);
pcache1EnterMutex(pGroup); pcache1EnterMutex(pGroup);
/* It is an error to call this function if the page is already /* It is an error to call this function if the page is already
** part of the PGroup LRU list. ** part of the PGroup LRU list.
*/ */
assert( pPage->pLruNext==0 ); assert(pPage->pLruNext == 0);
assert( PAGE_IS_PINNED(pPage) ); assert(PAGE_IS_PINNED(pPage));
if( reuseUnlikely || pGroup->nPurgeable>pGroup->nMaxPage ){ if (reuseUnlikely || pGroup->nPurgeable > pGroup->nMaxPage) {
pcache1RemoveFromHash(pPage, 1); pcache1RemoveFromHash(pPage, 1);
}else{ } else {
/* Add the page to the PGroup LRU list. */ /* Add the page to the PGroup LRU list. */
PgHdr1 **ppFirst = &pGroup->lru.pLruNext; PgHdr1 **ppFirst = &pGroup->lru.pLruNext;
pPage->pLruPrev = &pGroup->lru; pPage->pLruPrev = &pGroup->lru;
...@@ -1121,35 +1073,30 @@ static void pcache1Unpin( ...@@ -1121,35 +1073,30 @@ static void pcache1Unpin(
} }
/* /*
** Implementation of the sqlite3_pcache.xRekey method. ** Implementation of the sqlite3_pcache.xRekey method.
*/ */
static void pcache1Rekey( static void pcache1Rekey(sqlite3_pcache *p, sqlite3_pcache_page *pPg, unsigned int iOld, unsigned int iNew) {
sqlite3_pcache *p, PCache1 * pCache = (PCache1 *)p;
sqlite3_pcache_page *pPg, PgHdr1 * pPage = (PgHdr1 *)pPg;
unsigned int iOld, PgHdr1 ** pp;
unsigned int iNew unsigned int h;
){ assert(pPage->iKey == iOld);
PCache1 *pCache = (PCache1 *)p; assert(pPage->pCache == pCache);
PgHdr1 *pPage = (PgHdr1 *)pPg;
PgHdr1 **pp;
unsigned int h;
assert( pPage->iKey==iOld );
assert( pPage->pCache==pCache );
pcache1EnterMutex(pCache->pGroup); pcache1EnterMutex(pCache->pGroup);
h = iOld%pCache->nHash; h = iOld % pCache->nHash;
pp = &pCache->apHash[h]; pp = &pCache->apHash[h];
while( (*pp)!=pPage ){ while ((*pp) != pPage) {
pp = &(*pp)->pNext; pp = &(*pp)->pNext;
} }
*pp = pPage->pNext; *pp = pPage->pNext;
h = iNew%pCache->nHash; h = iNew % pCache->nHash;
pPage->iKey = iNew; pPage->iKey = iNew;
pPage->pNext = pCache->apHash[h]; pPage->pNext = pCache->apHash[h];
pCache->apHash[h] = pPage; pCache->apHash[h] = pPage;
if( iNew>pCache->iMaxKey ){ if (iNew > pCache->iMaxKey) {
pCache->iMaxKey = iNew; pCache->iMaxKey = iNew;
} }
...@@ -1157,36 +1104,36 @@ static void pcache1Rekey( ...@@ -1157,36 +1104,36 @@ static void pcache1Rekey(
} }
/* /*
** Implementation of the sqlite3_pcache.xTruncate method. ** Implementation of the sqlite3_pcache.xTruncate method.
** **
** Discard all unpinned pages in the cache with a page number equal to ** Discard all unpinned pages in the cache with a page number equal to
** or greater than parameter iLimit. Any pinned pages with a page number ** or greater than parameter iLimit. Any pinned pages with a page number
** equal to or greater than iLimit are implicitly unpinned. ** equal to or greater than iLimit are implicitly unpinned.
*/ */
static void pcache1Truncate(sqlite3_pcache *p, unsigned int iLimit){ static void pcache1Truncate(sqlite3_pcache *p, unsigned int iLimit) {
PCache1 *pCache = (PCache1 *)p; PCache1 *pCache = (PCache1 *)p;
pcache1EnterMutex(pCache->pGroup); pcache1EnterMutex(pCache->pGroup);
if( iLimit<=pCache->iMaxKey ){ if (iLimit <= pCache->iMaxKey) {
pcache1TruncateUnsafe(pCache, iLimit); pcache1TruncateUnsafe(pCache, iLimit);
pCache->iMaxKey = iLimit-1; pCache->iMaxKey = iLimit - 1;
} }
pcache1LeaveMutex(pCache->pGroup); pcache1LeaveMutex(pCache->pGroup);
} }
/* /*
** Implementation of the sqlite3_pcache.xDestroy method. ** Implementation of the sqlite3_pcache.xDestroy method.
** **
** Destroy a cache allocated using pcache1Create(). ** Destroy a cache allocated using pcache1Create().
*/ */
static void pcache1Destroy(sqlite3_pcache *p){ static void pcache1Destroy(sqlite3_pcache *p) {
PCache1 *pCache = (PCache1 *)p; PCache1 *pCache = (PCache1 *)p;
PGroup *pGroup = pCache->pGroup; PGroup * pGroup = pCache->pGroup;
assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) ); assert(pCache->bPurgeable || (pCache->nMax == 0 && pCache->nMin == 0));
pcache1EnterMutex(pGroup); pcache1EnterMutex(pGroup);
if( pCache->nPage ) pcache1TruncateUnsafe(pCache, 0); if (pCache->nPage) pcache1TruncateUnsafe(pCache, 0);
assert( pGroup->nMaxPage >= pCache->nMax ); assert(pGroup->nMaxPage >= pCache->nMax);
pGroup->nMaxPage -= pCache->nMax; pGroup->nMaxPage -= pCache->nMax;
assert( pGroup->nMinPage >= pCache->nMin ); assert(pGroup->nMinPage >= pCache->nMin);
pGroup->nMinPage -= pCache->nMin; pGroup->nMinPage -= pCache->nMin;
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pcache1EnforceMaxPage(pCache); pcache1EnforceMaxPage(pCache);
...@@ -1196,42 +1143,16 @@ static void pcache1Destroy(sqlite3_pcache *p){ ...@@ -1196,42 +1143,16 @@ static void pcache1Destroy(sqlite3_pcache *p){
sqlite3_free(pCache); sqlite3_free(pCache);
} }
/*
** This function is called during initialization (sqlite3_initialize()) to
** install the default pluggable cache module, assuming the user has not
** already provided an alternative.
*/
void sqlite3PCacheSetDefault(void){
static const sqlite3_pcache_methods2 defaultMethods = {
1, /* iVersion */
0, /* pArg */
pcache1Init, /* xInit */
pcache1Shutdown, /* xShutdown */
pcache1Create, /* xCreate */
pcache1Cachesize, /* xCachesize */
pcache1Pagecount, /* xPagecount */
pcache1Fetch, /* xFetch */
pcache1Unpin, /* xUnpin */
pcache1Rekey, /* xRekey */
pcache1Truncate, /* xTruncate */
pcache1Destroy, /* xDestroy */
pcache1Shrink /* xShrink */
};
sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultMethods);
}
/* /*
** Return the size of the header on each page of this PCACHE implementation. ** Return the size of the header on each page of this PCACHE implementation.
*/ */
int sqlite3HeaderSizePcache1(void){ return ROUND8(sizeof(PgHdr1)); } int sqlite3HeaderSizePcache1(void) { return ROUND8(sizeof(PgHdr1)); }
/* /*
** Return the global mutex used by this PCACHE implementation. The ** Return the global mutex used by this PCACHE implementation. The
** sqlite3_status() routine needs access to this mutex. ** sqlite3_status() routine needs access to this mutex.
*/ */
sqlite3_mutex *sqlite3Pcache1Mutex(void){ sqlite3_mutex *sqlite3Pcache1Mutex(void) { return pcache1.mutex; }
return pcache1.mutex;
}
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
/* /*
...@@ -1240,25 +1161,22 @@ sqlite3_mutex *sqlite3Pcache1Mutex(void){ ...@@ -1240,25 +1161,22 @@ sqlite3_mutex *sqlite3Pcache1Mutex(void){
** by the current thread may be sqlite3_free()ed. ** by the current thread may be sqlite3_free()ed.
** **
** nReq is the number of bytes of memory required. Once this much has ** nReq is the number of bytes of memory required. Once this much has
** been released, the function returns. The return value is the total number ** been released, the function returns. The return value is the total number
** of bytes of memory released. ** of bytes of memory released.
*/ */
int sqlite3PcacheReleaseMemory(int nReq){ int sqlite3PcacheReleaseMemory(int nReq) {
int nFree = 0; int nFree = 0;
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) ); assert(sqlite3_mutex_notheld(pcache1.grp.mutex));
assert( sqlite3_mutex_notheld(pcache1.mutex) ); assert(sqlite3_mutex_notheld(pcache1.mutex));
if( sqlite3GlobalConfig.pPage==0 ){ if (sqlite3GlobalConfig.pPage == 0) {
PgHdr1 *p; PgHdr1 *p;
pcache1EnterMutex(&pcache1.grp); pcache1EnterMutex(&pcache1.grp);
while( (nReq<0 || nFree<nReq) while ((nReq < 0 || nFree < nReq) && (p = pcache1.grp.lru.pLruPrev) != 0 && p->isAnchor == 0) {
&& (p=pcache1.grp.lru.pLruPrev)!=0
&& p->isAnchor==0
){
nFree += pcache1MemSize(p->page.pBuf); nFree += pcache1MemSize(p->page.pBuf);
#ifdef SQLITE_PCACHE_SEPARATE_HEADER #ifdef SQLITE_PCACHE_SEPARATE_HEADER
nFree += sqlite3MemSize(p); nFree += sqlite3MemSize(p);
#endif #endif
assert( PAGE_IS_UNPINNED(p) ); assert(PAGE_IS_UNPINNED(p));
pcache1PinPage(p); pcache1PinPage(p);
pcache1RemoveFromHash(p, 1); pcache1RemoveFromHash(p, 1);
} }
...@@ -1273,16 +1191,15 @@ int sqlite3PcacheReleaseMemory(int nReq){ ...@@ -1273,16 +1191,15 @@ int sqlite3PcacheReleaseMemory(int nReq){
** This function is used by test procedures to inspect the internal state ** This function is used by test procedures to inspect the internal state
** of the global cache. ** of the global cache.
*/ */
void sqlite3PcacheStats( void sqlite3PcacheStats(int *pnCurrent, /* OUT: Total number of pages cached */
int *pnCurrent, /* OUT: Total number of pages cached */ int *pnMax, /* OUT: Global maximum cache size */
int *pnMax, /* OUT: Global maximum cache size */ int *pnMin, /* OUT: Sum of PCache1.nMin for purgeable caches */
int *pnMin, /* OUT: Sum of PCache1.nMin for purgeable caches */ int *pnRecyclable /* OUT: Total number of pages available for recycling */
int *pnRecyclable /* OUT: Total number of pages available for recycling */ ) {
){
PgHdr1 *p; PgHdr1 *p;
int nRecyclable = 0; int nRecyclable = 0;
for(p=pcache1.grp.lru.pLruNext; p && !p->isAnchor; p=p->pLruNext){ for (p = pcache1.grp.lru.pLruNext; p && !p->isAnchor; p = p->pLruNext) {
assert( PAGE_IS_UNPINNED(p) ); assert(PAGE_IS_UNPINNED(p));
nRecyclable++; nRecyclable++;
} }
*pnCurrent = pcache1.grp.nPurgeable; *pnCurrent = pcache1.grp.nPurgeable;
...@@ -1291,3 +1208,19 @@ void sqlite3PcacheStats( ...@@ -1291,3 +1208,19 @@ void sqlite3PcacheStats(
*pnRecyclable = nRecyclable; *pnRecyclable = nRecyclable;
} }
#endif #endif
sqlite3_pcache_methods2 pcache2 = {
1, /* iVersion */
0, /* pArg */
pcache1Init, /* xInit */
pcache1Shutdown, /* xShutdown */
pcache1Create, /* xCreate */
pcache1Cachesize, /* xCachesize */
pcache1Pagecount, /* xPagecount */
pcache1Fetch, /* xFetch */
pcache1Unpin, /* xUnpin */
pcache1Rekey, /* xRekey */
pcache1Truncate, /* xTruncate */
pcache1Destroy, /* xDestroy */
pcache1Shrink /* xShrink */
};
...@@ -13,7 +13,10 @@ ...@@ -13,7 +13,10 @@
** **
*/ */
#include <assert.h>
#include <pthread.h>
#include <stdint.h> #include <stdint.h>
#include <string.h>
#ifndef SQLITEINT_H #ifndef SQLITEINT_H
#define SQLITEINT_H #define SQLITEINT_H
...@@ -32,6 +35,10 @@ typedef struct sqlite3_pcache_page { ...@@ -32,6 +35,10 @@ typedef struct sqlite3_pcache_page {
void *pExtra; /* Extra information associated with the page */ void *pExtra; /* Extra information associated with the page */
} sqlite3_pcache_page; } sqlite3_pcache_page;
#define ROUNDDOWN8(x) ((x) & ~7)
#define ROUND8(x) (((x) + 7) & ~7)
typedef u32 Pgno; typedef u32 Pgno;
typedef struct Pager Pager; typedef struct Pager Pager;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册