/* ** If there are currently more than pcache.nMaxPage pages allocated, try ** to recycle pages to reduce the number allocated to pcache.nMaxPage. */ static void pcacheEnforceMaxPage() { PgHdr *p; assert( sqlite3_mutex_held(pcache.mutex) ); while( pcache.nCurrentPage>pcache.nMaxPage && (p = pcacheRecyclePage()) ) { pcachePageFree(p); } }
/* ** Drop every cache entry whose page number is greater than "pgno". */ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){ PgHdr *p, *pNext; PgHdr *pDirty = pCache->pDirty; pcacheEnterMutex(); for(p=pCache->pClean; p||pDirty; p=pNext){ if( !p ){ p = pDirty; pDirty = 0; } pNext = p->pNext; if( p->pgno>pgno ){ if( p->nRef==0 ){ pcacheRemoveFromHash(p); if( p->flags&PGHDR_DIRTY ){ pcacheRemoveFromList(&pCache->pDirty, p); pCache->nPinned--; }else{ pcacheRemoveFromList(&pCache->pClean, p); pcacheRemoveFromLruList(p); } pcachePageFree(p); }else{ /* If there are references to the page, it cannot be freed. In this ** case, zero the page content instead. */ memset(p->pData, 0, pCache->szPage); } } } pcacheExitMutex(); }
/* ** Dereference a page. When the reference count reaches zero, ** move the page to the LRU list if it is clean. */ void sqlite3PcacheRelease(PgHdr *p) { assert( p->nRef>0 ); p->nRef--; if( p->nRef==0 ) { PCache *pCache = p->pCache; if( p->pCache->xDestroy ) { p->pCache->xDestroy(p); } pCache->nRef--; if( (p->flags&PGHDR_DIRTY)==0 ) { pCache->nPinned--; pcacheEnterMutex(); if( pcache.nCurrentPage>pcache.nMaxPage ) { pcacheRemoveFromList(&pCache->pClean, p); pcacheRemoveFromHash(p); pcachePageFree(p); } else { pcacheAddToLruList(p); } pcacheExitMutex(); } else { /* Move the page to the head of the caches dirty list. */ pcacheRemoveFromList(&pCache->pDirty, p); pcacheAddToList(&pCache->pDirty, p); } } }
/* ** Obtain space for a page. Try to recycle an old page if the limit on the ** number of pages has been reached. If the limit has not been reached or ** there are no pages eligible for recycling, allocate a new page. ** ** Return a pointer to the new page, or NULL if an OOM condition occurs. */ static int pcacheRecycleOrAlloc(PCache *pCache, PgHdr **ppPage) { PgHdr *p = 0; int szPage = pCache->szPage; int szExtra = pCache->szExtra; assert( pcache.isInit ); assert( sqlite3_mutex_held(pcache.mutex) ); *ppPage = 0; /* If we have reached the limit for pinned/dirty pages, and there is at ** least one dirty page, invoke the xStress callback to cause a page to ** become clean. */ expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) ); expensive_assert( pcacheCheckSynced(pCache) ); if( pCache->xStress && pCache->pDirty && pCache->nPinned>=(pcache.nMaxPage+pCache->nMin-pcache.nMinPage) ) { PgHdr *pPg; assert(pCache->pDirtyTail); for(pPg=pCache->pSynced; pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC)); pPg=pPg->pPrev ); if( !pPg ) { for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pPrev); } if( pPg ) { int rc; pcacheExitMutex(); rc = pCache->xStress(pCache->pStress, pPg); pcacheEnterMutex(); if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ) { return rc; } } } /* If the global page limit has been reached, try to recycle a page. */ if( pCache->bPurgeable && pcache.nCurrentPage>=pcache.nMaxPage ) { p = pcacheRecyclePage(); } /* If a page has been recycled but it is the wrong size, free it. */ if( p && (p->pCache->szPage!=szPage || p->pCache->szPage!=szExtra) ) { pcachePageFree(p); p = 0; } if( !p ) { p = pcachePageAlloc(pCache); } *ppPage = p; return (p?SQLITE_OK:SQLITE_NOMEM); }
/* ** Remove all content from a page cache */ static void pcacheClear(PCache *pCache){ PgHdr *p, *pNext; assert( sqlite3_mutex_held(pcache_g.mutex) ); for(p=pCache->pClean; p; p=pNext){ pNext = p->pNext; pcacheRemoveFromLruList(p); pcachePageFree(p); } for(p=pCache->pDirty; p; p=pNext){ pNext = p->pNext; pcachePageFree(p); } pCache->pClean = 0; pCache->pDirty = 0; pCache->pDirtyTail = 0; pCache->nPage = 0; pCache->nPinned = 0; memset(pCache->apHash, 0, pCache->nHash*sizeof(pCache->apHash[0])); }
/* ** Drop a page from the cache. There must be exactly one reference to the ** page. This function deletes that reference, so after it returns the ** page pointed to by p is invalid. */ void sqlite3PcacheDrop(PgHdr *p){ PCache *pCache; assert( p->nRef==1 ); assert( 0==(p->flags&PGHDR_DIRTY) ); pCache = p->pCache; pCache->nRef--; pCache->nPinned--; pcacheEnterMutex(); pcacheRemoveFromList(&pCache->pClean, p); pcacheRemoveFromHash(p); pcachePageFree(p); pcacheExitMutex(); }
/* ** This function is called to free superfluous dynamically allocated memory ** held by the pager system. Memory in use by any SQLite pager allocated ** by the current thread may be sqlite3_free()ed. ** ** 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 ** of bytes of memory released. */ int sqlite3PcacheReleaseMemory(int nReq){ int nFree = 0; if( pcache_g.pStart==0 ){ PgHdr *p; pcacheEnterMutex(); while( (nReq<0 || nFree<nReq) && (p=pcacheRecyclePage()) ){ nFree += pcachePageSize(p); pcachePageFree(p); } pcacheExitMutex(); } return nFree; }