#include "sqliteInt.h"
typedef struct PCache1 PCache1;
typedef struct PgHdr1 PgHdr1;
typedef struct PgFreeslot PgFreeslot;
typedef struct PGroup PGroup;
struct PgHdr1 {
sqlite3_pcache_page page;
unsigned int iKey;
u16 isBulkLocal;
u16 isAnchor;
PgHdr1 *pNext;
PCache1 *pCache;
PgHdr1 *pLruNext;
PgHdr1 *pLruPrev;
};
#define PAGE_IS_PINNED(p) ((p)->pLruNext==0)
#define PAGE_IS_UNPINNED(p) ((p)->pLruNext!=0)
struct PGroup {
sqlite3_mutex *mutex;
unsigned int nMaxPage;
unsigned int nMinPage;
unsigned int mxPinned;
unsigned int nPurgeable;
PgHdr1 lru;
};
struct PCache1 {
PGroup *pGroup;
unsigned int *pnPurgeable;
int szPage;
int szExtra;
int szAlloc;
int bPurgeable;
unsigned int nMin;
unsigned int nMax;
unsigned int n90pct;
unsigned int iMaxKey;
unsigned int nPurgeableDummy;
unsigned int nRecyclable;
unsigned int nPage;
unsigned int nHash;
PgHdr1 **apHash;
PgHdr1 *pFree;
void *pBulk;
};
struct PgFreeslot {
PgFreeslot *pNext;
};
static SQLITE_WSD struct PCacheGlobal {
PGroup grp;
int isInit;
int separateCache;
int nInitPage;
int szSlot;
int nSlot;
int nReserve;
void *pStart, *pEnd;
sqlite3_mutex *mutex;
PgFreeslot *pFree;
int nFreeSlot;
int bUnderPressure;
} pcache1_g;
#define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g))
#if !defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0
# define pcache1EnterMutex(X) assert((X)->mutex==0)
# define pcache1LeaveMutex(X) assert((X)->mutex==0)
# define PCACHE1_MIGHT_USE_GROUP_MUTEX 0
#else
# define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex)
# define pcache1LeaveMutex(X) sqlite3_mutex_leave((X)->mutex)
# define PCACHE1_MIGHT_USE_GROUP_MUTEX 1
#endif
void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
if( pcache1.isInit ){
PgFreeslot *p;
if( pBuf==0 ) sz = n = 0;
if( n==0 ) sz = 0;
sz = ROUNDDOWN8(sz);
pcache1.szSlot = sz;
pcache1.nSlot = pcache1.nFreeSlot = n;
pcache1.nReserve = n>90 ? 10 : (n/10 + 1);
pcache1.pStart = pBuf;
pcache1.pFree = 0;
pcache1.bUnderPressure = 0;
while( n-- ){
p = (PgFreeslot*)pBuf;
p->pNext = pcache1.pFree;
pcache1.pFree = p;
pBuf = (void*)&((char*)pBuf)[sz];
}
pcache1.pEnd = pBuf;
}
}
static int pcache1InitBulk(PCache1 *pCache){
i64 szBulk;
char *zBulk;
if( pcache1.nInitPage==0 ) return 0;
if( pCache->nMax<3 ) return 0;
sqlite3BeginBenignMalloc();
if( pcache1.nInitPage>0 ){
szBulk = pCache->szAlloc * (i64)pcache1.nInitPage;
}else{
szBulk = -1024 * (i64)pcache1.nInitPage;
}
if( szBulk > pCache->szAlloc*(i64)pCache->nMax ){
szBulk = pCache->szAlloc*(i64)pCache->nMax;
}
zBulk = pCache->pBulk = sqlite3Malloc( szBulk );
sqlite3EndBenignMalloc();
if( zBulk ){
int nBulk = sqlite3MallocSize(zBulk)/pCache->szAlloc;
do{
PgHdr1 *pX = (PgHdr1*)&zBulk[pCache->szPage];
pX->page.pBuf = zBulk;
pX->page.pExtra = &pX[1];
pX->isBulkLocal = 1;
pX->isAnchor = 0;
pX->pNext = pCache->pFree;
pX->pLruPrev = 0;
pCache->pFree = pX;
zBulk += pCache->szAlloc;
}while( --nBulk );
}
return pCache->pFree!=0;
}
static void *pcache1Alloc(int nByte){
void *p = 0;
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
if( nByte<=pcache1.szSlot ){
sqlite3_mutex_enter(pcache1.mutex);
p = (PgHdr1 *)pcache1.pFree;
if( p ){
pcache1.pFree = pcache1.pFree->pNext;
pcache1.nFreeSlot--;
pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
assert( pcache1.nFreeSlot>=0 );
sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1);
}
sqlite3_mutex_leave(pcache1.mutex);
}
if( p==0 ){
p = sqlite3Malloc(nByte);
#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
if( p ){
int sz = sqlite3MallocSize(p);
sqlite3_mutex_enter(pcache1.mutex);
sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
sqlite3_mutex_leave(pcache1.mutex);
}
#endif
sqlite3MemdebugSetType(p, MEMTYPE_PCACHE);
}
return p;
}
static void pcache1Free(void *p){
if( p==0 ) return;
if( SQLITE_WITHIN(p, pcache1.pStart, pcache1.pEnd) ){
PgFreeslot *pSlot;
sqlite3_mutex_enter(pcache1.mutex);
sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1);
pSlot = (PgFreeslot*)p;
pSlot->pNext = pcache1.pFree;
pcache1.pFree = pSlot;
pcache1.nFreeSlot++;
pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
assert( pcache1.nFreeSlot<=pcache1.nSlot );
sqlite3_mutex_leave(pcache1.mutex);
}else{
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
{
int nFreed = 0;
nFreed = sqlite3MallocSize(p);
sqlite3_mutex_enter(pcache1.mutex);
sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed);
sqlite3_mutex_leave(pcache1.mutex);
}
#endif
sqlite3_free(p);
}
}
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
static int pcache1MemSize(void *p){
if( p>=pcache1.pStart && p<pcache1.pEnd ){
return pcache1.szSlot;
}else{
int iSize;
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
iSize = sqlite3MallocSize(p);
sqlite3MemdebugSetType(p, MEMTYPE_PCACHE);
return iSize;
}
}
#endif
static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
PgHdr1 *p = 0;
void *pPg;
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
if( pCache->pFree || (pCache->nPage==0 && pcache1InitBulk(pCache)) ){
assert( pCache->pFree!=0 );
p = pCache->pFree;
pCache->pFree = p->pNext;
p->pNext = 0;
}else{
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
assert( pcache1.separateCache==0 );
assert( pCache->pGroup==&pcache1.grp );
pcache1LeaveMutex(pCache->pGroup);
#endif
if( benignMalloc ){ sqlite3BeginBenignMalloc(); }
pPg = pcache1Alloc(pCache->szAlloc);
if( benignMalloc ){ sqlite3EndBenignMalloc(); }
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
pcache1EnterMutex(pCache->pGroup);
#endif
if( pPg==0 ) return 0;
p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
p->page.pBuf = pPg;
p->page.pExtra = &p[1];
p->isBulkLocal = 0;
p->isAnchor = 0;
p->pLruPrev = 0;
}
(*pCache->pnPurgeable)++;
return p;
}
static void pcache1FreePage(PgHdr1 *p){
PCache1 *pCache;
assert( p!=0 );
pCache = p->pCache;
assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) );
if( p->isBulkLocal ){
p->pNext = pCache->pFree;
pCache->pFree = p;
}else{
pcache1Free(p->page.pBuf);
}
(*pCache->pnPurgeable)--;
}
void *sqlite3PageMalloc(int sz){
assert( sz<=65536+8 );
return pcache1Alloc(sz);
}
void sqlite3PageFree(void *p){
pcache1Free(p);
}
static int pcache1UnderMemoryPressure(PCache1 *pCache){
if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){
return pcache1.bUnderPressure;
}else{
return sqlite3HeapNearlyFull();
}
}
static void pcache1ResizeHash(PCache1 *p){
PgHdr1 **apNew;
unsigned int nNew;
unsigned int i;
assert( sqlite3_mutex_held(p->pGroup->mutex) );
nNew = p->nHash*2;
if( nNew<256 ){
nNew = 256;
}
pcache1LeaveMutex(p->pGroup);
if( p->nHash ){ sqlite3BeginBenignMalloc(); }
apNew = (PgHdr1 **)sqlite3MallocZero(sizeof(PgHdr1 *)*nNew);
if( p->nHash ){ sqlite3EndBenignMalloc(); }
pcache1EnterMutex(p->pGroup);
if( apNew ){
for(i=0; i<p->nHash; i++){
PgHdr1 *pPage;
PgHdr1 *pNext = p->apHash[i];
while( (pPage = pNext)!=0 ){
unsigned int h = pPage->iKey % nNew;
pNext = pPage->pNext;
pPage->pNext = apNew[h];
apNew[h] = pPage;
}
}
sqlite3_free(p->apHash);
p->apHash = apNew;
p->nHash = nNew;
}
}
static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){
assert( pPage!=0 );
assert( PAGE_IS_UNPINNED(pPage) );
assert( pPage->pLruNext );
assert( pPage->pLruPrev );
assert( sqlite3_mutex_held(pPage->pCache->pGroup->mutex) );
pPage->pLruPrev->pLruNext = pPage->pLruNext;
pPage->pLruNext->pLruPrev = pPage->pLruPrev;
pPage->pLruNext = 0;
assert( pPage->isAnchor==0 );
assert( pPage->pCache->pGroup->lru.isAnchor==1 );
pPage->pCache->nRecyclable--;
return pPage;
}
static void pcache1RemoveFromHash(PgHdr1 *pPage, int freeFlag){
unsigned int h;
PCache1 *pCache = pPage->pCache;
PgHdr1 **pp;
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
h = pPage->iKey % pCache->nHash;
for(pp=&pCache->apHash[h]; (*pp)!=pPage; pp=&(*pp)->pNext);
*pp = (*pp)->pNext;
pCache->nPage--;
if( freeFlag ) pcache1FreePage(pPage);
}
static void pcache1EnforceMaxPage(PCache1 *pCache){
PGroup *pGroup = pCache->pGroup;
PgHdr1 *p;
assert( sqlite3_mutex_held(pGroup->mutex) );
while( pGroup->nPurgeable>pGroup->nMaxPage
&& (p=pGroup->lru.pLruPrev)->isAnchor==0
){
assert( p->pCache->pGroup==pGroup );
assert( PAGE_IS_UNPINNED(p) );
pcache1PinPage(p);
pcache1RemoveFromHash(p, 1);
}
if( pCache->nPage==0 && pCache->pBulk ){
sqlite3_free(pCache->pBulk);
pCache->pBulk = pCache->pFree = 0;
}
}
static void pcache1TruncateUnsafe(
PCache1 *pCache,
unsigned int iLimit
){
TESTONLY( int nPage = 0; )
unsigned int h, iStop;
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
assert( pCache->iMaxKey >= iLimit );
assert( pCache->nHash > 0 );
if( pCache->iMaxKey - iLimit < pCache->nHash ){
h = iLimit % pCache->nHash;
iStop = pCache->iMaxKey % pCache->nHash;
TESTONLY( nPage = -10; )
}else{
h = pCache->nHash/2;
iStop = h - 1;
}
for(;;){
PgHdr1 **pp;
PgHdr1 *pPage;
assert( h<pCache->nHash );
pp = &pCache->apHash[h];
while( (pPage = *pp)!=0 ){
if( pPage->iKey>=iLimit ){
pCache->nPage--;
*pp = pPage->pNext;
if( PAGE_IS_UNPINNED(pPage) ) pcache1PinPage(pPage);
pcache1FreePage(pPage);
}else{
pp = &pPage->pNext;
TESTONLY( if( nPage>=0 ) nPage++; )
}
}
if( h==iStop ) break;
h = (h+1) % pCache->nHash;
}
assert( nPage<0 || pCache->nPage==(unsigned)nPage );
}
static int pcache1Init(void *NotUsed){
UNUSED_PARAMETER(NotUsed);
assert( pcache1.isInit==0 );
memset(&pcache1, 0, sizeof(pcache1));
#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT)
pcache1.separateCache = 0;
#elif SQLITE_THREADSAFE
pcache1.separateCache = sqlite3GlobalConfig.pPage==0
|| sqlite3GlobalConfig.bCoreMutex>0;
#else
pcache1.separateCache = sqlite3GlobalConfig.pPage==0;
#endif
#if SQLITE_THREADSAFE
if( sqlite3GlobalConfig.bCoreMutex ){
pcache1.grp.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU);
pcache1.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PMEM);
}
#endif
if( pcache1.separateCache
&& sqlite3GlobalConfig.nPage!=0
&& sqlite3GlobalConfig.pPage==0
){
pcache1.nInitPage = sqlite3GlobalConfig.nPage;
}else{
pcache1.nInitPage = 0;
}
pcache1.grp.mxPinned = 10;
pcache1.isInit = 1;
return SQLITE_OK;
}
static void pcache1Shutdown(void *NotUsed){
UNUSED_PARAMETER(NotUsed);
assert( pcache1.isInit!=0 );
memset(&pcache1, 0, sizeof(pcache1));
}
static void pcache1Destroy(sqlite3_pcache *p);
static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
PCache1 *pCache;
PGroup *pGroup;
int sz;
assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 );
assert( szExtra < 300 );
sz = sizeof(PCache1) + sizeof(PGroup)*pcache1.separateCache;
pCache = (PCache1 *)sqlite3MallocZero(sz);
if( pCache ){
if( pcache1.separateCache ){
pGroup = (PGroup*)&pCache[1];
pGroup->mxPinned = 10;
}else{
pGroup = &pcache1.grp;
}
pcache1EnterMutex(pGroup);
if( pGroup->lru.isAnchor==0 ){
pGroup->lru.isAnchor = 1;
pGroup->lru.pLruPrev = pGroup->lru.pLruNext = &pGroup->lru;
}
pCache->pGroup = pGroup;
pCache->szPage = szPage;
pCache->szExtra = szExtra;
pCache->szAlloc = szPage + szExtra + ROUND8(sizeof(PgHdr1));
pCache->bPurgeable = (bPurgeable ? 1 : 0);
pcache1ResizeHash(pCache);
if( bPurgeable ){
pCache->nMin = 10;
pGroup->nMinPage += pCache->nMin;
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pCache->pnPurgeable = &pGroup->nPurgeable;
}else{
pCache->pnPurgeable = &pCache->nPurgeableDummy;
}
pcache1LeaveMutex(pGroup);
if( pCache->nHash==0 ){
pcache1Destroy((sqlite3_pcache*)pCache);
pCache = 0;
}
}
return (sqlite3_pcache *)pCache;
}
static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
PCache1 *pCache = (PCache1 *)p;
u32 n;
assert( nMax>=0 );
if( pCache->bPurgeable ){
PGroup *pGroup = pCache->pGroup;
pcache1EnterMutex(pGroup);
n = (u32)nMax;
if( n > 0x7fff0000 - pGroup->nMaxPage + pCache->nMax ){
n = 0x7fff0000 - pGroup->nMaxPage + pCache->nMax;
}
pGroup->nMaxPage += (n - pCache->nMax);
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pCache->nMax = n;
pCache->n90pct = pCache->nMax*9/10;
pcache1EnforceMaxPage(pCache);
pcache1LeaveMutex(pGroup);
}
}
static void pcache1Shrink(sqlite3_pcache *p){
PCache1 *pCache = (PCache1*)p;
if( pCache->bPurgeable ){
PGroup *pGroup = pCache->pGroup;
unsigned int savedMaxPage;
pcache1EnterMutex(pGroup);
savedMaxPage = pGroup->nMaxPage;
pGroup->nMaxPage = 0;
pcache1EnforceMaxPage(pCache);
pGroup->nMaxPage = savedMaxPage;
pcache1LeaveMutex(pGroup);
}
}
static int pcache1Pagecount(sqlite3_pcache *p){
int n;
PCache1 *pCache = (PCache1*)p;
pcache1EnterMutex(pCache->pGroup);
n = pCache->nPage;
pcache1LeaveMutex(pCache->pGroup);
return n;
}
static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
PCache1 *pCache,
unsigned int iKey,
int createFlag
){
unsigned int nPinned;
PGroup *pGroup = pCache->pGroup;
PgHdr1 *pPage = 0;
assert( pCache->nPage >= pCache->nRecyclable );
nPinned = pCache->nPage - pCache->nRecyclable;
assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage );
assert( pCache->n90pct == pCache->nMax*9/10 );
if( createFlag==1 && (
nPinned>=pGroup->mxPinned
|| nPinned>=pCache->n90pct
|| (pcache1UnderMemoryPressure(pCache) && pCache->nRecyclable<nPinned)
)){
return 0;
}
if( pCache->nPage>=pCache->nHash ) pcache1ResizeHash(pCache);
assert( pCache->nHash>0 && pCache->apHash );
if( pCache->bPurgeable
&& !pGroup->lru.pLruPrev->isAnchor
&& ((pCache->nPage+1>=pCache->nMax) || pcache1UnderMemoryPressure(pCache))
){
PCache1 *pOther;
pPage = pGroup->lru.pLruPrev;
assert( PAGE_IS_UNPINNED(pPage) );
pcache1RemoveFromHash(pPage, 0);
pcache1PinPage(pPage);
pOther = pPage->pCache;
if( pOther->szAlloc != pCache->szAlloc ){
pcache1FreePage(pPage);
pPage = 0;
}else{
pGroup->nPurgeable -= (pOther->bPurgeable - pCache->bPurgeable);
}
}
if( !pPage ){
pPage = pcache1AllocPage(pCache, createFlag==1);
}
if( pPage ){
unsigned int h = iKey % pCache->nHash;
pCache->nPage++;
pPage->iKey = iKey;
pPage->pNext = pCache->apHash[h];
pPage->pCache = pCache;
pPage->pLruNext = 0;
*(void **)pPage->page.pExtra = 0;
pCache->apHash[h] = pPage;
if( iKey>pCache->iMaxKey ){
pCache->iMaxKey = iKey;
}
}
return pPage;
}
static PgHdr1 *pcache1FetchNoMutex(
sqlite3_pcache *p,
unsigned int iKey,
int createFlag
){
PCache1 *pCache = (PCache1 *)p;
PgHdr1 *pPage = 0;
pPage = pCache->apHash[iKey % pCache->nHash];
while( pPage && pPage->iKey!=iKey ){ pPage = pPage->pNext; }
if( pPage ){
if( PAGE_IS_UNPINNED(pPage) ){
return pcache1PinPage(pPage);
}else{
return pPage;
}
}else if( createFlag ){
return pcache1FetchStage2(pCache, iKey, createFlag);
}else{
return 0;
}
}
#if PCACHE1_MIGHT_USE_GROUP_MUTEX
static PgHdr1 *pcache1FetchWithMutex(
sqlite3_pcache *p,
unsigned int iKey,
int createFlag
){
PCache1 *pCache = (PCache1 *)p;
PgHdr1 *pPage;
pcache1EnterMutex(pCache->pGroup);
pPage = pcache1FetchNoMutex(p, iKey, createFlag);
assert( pPage==0 || pCache->iMaxKey>=iKey );
pcache1LeaveMutex(pCache->pGroup);
return pPage;
}
#endif
static sqlite3_pcache_page *pcache1Fetch(
sqlite3_pcache *p,
unsigned int iKey,
int createFlag
){
#if PCACHE1_MIGHT_USE_GROUP_MUTEX || defined(SQLITE_DEBUG)
PCache1 *pCache = (PCache1 *)p;
#endif
assert( offsetof(PgHdr1,page)==0 );
assert( pCache->bPurgeable || createFlag!=1 );
assert( pCache->bPurgeable || pCache->nMin==0 );
assert( pCache->bPurgeable==0 || pCache->nMin==10 );
assert( pCache->nMin==0 || pCache->bPurgeable );
assert( pCache->nHash>0 );
#if PCACHE1_MIGHT_USE_GROUP_MUTEX
if( pCache->pGroup->mutex ){
return (sqlite3_pcache_page*)pcache1FetchWithMutex(p, iKey, createFlag);
}else
#endif
{
return (sqlite3_pcache_page*)pcache1FetchNoMutex(p, iKey, createFlag);
}
}
static void pcache1Unpin(
sqlite3_pcache *p,
sqlite3_pcache_page *pPg,
int reuseUnlikely
){
PCache1 *pCache = (PCache1 *)p;
PgHdr1 *pPage = (PgHdr1 *)pPg;
PGroup *pGroup = pCache->pGroup;
assert( pPage->pCache==pCache );
pcache1EnterMutex(pGroup);
assert( pPage->pLruNext==0 );
assert( PAGE_IS_PINNED(pPage) );
if( reuseUnlikely || pGroup->nPurgeable>pGroup->nMaxPage ){
pcache1RemoveFromHash(pPage, 1);
}else{
PgHdr1 **ppFirst = &pGroup->lru.pLruNext;
pPage->pLruPrev = &pGroup->lru;
(pPage->pLruNext = *ppFirst)->pLruPrev = pPage;
*ppFirst = pPage;
pCache->nRecyclable++;
}
pcache1LeaveMutex(pCache->pGroup);
}
static void pcache1Rekey(
sqlite3_pcache *p,
sqlite3_pcache_page *pPg,
unsigned int iOld,
unsigned int iNew
){
PCache1 *pCache = (PCache1 *)p;
PgHdr1 *pPage = (PgHdr1 *)pPg;
PgHdr1 **pp;
unsigned int hOld, hNew;
assert( pPage->iKey==iOld );
assert( pPage->pCache==pCache );
assert( iOld!=iNew );
pcache1EnterMutex(pCache->pGroup);
assert( pcache1FetchNoMutex(p, iOld, 0)==pPage );
hOld = iOld%pCache->nHash;
pp = &pCache->apHash[hOld];
while( (*pp)!=pPage ){
pp = &(*pp)->pNext;
}
*pp = pPage->pNext;
assert( pcache1FetchNoMutex(p, iNew, 0)==0 );
hNew = iNew%pCache->nHash;
pPage->iKey = iNew;
pPage->pNext = pCache->apHash[hNew];
pCache->apHash[hNew] = pPage;
if( iNew>pCache->iMaxKey ){
pCache->iMaxKey = iNew;
}
pcache1LeaveMutex(pCache->pGroup);
}
static void pcache1Truncate(sqlite3_pcache *p, unsigned int iLimit){
PCache1 *pCache = (PCache1 *)p;
pcache1EnterMutex(pCache->pGroup);
if( iLimit<=pCache->iMaxKey ){
pcache1TruncateUnsafe(pCache, iLimit);
pCache->iMaxKey = iLimit-1;
}
pcache1LeaveMutex(pCache->pGroup);
}
static void pcache1Destroy(sqlite3_pcache *p){
PCache1 *pCache = (PCache1 *)p;
PGroup *pGroup = pCache->pGroup;
assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) );
pcache1EnterMutex(pGroup);
if( pCache->nPage ) pcache1TruncateUnsafe(pCache, 0);
assert( pGroup->nMaxPage >= pCache->nMax );
pGroup->nMaxPage -= pCache->nMax;
assert( pGroup->nMinPage >= pCache->nMin );
pGroup->nMinPage -= pCache->nMin;
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pcache1EnforceMaxPage(pCache);
pcache1LeaveMutex(pGroup);
sqlite3_free(pCache->pBulk);
sqlite3_free(pCache->apHash);
sqlite3_free(pCache);
}
void sqlite3PCacheSetDefault(void){
static const sqlite3_pcache_methods2 defaultMethods = {
1,
0,
pcache1Init,
pcache1Shutdown,
pcache1Create,
pcache1Cachesize,
pcache1Pagecount,
pcache1Fetch,
pcache1Unpin,
pcache1Rekey,
pcache1Truncate,
pcache1Destroy,
pcache1Shrink
};
sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultMethods);
}
int sqlite3HeaderSizePcache1(void){ return ROUND8(sizeof(PgHdr1)); }
sqlite3_mutex *sqlite3Pcache1Mutex(void){
return pcache1.mutex;
}
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
int sqlite3PcacheReleaseMemory(int nReq){
int nFree = 0;
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
assert( sqlite3_mutex_notheld(pcache1.mutex) );
if( sqlite3GlobalConfig.pPage==0 ){
PgHdr1 *p;
pcache1EnterMutex(&pcache1.grp);
while( (nReq<0 || nFree<nReq)
&& (p=pcache1.grp.lru.pLruPrev)!=0
&& p->isAnchor==0
){
nFree += pcache1MemSize(p->page.pBuf);
assert( PAGE_IS_UNPINNED(p) );
pcache1PinPage(p);
pcache1RemoveFromHash(p, 1);
}
pcache1LeaveMutex(&pcache1.grp);
}
return nFree;
}
#endif
#ifdef SQLITE_TEST
void sqlite3PcacheStats(
int *pnCurrent,
int *pnMax,
int *pnMin,
int *pnRecyclable
){
PgHdr1 *p;
int nRecyclable = 0;
for(p=pcache1.grp.lru.pLruNext; p && !p->isAnchor; p=p->pLruNext){
assert( PAGE_IS_UNPINNED(p) );
nRecyclable++;
}
*pnCurrent = pcache1.grp.nPurgeable;
*pnMax = (int)pcache1.grp.nMaxPage;
*pnMin = (int)pcache1.grp.nMinPage;
*pnRecyclable = nRecyclable;
}
#endif