/* ** Implementation of the sqlite3_pcache.xPagecount method. */ static int pcache1Pagecount(sqlite3_pcache p) { int n; pcache1EnterMutex(); n = (int)((PCache1)p).nPage; pcache1LeaveMutex(); return(n); }
private void SetPageSize(int value) { if (Cache != null) { //sqlite3GlobalConfig_pcache.xDestroy(Cache); Cache = null; Page1 = null; } PageSize = value; }
/* ** Implementation of the sqlite3_pcache.xPagecount method. */ static int pcache1Pagecount(sqlite3_pcache p) { int n; PCache1 pCache = (PCache1)p; pcache1EnterMutex(pCache.pGroup); n = (int)pCache.nPage; pcache1LeaveMutex(pCache.pGroup); return(n); }
/* ** Implementation of the sqlite3_pcache.xTruncate method. ** ** 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 ** equal to or greater than iLimit are implicitly unpinned. */ static void pcache1Truncate(sqlite3_pcache p, Pgno iLimit) { PCache1 pCache = (PCache1)p; pcache1EnterMutex(pCache.pGroup); if (iLimit <= pCache.iMaxKey) { pcache1TruncateUnsafe(pCache, iLimit); pCache.iMaxKey = iLimit - 1; } pcache1LeaveMutex(pCache.pGroup); }
/* ** Implementation of the sqlite3_pcache.xCachesize method. ** ** Configure the cache_size limit for a cache. */ private static void pcache1Cachesize(sqlite3_pcache p, int nMax) { var pCache = p; if (pCache.bPurgeable) { pcache1EnterMutex(); pcache1.nMaxPage += (int)(nMax - pCache.nMax); pCache.nMax = (uint)nMax; pcache1EnforceMaxPage(); pcache1LeaveMutex(); } }
/* ** Implementation of the sqlite3_pcache.xDestroy method. ** ** Destroy a cache allocated using pcache1Create(). */ static void pcache1Destroy(ref sqlite3_pcache p) { PCache1 pCache = p; pcache1EnterMutex(); pcache1TruncateUnsafe(pCache, 0); pcache1.nMaxPage -= (int)pCache.nMax; pcache1.nMinPage -= (int)pCache.nMin; pcache1EnforceMaxPage(); pcache1LeaveMutex(); //sqlite3_free( ref pCache.apHash ); //sqlite3_free( ref pCache ); }
/* ** Implementation of the sqlite3_pcache.xTruncate method. ** ** 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 ** equal to or greater than iLimit are implicitly unpinned. */ private static void pcache1Truncate(sqlite3_pcache p, uint iLimit) { var pCache = p; pcache1EnterMutex(); if (iLimit <= pCache.iMaxKey) { pcache1TruncateUnsafe(pCache, iLimit); pCache.iMaxKey = iLimit - 1; } pcache1LeaveMutex(); }
/* ** Implementation of the sqlite3_pcache.xCachesize method. ** ** Configure the cache_size limit for a cache. */ static void pcache1Cachesize(sqlite3_pcache p, int nMax) { PCache1 pCache = (PCache1)p; if (pCache.bPurgeable) { pcache1EnterMutex(); pcache1.nMaxPage += (int)(nMax - pCache.nMax); pCache.nMax = (u32)nMax; pcache1EnforceMaxPage(); pcache1LeaveMutex(); } }
/* ** Implementation of the sqlite3_pcache.xRekey method. */ static void pcache1Rekey( sqlite3_pcache p, PgHdr pPg, u32 iOld, u32 iNew ) { PCache1 pCache = p; PgHdr1 pPage = PAGE_TO_PGHDR1(pCache, pPg); PgHdr1 pp; u32 h; Debug.Assert(pPage.iKey == iOld); Debug.Assert(pPage.pCache == pCache); pcache1EnterMutex(); h = iOld % pCache.nHash; pp = pCache.apHash[h]; while (pp != pPage) { pp = pp.pNext; } if (pp == pCache.apHash[h]) { pCache.apHash[h] = pp.pNext; } else { pp.pNext = pPage.pNext; } h = iNew % pCache.nHash; pPage.iKey = iNew; pPage.pNext = pCache.apHash[h]; pCache.apHash[h] = pPage; /* The xRekey() interface is only used to move pages earlier in the ** database file (in order to move all free pages to the end of the ** file where they can be truncated off.) Hence, it is not possible ** for the new page number to be greater than the largest previously ** fetched page. But we retain the following test in case xRekey() ** begins to be used in different ways in the future. */ if (NEVER(iNew > pCache.iMaxKey)) { pCache.iMaxKey = iNew; } pcache1LeaveMutex(); }
/* ** Implementation of the sqlite3_pcache.xCachesize method. ** ** Configure the cache_size limit for a cache. */ static void pcache1Cachesize(sqlite3_pcache p, int nMax) { PCache1 pCache = (PCache1)p; if (pCache.bPurgeable) { PGroup pGroup = pCache.pGroup; pcache1EnterMutex(pGroup); pGroup.nMaxPage += nMax - pCache.nMax; pGroup.mxPinned = pGroup.nMaxPage + 10 - pGroup.nMinPage; pCache.nMax = nMax; pCache.n90pct = pCache.nMax * 9 / 10; pcache1EnforceMaxPage(pGroup); pcache1LeaveMutex(pGroup); } }
/* ** Implementation of the sqlite3_pcache.xDestroy method. ** ** Destroy a cache allocated using pcache1Create(). */ static void pcache1Destroy(ref sqlite3_pcache p) { PCache1 pCache = (PCache1)p; PGroup pGroup = pCache.pGroup; Debug.Assert(pCache.bPurgeable || (pCache.nMax == 0 && pCache.nMin == 0)); pcache1EnterMutex(pGroup); pcache1TruncateUnsafe(pCache, 0); pGroup.nMaxPage -= pCache.nMax; pGroup.nMinPage -= pCache.nMin; pGroup.mxPinned = pGroup.nMaxPage + 10 - pGroup.nMinPage; pcache1EnforceMaxPage(pGroup); pcache1LeaveMutex(pGroup); //sqlite3_free( pCache.apHash ); //sqlite3_free( pCache ); p = null; }
/* ** Implementation of the sqlite3_pcache.xRekey method. */ static void pcache1Rekey( sqlite3_pcache p, PgHdr pPg, u32 iOld, u32 iNew ) { PCache1 pCache = p; PgHdr1 pPage = PAGE_TO_PGHDR1(pCache, pPg); PgHdr1 pp; u32 h; Debug.Assert(pPage.iKey == iOld); Debug.Assert(pPage.pCache == pCache); pcache1EnterMutex(); h = iOld % pCache.nHash; pp = pCache.apHash[h]; while (pp != pPage) { pp = pp.pNext; } if (pp == pCache.apHash[h]) { pCache.apHash[h] = pp.pNext; } else { pp.pNext = pPage.pNext; } h = iNew % pCache.nHash; pPage.iKey = iNew; pPage.pNext = pCache.apHash[h]; pCache.apHash[h] = pPage; if (iNew > pCache.iMaxKey) { pCache.iMaxKey = iNew; } pcache1LeaveMutex(); }
/* ** Implementation of the sqlite3_pcache.xRekey method. */ static void pcache1Rekey( sqlite3_pcache p, PgHdr pPg, Pgno iOld, Pgno iNew ) { PCache1 pCache = (PCache1)p; PgHdr1 pPage = PAGE_TO_PGHDR1(pCache, pPg); PgHdr1 pp; int h; Debug.Assert(pPage.iKey == iOld); Debug.Assert(pPage.pCache == pCache); pcache1EnterMutex(pCache.pGroup); h = (int)(iOld % pCache.nHash); pp = pCache.apHash[h]; while ((pp) != pPage) { pp = (pp).pNext; } if (pp == pCache.apHash[h]) { pCache.apHash[h] = pp.pNext; } else { pp.pNext = pPage.pNext; } h = (int)(iNew % pCache.nHash); pPage.iKey = iNew; pPage.pNext = pCache.apHash[h]; pCache.apHash[h] = pPage; if (iNew > pCache.iMaxKey) { pCache.iMaxKey = iNew; } pcache1LeaveMutex(pCache.pGroup); }
/* ** Implementation of the sqlite3_pcache.xUnpin method. ** ** Mark a page as unpinned (eligible for asynchronous recycling). */ private static void pcache1Unpin(sqlite3_pcache p, PgHdr pPg, int reuseUnlikely) { var pCache = p; var pPage = PAGE_TO_PGHDR1(pCache, pPg); Debug.Assert(pPage.pCache == pCache); pcache1EnterMutex(); /* It is an error to call this function if the page is already ** part of the global LRU list. */ Debug.Assert(pPage.pLruPrev == null && pPage.pLruNext == null); Debug.Assert(pcache1.pLruHead != pPage && pcache1.pLruTail != pPage); if (reuseUnlikely != 0 || pcache1.nCurrentPage > pcache1.nMaxPage) { pcache1RemoveFromHash(pPage); pcache1FreePage(ref 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 != null) { pcache1.pLruHead.pLruPrev = pPage; pPage.pLruNext = pcache1.pLruHead; pcache1.pLruHead = pPage; } else { pcache1.pLruTail = pPage; pcache1.pLruHead = pPage; } pCache.nRecyclable++; } pcache1LeaveMutex(); }
/* ** Implementation of the sqlite3_pcache.xUnpin method. ** ** Mark a page as unpinned (eligible for asynchronous recycling). */ static void pcache1Unpin(sqlite3_pcache p, PgHdr pPg, bool reuseUnlikely) { PCache1 pCache = (PCache1)p; PgHdr1 pPage = PAGE_TO_PGHDR1(pCache, pPg); PGroup pGroup = pCache.pGroup; Debug.Assert(pPage.pCache == pCache); pcache1EnterMutex(pGroup); /* It is an error to call this function if the page is already ** part of the PGroup LRU list. */ Debug.Assert(pPage.pLruPrev == null && pPage.pLruNext == null); Debug.Assert(pGroup.pLruHead != pPage && pGroup.pLruTail != pPage); if (reuseUnlikely || pGroup.nCurrentPage > pGroup.nMaxPage) { pcache1RemoveFromHash(pPage); pcache1FreePage(ref pPage); } else { /* Add the page to the PGroup LRU list. */ if (pGroup.pLruHead != null) { pGroup.pLruHead.pLruPrev = pPage; pPage.pLruNext = pGroup.pLruHead; pGroup.pLruHead = pPage; } else { pGroup.pLruTail = pPage; pGroup.pLruHead = pPage; } pCache.nRecyclable++; } pcache1LeaveMutex(pCache.pGroup); }
/* ** Implementation of the sqlite3_pcache.xTruncate method. ** ** 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 ** equal to or greater than iLimit are implicitly unpinned. */ static void pcache1Truncate( sqlite3_pcache p, u32 iLimit ) { PCache1 pCache = (PCache1)p; pcache1EnterMutex(); if ( iLimit <= pCache.iMaxKey ) { pcache1TruncateUnsafe( pCache, iLimit ); pCache.iMaxKey = iLimit - 1; } pcache1LeaveMutex(); }
/* ** Implementation of the sqlite3_pcache.xRekey method. */ static void pcache1Rekey( sqlite3_pcache p, PgHdr pPg, u32 iOld, u32 iNew ) { PCache1 pCache = p; PgHdr1 pPage = PAGE_TO_PGHDR1( pCache, pPg ); PgHdr1 pp; u32 h; Debug.Assert( pPage.iKey == iOld ); Debug.Assert( pPage.pCache == pCache ); pcache1EnterMutex(); h = iOld % pCache.nHash; pp = pCache.apHash[h]; while ( pp != pPage ) { pp = pp.pNext; } if ( pp == pCache.apHash[h] ) pCache.apHash[h] = pp.pNext; else pp.pNext = pPage.pNext; h = iNew % pCache.nHash; pPage.iKey = iNew; pPage.pNext = pCache.apHash[h]; pCache.apHash[h] = pPage; /* The xRekey() interface is only used to move pages earlier in the ** database file (in order to move all free pages to the end of the ** file where they can be truncated off.) Hence, it is not possible ** for the new page number to be greater than the largest previously ** fetched page. But we retain the following test in case xRekey() ** begins to be used in different ways in the future. */ if ( NEVER( iNew > pCache.iMaxKey ) ) { pCache.iMaxKey = iNew; } pcache1LeaveMutex(); }
/* ** 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. 0 means do not allocate a new ** page. 1 means allocate a new page if space is easily available. 2 ** 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 ** database) there is really no difference between createFlag 1 and 2. So ** the calling function (pcache.c) will never have a createFlag of 1 on ** a non-purgable cache. ** ** 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, and the page is not already in the cache, then ** return NULL (do not allocate a new page) if any of the following ** conditions are true: ** ** (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, or ** ** 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, ** ** (c) The system is under memory pressure and wants to avoid ** unnecessary pages cache entry allocations ** ** 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 PgHdr pcache1Fetch(sqlite3_pcache p, Pgno iKey, int createFlag) { int nPinned; PCache1 pCache = (PCache1)p; PGroup pGroup; PgHdr1 pPage = null; Debug.Assert(pCache.bPurgeable || createFlag != 1); Debug.Assert(pCache.bPurgeable || pCache.nMin == 0); Debug.Assert(pCache.bPurgeable == false || pCache.nMin == 10); Debug.Assert(pCache.nMin == 0 || pCache.bPurgeable); pcache1EnterMutex(pGroup = pCache.pGroup); /* Step 1: Search the hash table for an existing entry. */ if (pCache.nHash > 0) { int h = (int)(iKey % pCache.nHash); for (pPage = pCache.apHash[h]; pPage != null && pPage.iKey != iKey; pPage = pPage.pNext) { ; } } /* Step 2: Abort if no existing page is found and createFlag is 0 */ if (pPage != null || createFlag == 0) { pcache1PinPage(pPage); goto fetch_out; } /* The pGroup local variable will normally be initialized by the ** pcache1EnterMutex() macro above. But if SQLITE_MUTEX_OMIT is defined, ** then pcache1EnterMutex() is a no-op, so we have to initialize the ** local variable here. Delaying the initialization of pGroup is an ** optimization: The common case is to exit the module before reaching ** this point. */ #if SQLITE_MUTEX_OMIT pGroup = pCache.pGroup; #endif /* Step 3: Abort if createFlag is 1 but the cache is nearly full */ nPinned = pCache.nPage - pCache.nRecyclable; Debug.Assert(nPinned >= 0); Debug.Assert(pGroup.mxPinned == pGroup.nMaxPage + 10 - pGroup.nMinPage); Debug.Assert(pCache.n90pct == pCache.nMax * 9 / 10); if (createFlag == 1 && ( nPinned >= pGroup.mxPinned || nPinned >= (int)pCache.n90pct || pcache1UnderMemoryPressure(pCache) )) { goto fetch_out; } if (pCache.nPage >= pCache.nHash && pcache1ResizeHash(pCache) != 0) { goto fetch_out; } /* Step 4. Try to recycle a page. */ if (pCache.bPurgeable && pGroup.pLruTail != null && ( (pCache.nPage + 1 >= pCache.nMax) || pGroup.nCurrentPage >= pGroup.nMaxPage || pcache1UnderMemoryPressure(pCache) )) { PCache1 pOtherCache; pPage = pGroup.pLruTail; pcache1RemoveFromHash(pPage); pcache1PinPage(pPage); if ((pOtherCache = pPage.pCache).szPage != pCache.szPage) { pcache1FreePage(ref pPage); pPage = null; } else { pGroup.nCurrentPage -= (pOtherCache.bPurgeable ? 1 : 0) - (pCache.bPurgeable ? 1 : 0); } } /* Step 5. If a usable page buffer has still not been found, ** attempt to allocate a new one. */ if (null == pPage) { if (createFlag == 1) { sqlite3BeginBenignMalloc(); } pcache1LeaveMutex(pGroup); pPage = pcache1AllocPage(pCache); pcache1EnterMutex(pGroup); if (createFlag == 1) { sqlite3EndBenignMalloc(); } } if (pPage != null) { int h = (int)(iKey % pCache.nHash); pCache.nPage++; pPage.iKey = iKey; pPage.pNext = pCache.apHash[h]; pPage.pCache = pCache; pPage.pLruPrev = null; pPage.pLruNext = null; PGHDR1_TO_PAGE(pPage).Clear();// *(void **)(PGHDR1_TO_PAGE(pPage)) = 0; pPage.pPgHdr.pPgHdr1 = pPage; pCache.apHash[h] = pPage; } fetch_out: if (pPage != null && iKey > pCache.iMaxKey) { pCache.iMaxKey = iKey; } pcache1LeaveMutex(pGroup); return(pPage != null ? PGHDR1_TO_PAGE(pPage) : null); }
/* ** Implementation of the sqlite3_pcache.xPagecount method. */ static int pcache1Pagecount( sqlite3_pcache p ) { int n; pcache1EnterMutex(); n = (int)( (PCache1)p ).nPage; pcache1LeaveMutex(); return n; }
/* ** Implementation of the sqlite3_pcache.xCachesize method. ** ** Configure the cache_size limit for a cache. */ static void pcache1Cachesize( sqlite3_pcache p, int nMax ) { PCache1 pCache = (PCache1)p; if ( pCache.bPurgeable ) { pcache1EnterMutex(); pcache1.nMaxPage += (int)( nMax - pCache.nMax ); pCache.nMax = (u32)nMax; pcache1EnforceMaxPage(); pcache1LeaveMutex(); } }
/* ** 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. 0 means do not allocate a new ** page. 1 means allocate a new page if space is easily available. 2 ** 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 ** database) there is really no difference between createFlag 1 and 2. So ** the calling function (pcache.c) will never have a createFlag of 1 on ** a non-purgable cache. ** ** 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, 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 PgHdr pcache1Fetch( sqlite3_pcache p, u32 iKey, int createFlag ) { u32 nPinned; PCache1 pCache = p; PgHdr1 pPage = null; Debug.Assert( pCache.bPurgeable || createFlag != 1 ); pcache1EnterMutex(); if ( createFlag == 1 ) sqlite3BeginBenignMalloc(); /* Search the hash table for an existing entry. */ if ( pCache.nHash > 0 ) { u32 h = iKey % pCache.nHash; for ( pPage = pCache.apHash[h]; pPage != null && pPage.iKey != iKey; pPage = pPage.pNext ) ; } if ( pPage != null || createFlag == 0 ) { pcache1PinPage( pPage ); goto fetch_out; } /* Step 3 of header comment. */ nPinned = pCache.nPage - pCache.nRecyclable; if ( createFlag == 1 && ( nPinned >= ( pcache1.nMaxPage + pCache.nMin - pcache1.nMinPage ) || nPinned >= ( pCache.nMax * 9 / 10 ) ) ) { goto fetch_out; } if ( pCache.nPage >= pCache.nHash && pcache1ResizeHash( pCache ) != 0 ) { goto fetch_out; } /* Step 4. Try to recycle a page buffer if appropriate. */ if ( pCache.bPurgeable && pcache1.pLruTail != null && ( pCache.nPage + 1 >= pCache.nMax || pcache1.nCurrentPage >= pcache1.nMaxPage ) ) { pPage = pcache1.pLruTail; pcache1RemoveFromHash( pPage ); pcache1PinPage( pPage ); if ( pPage.pCache.szPage != pCache.szPage ) { pcache1FreePage( ref pPage ); pPage = null; } else { pcache1.nCurrentPage -= ( ( pPage.pCache.bPurgeable ? 1 : 0 ) - ( pCache.bPurgeable ? 1 : 0 ) ); } } /* Step 5. If a usable page buffer has still not been found, ** attempt to allocate a new one. */ if ( null == pPage ) { pPage = pcache1AllocPage( pCache ); } if ( pPage != null ) { u32 h = iKey % pCache.nHash; pCache.nPage++; pPage.iKey = iKey; pPage.pNext = pCache.apHash[h]; pPage.pCache = pCache; pPage.pLruPrev = null; pPage.pLruNext = null; PGHDR1_TO_PAGE( pPage ).Clear();// *(void **)(PGHDR1_TO_PAGE(pPage)) = 0; pPage.pPgHdr.pPgHdr1 = pPage; pCache.apHash[h] = pPage; #if FALSE Debug.Assert( pcache1CountHash( pCache ) == pCache.nPage ); #endif } fetch_out: if ( pPage != null && iKey > pCache.iMaxKey ) { pCache.iMaxKey = iKey; } if ( createFlag == 1 ) sqlite3EndBenignMalloc(); pcache1LeaveMutex(); return ( pPage != null ? PGHDR1_TO_PAGE( pPage ) : null ); }
/* ** Implementation of the sqlite3_pcache.xUnpin method. ** ** Mark a page as unpinned (eligible for asynchronous recycling). */ static void pcache1Unpin( sqlite3_pcache p, PgHdr pPg, int reuseUnlikely ) { PCache1 pCache = (PCache1)p; PgHdr1 pPage = PAGE_TO_PGHDR1( pCache, pPg ); Debug.Assert( pPage.pCache == pCache ); pcache1EnterMutex(); /* It is an error to call this function if the page is already ** part of the global LRU list. */ Debug.Assert( pPage.pLruPrev == null && pPage.pLruNext == null ); Debug.Assert( pcache1.pLruHead != pPage && pcache1.pLruTail != pPage ); if ( reuseUnlikely != 0 || pcache1.nCurrentPage > pcache1.nMaxPage ) { pcache1RemoveFromHash( pPage ); pcache1FreePage( ref 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 != null ) { pcache1.pLruHead.pLruPrev = pPage; pPage.pLruNext = pcache1.pLruHead; pcache1.pLruHead = pPage; } else { pcache1.pLruTail = pPage; pcache1.pLruHead = pPage; } pCache.nRecyclable++; } pcache1LeaveMutex(); }
/* ** Implementation of the sqlite3_pcache.xRekey method. */ static void pcache1Rekey( sqlite3_pcache p, PgHdr pPg, u32 iOld, u32 iNew ) { PCache1 pCache = p; PgHdr1 pPage = PAGE_TO_PGHDR1( pCache, pPg ); PgHdr1 pp; u32 h; Debug.Assert( pPage.iKey == iOld ); Debug.Assert( pPage.pCache == pCache ); pcache1EnterMutex(); h = iOld % pCache.nHash; pp = pCache.apHash[h]; while ( pp != pPage ) { pp = pp.pNext; } if ( pp == pCache.apHash[h] ) pCache.apHash[h] = pp.pNext; else pp.pNext = pPage.pNext; h = iNew % pCache.nHash; pPage.iKey = iNew; pPage.pNext = pCache.apHash[h]; pCache.apHash[h] = pPage; if ( iNew > pCache.iMaxKey ) { pCache.iMaxKey = iNew; } pcache1LeaveMutex(); }
/* ** 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. 0 means do not allocate a new ** page. 1 means allocate a new page if space is easily available. 2 ** 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 ** database) there is really no difference between createFlag 1 and 2. So ** the calling function (pcache.c) will never have a createFlag of 1 on ** a non-purgable cache. ** ** 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, 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 PgHdr pcache1Fetch(sqlite3_pcache p, u32 iKey, int createFlag) { u32 nPinned; PCache1 pCache = p; PgHdr1 pPage = null; Debug.Assert(pCache.bPurgeable || createFlag != 1); pcache1EnterMutex(); if (createFlag == 1) { sqlite3BeginBenignMalloc(); } /* Search the hash table for an existing entry. */ if (pCache.nHash > 0) { u32 h = iKey % pCache.nHash; for (pPage = pCache.apHash[h]; pPage != null && pPage.iKey != iKey; pPage = pPage.pNext) { ; } } if (pPage != null || createFlag == 0) { pcache1PinPage(pPage); goto fetch_out; } /* Step 3 of header comment. */ nPinned = pCache.nPage - pCache.nRecyclable; if (createFlag == 1 && ( nPinned >= (pcache1.nMaxPage + pCache.nMin - pcache1.nMinPage) || nPinned >= (pCache.nMax * 9 / 10) )) { goto fetch_out; } if (pCache.nPage >= pCache.nHash && pcache1ResizeHash(pCache) != 0) { goto fetch_out; } /* Step 4. Try to recycle a page buffer if appropriate. */ if (pCache.bPurgeable && pcache1.pLruTail != null && ( pCache.nPage + 1 >= pCache.nMax || pcache1.nCurrentPage >= pcache1.nMaxPage )) { pPage = pcache1.pLruTail; pcache1RemoveFromHash(pPage); pcache1PinPage(pPage); if (pPage.pCache.szPage != pCache.szPage) { pcache1FreePage(ref pPage); pPage = null; } else { pcache1.nCurrentPage -= ((pPage.pCache.bPurgeable ? 1 : 0) - (pCache.bPurgeable ? 1 : 0)); } } /* Step 5. If a usable page buffer has still not been found, ** attempt to allocate a new one. */ if (null == pPage) { pPage = pcache1AllocPage(pCache); } if (pPage != null) { u32 h = iKey % pCache.nHash; pCache.nPage++; pPage.iKey = iKey; pPage.pNext = pCache.apHash[h]; pPage.pCache = pCache; pPage.pLruPrev = null; pPage.pLruNext = null; PGHDR1_TO_PAGE(pPage).Clear();// *(void **)(PGHDR1_TO_PAGE(pPage)) = 0; pPage.pPgHdr.pPgHdr1 = pPage; pCache.apHash[h] = pPage; #if FALSE Debug.Assert(pcache1CountHash(pCache) == pCache.nPage); #endif } fetch_out: if (pPage != null && iKey > pCache.iMaxKey) { pCache.iMaxKey = iKey; } if (createFlag == 1) { sqlite3EndBenignMalloc(); } pcache1LeaveMutex(); return(pPage != null ? PGHDR1_TO_PAGE(pPage) : null); }
/* ** Implementation of the sqlite3_pcache.xDestroy method. ** ** Destroy a cache allocated using pcache1Create(). */ static void pcache1Destroy( ref sqlite3_pcache p ) { PCache1 pCache = p; pcache1EnterMutex(); pcache1TruncateUnsafe( pCache, 0 ); pcache1.nMaxPage -= (int)pCache.nMax; pcache1.nMinPage -= (int)pCache.nMin; pcache1EnforceMaxPage(); pcache1LeaveMutex(); //sqlite3_free( ref pCache.apHash ); //sqlite3_free( ref pCache ); p = null; }