public PgHdr pPgHdr = new PgHdr(); /* Pointer to Actual Page Header */ public void Clear() { iKey = 0; pNext = null; pCache = null; pPgHdr.Clear(); }
public PgHdr pPgHdr = new PgHdr(); /* Pointer to Actual Page Header */ public void Clear() { this.iKey = 0; this.pNext = null; this.pCache = null; this.pPgHdr.Clear(); }
/* ** Remove the page supplied as an argument from the hash table ** (PCache1.apHash structure) that it is currently stored in. ** ** The global mutex must be held when this function is called. */ static void pcache1RemoveFromHash(PgHdr1 pPage) { u32 h; PCache1 pCache = pPage.pCache; PgHdr1 pp, pPrev; h = pPage.iKey % pCache.nHash; pPrev = null; for (pp = pCache.apHash[h]; pp != pPage; pPrev = pp, pp = pp.pNext) { ; } if (pPrev == null) { pCache.apHash[h] = pp.pNext; } else { pPrev.pNext = pp.pNext; // pCache.apHash[h] = pp.pNext; } pCache.nPage--; #if FALSE Debug.Assert(pcache1CountHash(pCache) == pCache.nPage); #endif }
/* ** 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.xCreate method. ** ** Allocate a new cache. */ static sqlite3_pcache pcache1Create(int szPage, bool bPurgeable) { PCache1 pCache; /* The newly created page cache */ PGroup pGroup; /* The group the new page cache will belong to */ //int sz; /* Bytes of memory required to allocate the new cache */ /* ** The seperateCache variable is true if each PCache has its own private ** PGroup. In other words, separateCache is true for mode (1) where no ** mutexing is required. ** ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT ** ** * Always use a unified cache in single-threaded applications ** ** * Otherwise (if multi-threaded and ENABLE_MEMORY_MANAGEMENT is off) ** use separate caches (mode-1) */ #if (SQLITE_ENABLE_MEMORY_MANAGEMENT) || !SQLITE_THREADSAF const int separateCache = 0; #else int separateCache = sqlite3GlobalConfig.bCoreMutex > 0; #endif //sz = sizeof( PCache1 ) + sizeof( PGroup ) * separateCache; pCache = new PCache1();//(PCache1)sqlite3_malloc( sz ); //if ( pCache != null ) //{ //memset( pCache, 0, sz ); if (separateCache == 0) { pGroup = pcache1.grp; } ////else ////{ ////pGroup = new PGroup();//(PGroup)pCache[1]; ////pGroup.mxPinned = 10; ////} pCache.pGroup = pGroup; pCache.szPage = szPage; pCache.bPurgeable = bPurgeable;//( bPurgeable ? 1 : 0 ); if (bPurgeable) { pCache.nMin = 10; pcache1EnterMutex(pGroup); pGroup.nMinPage += (int)pCache.nMin; pGroup.mxPinned = pGroup.nMaxPage + 10 - pGroup.nMinPage; pcache1LeaveMutex(pGroup); } //} return((sqlite3_pcache)pCache); }
/* ** Return true if it desirable to avoid allocating a new page cache ** entry. ** ** If memory was allocated specifically to the page cache using ** SQLITE_CONFIG_PAGECACHE but that memory has all been used, then ** it is desirable to avoid allocating a new page cache entry because ** presumably SQLITE_CONFIG_PAGECACHE was suppose to be sufficient ** for all page cache needs and we should not need to spill the ** allocation onto the heap. ** ** Or, the heap is used for all page cache memory put the heap is ** under memory pressure, then again it is desirable to avoid ** allocating a new page cache entry in order to avoid stressing ** the heap even further. */ static bool pcache1UnderMemoryPressure(PCache1 pCache) { if (pcache1.nSlot != 0 && pCache.szPage <= pcache1.szSlot) { return(pcache1.bUnderPressure); } else { return(sqlite3HeapNearlyFull()); } }
/* ** Free a page object allocated by pcache1AllocPage(). ** ** The pointer is allowed to be NULL, which is prudent. But it turns out ** that the current implementation happens to never call this routine ** with a NULL pointer, so we mark the NULL test with ALWAYS(). */ static void pcache1FreePage(ref PgHdr1 p) { if (ALWAYS(p)) { PCache1 pCache = p.pCache; if (pCache.bPurgeable) { pCache.pGroup.nCurrentPage--; } pcache1Free(ref p.pPgHdr);//PGHDR1_TO_PAGE( p ); } }
/* ** 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); }
/* ** 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. */ private static void pcache1TruncateUnsafe( PCache1 pCache, uint iLimit ) { //TESTONLY( unsigned int nPage = 0; ) /* Used to assert pCache->nPage is correct */ #if !NDEBUG || SQLITE_COVERAGE_TEST u32 nPage = 0; #endif uint h; Debug.Assert(sqlite3_mutex_held(pcache1.mutex)); for (h = 0; h < pCache.nHash; h++) { PgHdr1 pPrev = null; var pp = pCache.apHash[h]; PgHdr1 pPage; while ((pPage = pp) != null) { if (pPage.iKey >= iLimit) { pp = pPage.pNext; pcache1PinPage(pPage); if (pCache.apHash[h] == pPage) { pCache.apHash[h] = pPage.pNext; } else { pPrev.pNext = pp; } pcache1FreePage(ref pPage); pCache.nPage--; #if FALSE Debug.Assert(pcache1CountHash(pCache) == pCache.nPage); #endif } else { pp = pPage.pNext; //TESTONLY( nPage++; ) #if !NDEBUG || SQLITE_COVERAGE_TEST nPage++; #endif } pPrev = pPage; } } #if !NDEBUG || SQLITE_COVERAGE_TEST Debug.Assert(pCache.nPage == nPage); #endif }
/* ** 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.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(); }
static u32 pcache1CountHash(PCache1 pCache) { u32 nPage = 0; for (u32 h = 0; h < pCache.nHash; h++) { PgHdr1 pp = pCache.apHash[h]; while (pp != null) { nPage++; pp = pp.pNext; } } return(nPage); }
/******************************************************************************/ /******** General Implementation Functions ************************************/ /* ** This function is used to resize the hash table used by the cache passed ** as the first argument. ** ** The global mutex must be held when this function is called. */ private static int pcache1ResizeHash(PCache1 p) { PgHdr1[] apNew; uint nNew; uint i; Debug.Assert(sqlite3_mutex_held(pcache1.mutex)); nNew = p.nHash * 2; if (nNew < 256) { nNew = 256; } pcache1LeaveMutex(); if (p.nHash != 0) { sqlite3BeginBenignMalloc(); } apNew = new PgHdr1[nNew]; // (PgHdr1**)sqlite3_malloc( sizeof( PgHdr1* ) * nNew ); if (p.nHash != 0) { sqlite3EndBenignMalloc(); } pcache1EnterMutex(); if (apNew != null) { //memset(apNew, 0, sizeof(PgHdr1 *)*nNew); for (i = 0; i < p.nHash; i++) { PgHdr1 pPage; var pNext = p.apHash[i]; while ((pPage = pNext) != null) { var h = pPage.iKey % nNew; pNext = pPage.pNext; pPage.pNext = apNew[h]; apNew[h] = pPage; } } //sqlite3_free( ref p.apHash ); p.apHash = apNew; p.nHash = nNew; } return(p.apHash != null ? SQLITE_OK : SQLITE_NOMEM); }
/* ** 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); } }
/* ** 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, u32 iLimit ) { //TESTONLY( unsigned int nPage = 0; ) /* Used to assert pCache->nPage is correct */ #if !NDEBUG || SQLITE_COVERAGE_TEST u32 nPage = 0; #endif u32 h; Debug.Assert(sqlite3_mutex_held(pcache1.mutex)); for (h = 0; h < pCache.nHash; h++) { PgHdr1 pp = pCache.apHash[h]; PgHdr1 pPage; while ((pPage = pp) != null) { if (pPage.iKey >= iLimit) { pCache.nPage--; pp = pPage.pNext; pcache1PinPage(pPage); if (pCache.apHash[h] == pPage) { pCache.apHash[h] = pPage.pNext; } else { Debugger.Break(); } pcache1FreePage(ref pPage); } else { pp = pPage.pNext; //TESTONLY( nPage++; ) #if !NDEBUG || SQLITE_COVERAGE_TEST nPage++; #endif } } } #if !NDEBUG || SQLITE_COVERAGE_TEST Debug.Assert(pCache.nPage == nPage); #endif }
/* ** 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 PCache mutex must be held when this function is called. */ static void pcache1TruncateUnsafe( PCache1 pCache, /* The cache to truncate */ uint iLimit /* Drop pages with this pgno or larger */ ) { #if !NDEBUG || SQLITE_COVERAGE_TEST //TESTONLY( uint nPage = 0; ) /* To assert pCache.nPage is correct */ uint nPage = 0; #endif uint h; Debug.Assert(sqlite3_mutex_held(pCache.pGroup.mutex)); for (h = 0; h < pCache.nHash; h++) { PgHdr1 pPrev = null; PgHdr1 pp = pCache.apHash[h]; PgHdr1 pPage; while ((pPage = pp) != null) { if (pPage.iKey >= iLimit) { pCache.nPage--; pp = pPage.pNext; pcache1PinPage(pPage); if (pCache.apHash[h] == pPage) { pCache.apHash[h] = pPage.pNext; } else { pPrev.pNext = pp; } pcache1FreePage(ref pPage); } else { pp = pPage.pNext; #if !NDEBUG || SQLITE_COVERAGE_TEST //TESTONLY( nPage++; ) nPage++; #endif } pPrev = pPage; } } #if !NDEBUG || SQLITE_COVERAGE_TEST Debug.Assert(pCache.nPage == nPage); #endif }
/* ** 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). */ 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.xCreate method. ** ** Allocate a new cache. */ static sqlite3_pcache pcache1Create(int szPage, int bPurgeable) { PCache1 pCache; pCache = new PCache1();// (PCache1*)sqlite3_malloc( sizeof( PCache1 ) ); if (pCache != null) { //memset(pCache, 0, sizeof(PCache1)); pCache.szPage = szPage; pCache.bPurgeable = (bPurgeable != 0); if (bPurgeable != 0) { pCache.nMin = 10; pcache1EnterMutex(); pcache1.nMinPage += (int)pCache.nMin; pcache1LeaveMutex(); } } return(pCache); }
/* ** Remove the page supplied as an argument from the hash table ** (PCache1.apHash structure) that it is currently stored in. ** ** The global mutex must be held when this function is called. */ static void pcache1RemoveFromHash(PgHdr1 pPage) { u32 h; PCache1 pCache = pPage.pCache; PgHdr1 pp, pPrev; h = pPage.iKey % pCache.nHash; pPrev = null; for (pp = pCache.apHash[h]; pp != pPage; pPrev = pp, pp = pp.pNext) { ; } if (pPrev == null) { pCache.apHash[h] = pp.pNext; } else { pPrev.pNext = pp.pNext; // pCache.apHash[h] = pp.pNext; } pCache.nPage--; }
/* ** 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); }
/* ** Remove the page supplied as an argument from the hash table ** (PCache1.apHash structure) that it is currently stored in. ** ** The PGroup mutex must be held when this function is called. */ static void pcache1RemoveFromHash(PgHdr1 pPage) { int h; PCache1 pCache = pPage.pCache; PgHdr1 pp; PgHdr1 pPrev = null; Debug.Assert(sqlite3_mutex_held(pCache.pGroup.mutex)); h = (int)(pPage.iKey % pCache.nHash); for (pp = pCache.apHash[h]; pp != pPage; pPrev = pp, pp = pp.pNext) { ; } if (pPrev == null) { pCache.apHash[h] = pp.pNext; } else { pPrev.pNext = pp.pNext; // pCache.apHash[h] = pp.pNext; } pCache.nPage--; }
/* ** Allocate a new page object initially associated with cache pCache. */ static PgHdr1 pcache1AllocPage(PCache1 pCache) { //int nByte = sizeof( PgHdr1 ) + pCache.szPage; PgHdr pPg = pcache1Alloc(pCache.szPage);//nByte ); PgHdr1 p = null; //if ( pPg !=null) { //PAGE_TO_PGHDR1( pCache, pPg ); p = new PgHdr1(); p.pCache = pCache; p.pPgHdr = pPg; if (pCache.bPurgeable) { pCache.pGroup.nCurrentPage++; } } //else //{ // p = 0; //} return(p); }
/* ** 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); }
internal void freeTempSpace() { PCache1.sqlite3PageFree(ref this.pTmpSpace); }
//#define PAGE_TO_PGHDR1(c, p) (PgHdr1*)(((char*)p) + c->szPage) static PgHdr1 PAGE_TO_PGHDR1( PCache1 c, PgHdr p ) { return p.pPgHdr1; }
/* ** Allocate a new page object initially associated with cache pCache. */ static PgHdr1 pcache1AllocPage( PCache1 pCache ) { //int nByte = sizeof(PgHdr1) + pCache.szPage; PgHdr pPg = pcache1Alloc( pCache.szPage ); PgHdr1 p; //if ( pPg != null ) { // PAGE_TO_PGHDR1( pCache, pPg ); p = new PgHdr1(); p.pCache = pCache; p.pPgHdr = pPg; if ( pCache.bPurgeable ) { pcache1.nCurrentPage++; } } //else //{ // p = null; //} return p; }
/******************************************************************************/ /******** General Implementation Functions ************************************/ /* ** This function is used to resize the hash table used by the cache passed ** as the first argument. ** ** The global mutex must be held when this function is called. */ static int pcache1ResizeHash( PCache1 p ) { PgHdr1[] apNew; u32 nNew; u32 i; Debug.Assert( sqlite3_mutex_held( pcache1.mutex ) ); nNew = p.nHash * 2; if ( nNew < 256 ) { nNew = 256; } pcache1LeaveMutex(); if ( p.nHash != 0 ) { sqlite3BeginBenignMalloc(); } apNew = new PgHdr1[nNew];// (PgHdr1**)sqlite3_malloc( sizeof( PgHdr1* ) * nNew ); if ( p.nHash != 0 ) { sqlite3EndBenignMalloc(); } pcache1EnterMutex(); if ( apNew != null ) { //memset(apNew, 0, sizeof(PgHdr1 *)*nNew); for ( i = 0; i < p.nHash; i++ ) { PgHdr1 pPage; PgHdr1 pNext = p.apHash[i]; while ( ( pPage = pNext ) != null ) { u32 h = (u32)( pPage.iKey % nNew ); pNext = pPage.pNext; pPage.pNext = apNew[h]; apNew[h] = pPage; } } //sqlite3_free( ref p.apHash ); p.apHash = apNew; p.nHash = nNew; } return ( p.apHash != null ? SQLITE_OK : SQLITE_NOMEM ); }
//#define PAGE_TO_PGHDR1(c, p) (PgHdr1)(((char)p) + c.szPage) static PgHdr1 PAGE_TO_PGHDR1(PCache1 c, PgHdr p) { return(p.pPgHdr1); }
static u32 pcache1CountHash( PCache1 pCache ) { u32 nPage = 0; for ( u32 h = 0; h < pCache.nHash; h++ ) { PgHdr1 pp = pCache.apHash[h]; while ( pp != null ) { nPage++; pp = pp.pNext; } } return nPage; }
/* ** 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, u32 iLimit ) { //TESTONLY( unsigned int nPage = 0; ) /* Used to assert pCache->nPage is correct */ #if !NDEBUG || SQLITE_COVERAGE_TEST u32 nPage = 0; #endif u32 h; Debug.Assert( sqlite3_mutex_held( pcache1.mutex ) ); for ( h = 0; h < pCache.nHash; h++ ) { PgHdr1 pPrev = null; PgHdr1 pp = pCache.apHash[h]; PgHdr1 pPage; while ( ( pPage = pp ) != null ) { if ( pPage.iKey >= iLimit ) { pp = pPage.pNext; pcache1PinPage( pPage ); if ( pCache.apHash[h] == pPage ) pCache.apHash[h] = pPage.pNext; else pPrev.pNext = pp; pcache1FreePage( ref pPage ); pCache.nPage--; #if FALSE Debug.Assert( pcache1CountHash( pCache ) == pCache.nPage ); #endif } else { pp = pPage.pNext; //TESTONLY( nPage++; ) #if !NDEBUG || SQLITE_COVERAGE_TEST nPage++; #endif } pPrev = pPage; } } #if !NDEBUG || SQLITE_COVERAGE_TEST Debug.Assert( pCache.nPage == nPage ); #endif }
/* ** Implementation of the sqlite3_pcache.xCreate method. ** ** Allocate a new cache. */ static sqlite3_pcache pcache1Create( int szPage, int bPurgeable ) { PCache1 pCache; pCache = new PCache1();// (PCache1*)sqlite3_malloc( sizeof( PCache1 ) ); if ( pCache != null ) { //memset(pCache, 0, sizeof(PCache1)); pCache.szPage = szPage; pCache.bPurgeable = ( bPurgeable != 0 ); if ( bPurgeable != 0 ) { pCache.nMin = 10; pcache1EnterMutex(); pcache1.nMinPage += (int)pCache.nMin; pcache1LeaveMutex(); } } return pCache; }
/* ** 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); }