📄 pcache.c
字号:
/*** 2008 August 05**** The author disclaims copyright to this source code. In place of** a legal notice, here is a blessing:**** May you do good and not evil.** May you find forgiveness for yourself and forgive others.** May you share freely, never taking more than you give.***************************************************************************** This file implements that page cache.**** @(#) $Id: pcache.c,v 1.38 2008/11/19 16:52:44 danielk1977 Exp $*/#include "sqliteInt.h"/*** A complete page cache is an instance of this structure.*/struct PCache { PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */ PgHdr *pSynced; /* Last synced page in dirty page list */ int nRef; /* Number of referenced pages */ int nMax; /* Configured cache size */ int nMin; /* Configured minimum cache size */ int szPage; /* Size of every page in this cache */ int szExtra; /* Size of extra space for each page */ int bPurgeable; /* True if pages are on backing store */ int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */ void *pStress; /* Argument to xStress */ sqlite3_pcache *pCache; /* Pluggable cache module */ PgHdr *pPage1;};/*** Some of the assert() macros in this code are too expensive to run** even during normal debugging. Use them only rarely on long-running** tests. Enable the expensive asserts using the** -DSQLITE_ENABLE_EXPENSIVE_ASSERT=1 compile-time option.*/#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT# define expensive_assert(X) assert(X)#else# define expensive_assert(X)#endif/********************************** Linked List Management ********************/#if !defined(NDEBUG) && defined(SQLITE_ENABLE_EXPENSIVE_ASSERT)/*** Check that the pCache->pSynced variable is set correctly. If it** is not, either fail an assert or return zero. Otherwise, return** non-zero. This is only used in debugging builds, as follows:**** expensive_assert( pcacheCheckSynced(pCache) );*/static int pcacheCheckSynced(PCache *pCache){ PgHdr *p; for(p=pCache->pDirtyTail; p!=pCache->pSynced; p=p->pDirtyPrev){ assert( p->nRef || (p->flags&PGHDR_NEED_SYNC) ); } return (p==0 || p->nRef || (p->flags&PGHDR_NEED_SYNC)==0);}#endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT *//*** Remove page pPage from the list of dirty pages.*/static void pcacheRemoveFromDirtyList(PgHdr *pPage){ PCache *p = pPage->pCache; assert( pPage->pDirtyNext || pPage==p->pDirtyTail ); assert( pPage->pDirtyPrev || pPage==p->pDirty ); /* Update the PCache1.pSynced variable if necessary. */ if( p->pSynced==pPage ){ PgHdr *pSynced = pPage->pDirtyPrev; while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){ pSynced = pSynced->pDirtyPrev; } p->pSynced = pSynced; } if( pPage->pDirtyNext ){ pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev; }else{ assert( pPage==p->pDirtyTail ); p->pDirtyTail = pPage->pDirtyPrev; } if( pPage->pDirtyPrev ){ pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext; }else{ assert( pPage==p->pDirty ); p->pDirty = pPage->pDirtyNext; } pPage->pDirtyNext = 0; pPage->pDirtyPrev = 0; expensive_assert( pcacheCheckSynced(p) );}/*** Add page pPage to the head of the dirty list (PCache1.pDirty is set to** pPage).*/static void pcacheAddToDirtyList(PgHdr *pPage){ PCache *p = pPage->pCache; assert( pPage->pDirtyNext==0 && pPage->pDirtyPrev==0 && p->pDirty!=pPage ); pPage->pDirtyNext = p->pDirty; if( pPage->pDirtyNext ){ assert( pPage->pDirtyNext->pDirtyPrev==0 ); pPage->pDirtyNext->pDirtyPrev = pPage; } p->pDirty = pPage; if( !p->pDirtyTail ){ p->pDirtyTail = pPage; } if( !p->pSynced && 0==(pPage->flags&PGHDR_NEED_SYNC) ){ p->pSynced = pPage; } expensive_assert( pcacheCheckSynced(p) );}/*** Wrapper around the pluggable caches xUnpin method. If the cache is** being used for an in-memory database, this function is a no-op.*/static void pcacheUnpin(PgHdr *p){ PCache *pCache = p->pCache; if( pCache->bPurgeable ){ if( p->pgno==1 ){ pCache->pPage1 = 0; } sqlite3GlobalConfig.pcache.xUnpin(pCache->pCache, p, 0); }}/*************************************************** General Interfaces ********** Initialize and shutdown the page cache subsystem. Neither of these ** functions are threadsafe.*/int sqlite3PcacheInitialize(void){ if( sqlite3GlobalConfig.pcache.xInit==0 ){ sqlite3PCacheSetDefault(); } return sqlite3GlobalConfig.pcache.xInit(sqlite3GlobalConfig.pcache.pArg);}void sqlite3PcacheShutdown(void){ if( sqlite3GlobalConfig.pcache.xShutdown ){ sqlite3GlobalConfig.pcache.xShutdown(sqlite3GlobalConfig.pcache.pArg); }}/*** Return the size in bytes of a PCache object.*/int sqlite3PcacheSize(void){ return sizeof(PCache); }/*** Create a new PCache object. Storage space to hold the object** has already been allocated and is passed in as the p pointer. ** The caller discovers how much space needs to be allocated by ** calling sqlite3PcacheSize().*/void sqlite3PcacheOpen( int szPage, /* Size of every page */ int szExtra, /* Extra space associated with each page */ int bPurgeable, /* True if pages are on backing store */ int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */ void *pStress, /* Argument to xStress */ PCache *p /* Preallocated space for the PCache */){ memset(p, 0, sizeof(PCache)); p->szPage = szPage; p->szExtra = szExtra; p->bPurgeable = bPurgeable; p->xStress = xStress; p->pStress = pStress; p->nMax = 100; p->nMin = 10;}/*** Change the page size for PCache object. The caller must ensure that there** are no outstanding page references when this function is called.*/void sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ assert( pCache->nRef==0 && pCache->pDirty==0 ); if( pCache->pCache ){ sqlite3GlobalConfig.pcache.xDestroy(pCache->pCache); pCache->pCache = 0; } pCache->szPage = szPage;}/*** Try to obtain a page from the cache.*/int sqlite3PcacheFetch( PCache *pCache, /* Obtain the page from this cache */ Pgno pgno, /* Page number to obtain */ int createFlag, /* If true, create page if it does not exist already */ PgHdr **ppPage /* Write the page here */){ PgHdr *pPage = 0; int eCreate; assert( pCache!=0 ); assert( pgno>0 ); /* If the pluggable cache (sqlite3_pcache*) has not been allocated, ** allocate it now. */ if( !pCache->pCache && createFlag ){ sqlite3_pcache *p; int nByte; nByte = pCache->szPage + pCache->szExtra + sizeof(PgHdr); p = sqlite3GlobalConfig.pcache.xCreate(nByte, pCache->bPurgeable); if( !p ){ return SQLITE_NOMEM; } sqlite3GlobalConfig.pcache.xCachesize(p, pCache->nMax); pCache->pCache = p; } eCreate = createFlag ? 1 : 0; if( eCreate && (!pCache->bPurgeable || !pCache->pDirty) ){ eCreate = 2; } if( pCache->pCache ){ pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, eCreate); } if( !pPage && eCreate==1 ){ PgHdr *pPg; /* 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 ** cleared), but if that is not possible settle for any other ** unreferenced dirty page. */ expensive_assert( pcacheCheckSynced(pCache) ); for(pPg=pCache->pSynced; pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC)); pPg=pPg->pDirtyPrev ); if( !pPg ){ for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev); } if( pPg ){ int rc; rc = pCache->xStress(pCache->pStress, pPg); if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ return rc; } } pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, 2); } if( pPage ){ if( 0==pPage->nRef ){ pCache->nRef++; } pPage->nRef++; pPage->pData = (void*)&pPage[1]; pPage->pExtra = (void*)&((char*)pPage->pData)[pCache->szPage]; pPage->pCache = pCache; pPage->pgno = pgno; if( pgno==1 ){ pCache->pPage1 = pPage; } } *ppPage = pPage; return (pPage==0 && eCreate) ? SQLITE_NOMEM : SQLITE_OK;}/*** Decrement the reference count on a page. If the page is clean and the** reference count drops to 0, then it is made elible for recycling.*/void sqlite3PcacheRelease(PgHdr *p){ assert( p->nRef>0 ); p->nRef--; if( p->nRef==0 ){ PCache *pCache = p->pCache; pCache->nRef--;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -