/* ** Implementation of the sqlite3_pcache.xUnpin method. ** ** Mark a page as unpinned (eligible for asynchronous recycling). */ static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){ PCache1 *pCache = (PCache1 *)p; PgHdr1 *pPage = PAGE_TO_PGHDR1(pPg); pcache1EnterMutex(); /* It is an error to call this function if the page is already ** part of the global LRU list. */ assert( pPage->pLruPrev==0 && pPage->pLruNext==0 ); assert( pcache1.pLruHead!=pPage && pcache1.pLruTail!=pPage ); if( reuseUnlikely || pcache1.nCurrentPage>pcache1.nMaxPage ){ pcache1RemoveFromHash(pPage); pcache1FreePage(pPage); }else{ /* Add the page to the global LRU list. Normally, the page is added to ** the head of the list (last page to be recycled). However, if the ** reuseUnlikely flag passed to this function is true, the page is added ** to the tail of the list (first page to be recycled). */ if( pcache1.pLruHead ){ pcache1.pLruHead->pLruPrev = pPage; pPage->pLruNext = pcache1.pLruHead; pcache1.pLruHead = pPage; }else{ pcache1.pLruTail = pPage; pcache1.pLruHead = pPage; } pCache->nRecyclable++; } pcache1LeaveMutex(); }
/* ** If there are currently more than pcache.nMaxPage pages allocated, try ** to recycle pages to reduce the number allocated to pcache.nMaxPage. */ static void pcache1EnforceMaxPage(void){ assert( sqlite3_mutex_held(pcache1.mutex) ); while( pcache1.nCurrentPage>pcache1.nMaxPage && pcache1.pLruTail ){ PgHdr1 *p = pcache1.pLruTail; pcache1PinPage(p); pcache1RemoveFromHash(p); pcache1FreePage(p); } }
/* ** If there are currently more than nMaxPage pages allocated, try ** to recycle pages to reduce the number allocated to nMaxPage. */ static void pcache1EnforceMaxPage(PGroup *pGroup){ assert( sqlite3_mutex_held(pGroup->mutex) ); while( pGroup->nCurrentPage>pGroup->nMaxPage && pGroup->pLruTail ){ PgHdr1 *p = pGroup->pLruTail; assert( p->pCache->pGroup==pGroup ); pcache1PinPage(p); pcache1RemoveFromHash(p); pcache1FreePage(p); } }
/* ** Remove the page supplied as an argument from the hash table ** (PCache1.apHash structure) that it is currently stored in. ** Also free the page if freePage is true. ** ** The PGroup mutex must be held when this function is called. */ 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); }
/* ** 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( pcache1.pStart==0 ){ PgHdr1 *p; pcache1EnterMutex(); while( (nReq<0 || nFree<nReq) && (p=pcache1.pLruTail) ){ nFree += sqlite3MallocSize(p); pcache1PinPage(p); pcache1RemoveFromHash(p); pcache1FreePage(p); } pcache1LeaveMutex(); } return nFree; }
/* ** Discard all pages from cache pCache with a page number (key value) ** greater than or equal to iLimit. Any pinned pages that meet this ** criteria are unpinned before they are discarded. ** ** The global mutex must be held when this function is called. */ static void pcache1TruncateUnsafe( PCache1 *pCache, unsigned int iLimit ){ unsigned int h; assert( sqlite3_mutex_held(pcache1.mutex) ); for(h=0; h<pCache->nHash; h++){ PgHdr1 **pp = &pCache->apHash[h]; PgHdr1 *pPage; while( (pPage = *pp)!=0 ){ if( pPage->iKey>=iLimit ){ pcache1PinPage(pPage); *pp = pPage->pNext; pcache1FreePage(pPage); }else{ pp = &pPage->pNext; } } } }
/* ** Implementation of the sqlite3_pcache.xFetch method. ** ** Fetch a page by key value. ** ** Whether or not a new page may be allocated by this function depends on ** the value of the createFlag argument. ** ** 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). ** ** 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. ** ** 2. If createFlag==0 and the page is not already in the cache, NULL is ** returned. ** ** 3. If createFlag is 1, the cache is marked as purgeable and the page is ** not already in the cache, and if either of the following are true, ** return NULL: ** ** (a) the number of pages pinned by the cache is greater than ** PCache1.nMax, or ** (b) the number of pages pinned by the cache is greater than ** the sum of nMax for all purgeable caches, less the sum of ** nMin for all other purgeable caches. ** ** 4. If none of the first three conditions apply and the cache is marked ** as purgeable, and if one of the following is true: ** ** (a) The number of pages allocated for the cache is already ** PCache1.nMax, or ** ** (b) The number of pages allocated for all purgeable caches is ** already equal to or greater than the sum of nMax for all ** purgeable caches, ** ** 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 ** proceed to step 5. ** ** 5. Otherwise, allocate and return a new page buffer. */ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){ unsigned int nPinned; PCache1 *pCache = (PCache1 *)p; PgHdr1 *pPage = 0; pcache1EnterMutex(); if( createFlag==1 ) sqlite3BeginBenignMalloc(); /* Search the hash table for an existing entry. */ if( pCache->nHash>0 ){ unsigned int h = iKey % pCache->nHash; for(pPage=pCache->apHash[h]; pPage&&pPage->iKey!=iKey; pPage=pPage->pNext); } if( pPage || createFlag==0 ){ pcache1PinPage(pPage); goto fetch_out; } /* Step 3 of header comment. */ nPinned = pCache->nPage - pCache->nRecyclable; if( createFlag==1 && pCache->bPurgeable && ( nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage) || nPinned>=(pCache->nMax) )){ goto fetch_out; } if( pCache->nPage>=pCache->nHash && pcache1ResizeHash(pCache) ){ goto fetch_out; } /* Step 4. Try to recycle a page buffer if appropriate. */ if( pCache->bPurgeable && pcache1.pLruTail && ( pCache->nPage>=pCache->nMax-1 || pcache1.nCurrentPage>=pcache1.nMaxPage )){ pPage = pcache1.pLruTail; pcache1RemoveFromHash(pPage); pcache1PinPage(pPage); if( pPage->pCache->szPage!=pCache->szPage ){ pcache1FreePage(pPage); pPage = 0; }else{ pcache1.nCurrentPage -= (pPage->pCache->bPurgeable - pCache->bPurgeable); } } /* Step 5. If a usable page buffer has still not been found, ** attempt to allocate a new one. */ if( !pPage ){ pPage = pcache1AllocPage(pCache); } if( pPage ){ unsigned int h = iKey % pCache->nHash; *(void **)(PGHDR1_TO_PAGE(pPage)) = 0; pCache->nPage++; pPage->iKey = iKey; pPage->pNext = pCache->apHash[h]; pPage->pCache = pCache; pPage->pLruPrev = 0; pPage->pLruNext = 0; pCache->apHash[h] = pPage; } fetch_out: if( pPage && iKey>pCache->iMaxKey ){ pCache->iMaxKey = iKey; } if( createFlag==1 ) sqlite3EndBenignMalloc(); pcache1LeaveMutex(); return (pPage ? PGHDR1_TO_PAGE(pPage) : 0); }