Example #1
0
            public PgHdr pPgHdr = new PgHdr(); /* Pointer to Actual Page Header */

            public void Clear()
            {
                iKey   = 0;
                pNext  = null;
                pCache = null;
                pPgHdr.Clear();
            }
Example #2
0
            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();
            }
Example #3
0
        /*
        ** 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
        }
Example #4
0
        /*
        ** 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);
        }
Example #5
0
        /*
        ** 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);
        }
Example #6
0
 /*
 ** 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());
     }
 }
Example #7
0
 /*
 ** 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 );
     }
 }
Example #8
0
        /*
        ** 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);
        }
Example #9
0
        /*
        ** 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
        }
Example #10
0
        /*
        ** 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 );
        }
Example #11
0
        /*
        ** 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();
            }
        }
Example #12
0
        /*
        ** 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();
        }
Example #13
0
        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);
        }
Example #14
0
        /******************************************************************************/
        /******** 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);
        }
Example #15
0
        /*
        ** 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);
            }
        }
Example #16
0
        /*
        ** 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
        }
Example #17
0
        /*
        ** 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
        }
Example #18
0
        /*
        ** 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;
        }
Example #19
0
        /*
        ** 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();
        }
Example #20
0
        /*
        ** 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);
        }
Example #21
0
        /*
        ** 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();
        }
Example #22
0
        /*
        ** 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);
        }
Example #23
0
        /*
        ** 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--;
        }
Example #24
0
        /*
        ** 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);
        }
Example #25
0
        /*
        ** 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--;
        }
Example #26
0
        /*
        ** 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);
        }
Example #27
0
        /*
        ** 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);
        }
Example #28
0
 internal void freeTempSpace()
 {
     PCache1.sqlite3PageFree(ref this.pTmpSpace);
 }
Example #29
0
 //#define PAGE_TO_PGHDR1(c, p) (PgHdr1*)(((char*)p) + c->szPage)
 static PgHdr1 PAGE_TO_PGHDR1( PCache1 c, PgHdr p ) { return p.pPgHdr1; }
Example #30
0
 /*
 ** 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;
 }
Example #31
0
    /******************************************************************************/
    /******** 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 );
    }
Example #32
0
 //#define PAGE_TO_PGHDR1(c, p) (PgHdr1)(((char)p) + c.szPage)
 static PgHdr1 PAGE_TO_PGHDR1(PCache1 c, PgHdr p)
 {
     return(p.pPgHdr1);
 }
Example #33
0
 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;
 }
Example #34
0
    /*
    ** 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
    }
Example #35
0
    /*
    ** 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;
    }
Example #36
0
      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();
      }
Example #37
0
        /*
        ** 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);
        }