Exemple #1
0
 /*
 ** This function is used internally to remove the page pPage from the
 ** global LRU list, if is part of it. If pPage is not part of the global
 ** LRU list, then this function is a no-op.
 **
 ** The global mutex must be held when this function is called.
 */
 static void pcache1PinPage(PgHdr1 pPage)
 {
     Debug.Assert(sqlite3_mutex_held(pcache1.mutex));
     if (pPage != null && (pPage.pLruNext != null || pPage == pcache1.pLruTail))
     {
         if (pPage.pLruPrev != null)
         {
             pPage.pLruPrev.pLruNext = pPage.pLruNext;
         }
         if (pPage.pLruNext != null)
         {
             pPage.pLruNext.pLruPrev = pPage.pLruPrev;
         }
         if (pcache1.pLruHead == pPage)
         {
             pcache1.pLruHead = pPage.pLruNext;
         }
         if (pcache1.pLruTail == pPage)
         {
             pcache1.pLruTail = pPage.pLruPrev;
         }
         pPage.pLruNext = null;
         pPage.pLruPrev = null;
         pPage.pCache.nRecyclable--;
     }
 }
Exemple #2
0
        /*
        ** This function is used internally to remove the page pPage from the
        ** PGroup LRU list, if is part of it. If pPage is not part of the PGroup
        ** LRU list, then this function is a no-op.
        **
        ** The PGroup mutex must be held when this function is called.
        **
        ** If pPage is NULL then this routine is a no-op.
        */
        static void pcache1PinPage(PgHdr1 pPage)
        {
            PCache1 pCache;
            PGroup  pGroup;

            if (pPage == null)
            {
                return;
            }
            pCache = pPage.pCache;
            pGroup = pCache.pGroup;
            Debug.Assert(sqlite3_mutex_held(pGroup.mutex));
            if (pPage.pLruNext != null || pPage == pGroup.pLruTail)
            {
                if (pPage.pLruPrev != null)
                {
                    pPage.pLruPrev.pLruNext = pPage.pLruNext;
                }
                if (pPage.pLruNext != null)
                {
                    pPage.pLruNext.pLruPrev = pPage.pLruPrev;
                }
                if (pGroup.pLruHead == pPage)
                {
                    pGroup.pLruHead = pPage.pLruNext;
                }
                if (pGroup.pLruTail == pPage)
                {
                    pGroup.pLruTail = pPage.pLruPrev;
                }
                pPage.pLruNext = null;
                pPage.pLruPrev = null;
                pPage.pCache.nRecyclable--;
            }
        }
Exemple #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
        }
Exemple #4
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();
            }
Exemple #5
0
            public PgHdr pPgHdr = new PgHdr(); /* Pointer to Actual Page Header */

            public void Clear()
            {
                iKey   = 0;
                pNext  = null;
                pCache = null;
                pPgHdr.Clear();
            }
Exemple #6
0
 /*
 ** If there are currently more than pcache.nMaxPage pages allocated, try
 ** to recycle pages to reduce the number allocated to pcache.nMaxPage.
 */
 static void pcache1EnforceMaxPage()
 {
     Debug.Assert(sqlite3_mutex_held(pcache1.mutex));
     while (pcache1.nCurrentPage > pcache1.nMaxPage && pcache1.pLruTail != null)
     {
         PgHdr1 p = pcache1.pLruTail;
         pcache1PinPage(p);
         pcache1RemoveFromHash(p);
         pcache1FreePage(ref p);
     }
 }
Exemple #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 != null))
     {
         if (p.pCache.bPurgeable)
         {
             pcache1.nCurrentPage--;
         }
         pcache1Free(ref p.pPgHdr);//PGHDR1_TO_PAGE( p );
     }
 }
Exemple #8
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 );
     }
 }
Exemple #9
0
 /*
 ** If there are currently more than nMaxPage pages allocated, try
 ** to recycle pages to reduce the number allocated to nMaxPage.
 */
 static void pcache1EnforceMaxPage(PGroup pGroup)
 {
     Debug.Assert(sqlite3_mutex_held(pGroup.mutex));
     while (pGroup.nCurrentPage > pGroup.nMaxPage && pGroup.pLruTail != null)
     {
         PgHdr1 p = pGroup.pLruTail;
         Debug.Assert(p.pCache.pGroup == pGroup);
         pcache1PinPage(p);
         pcache1RemoveFromHash(p);
         pcache1FreePage(ref p);
     }
 }
Exemple #10
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
        }
Exemple #11
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();
        }
Exemple #12
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);
        }
Exemple #13
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);
        }
Exemple #14
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
        }
Exemple #15
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
        }
Exemple #16
0
            public void Clear()
            {
                this.pData  = null;
                this.pExtra = null;
                this.pDirty = null;
                this.pgno   = 0;
                this.pPager = null;
#if SQLITE_CHECK_PAGES
                this.pageHash = 0;
#endif
                this.flags      = 0;
                this.nRef       = 0;
                this.pCache     = null;
                this.pDirtyNext = null;
                this.pDirtyPrev = null;
                this.pPgHdr1    = null;
            }
Exemple #17
0
            public void Clear()
            {
                sqlite3_free(ref pData);
                pData  = null;
                pExtra = null;
                pDirty = null;
                pgno   = 0;
                pPager = null;
#if SQLITE_CHECK_PAGES
                this.pageHash = 0;
#endif
                flags      = 0;
                nRef       = 0;
                pCache     = null;
                pDirtyNext = null;
                pDirtyPrev = null;
                pPgHdr1    = null;
            }
Exemple #18
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);
        }
Exemple #19
0
            public void Clear()
            {
                sqlite3_free(ref this.pData);
                this.pData  = null;
                this.pExtra = null;
                this.pDirty = null;
                this.pgno   = 0;
                this.pPager = null;
#if SQLITE_CHECK_PAGES
                this.pageHash = 0;
#endif
                this.flags          = 0;
                this.nRef           = 0;
                this.CacheAllocated = false;
                this.pCache         = null;
                this.pDirtyNext     = null;
                this.pDirtyPrev     = null;
                this.pPgHdr1        = null;
            }
Exemple #20
0
			public void Clear()
			{
				sqlite3_free(ref this.pData);
				this.pData = null;
				this.pExtra = null;
				this.pDirty = null;
				this.pgno = 0;
				this.pPager = null;
#if SQLITE_CHECK_PAGES
this.pageHash=0;
#endif
				this.flags = 0;
				this.nRef = 0;
				this.CacheAllocated = false;
				this.pCache = null;
				this.pDirtyNext = null;
				this.pDirtyPrev = null;
				this.pPgHdr1 = null;
			}
Exemple #21
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();
        }
Exemple #22
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();
        }
Exemple #23
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);
        }
Exemple #24
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--;
        }
Exemple #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--;
        }
Exemple #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);
        }
    /******************************************************************************/
    /******** 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 );
    }
Exemple #28
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);
        }
    /*
    ** 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
    }
      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();
      }
 /*
 ** 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 != null ) )
   {
     if ( p.pCache.bPurgeable )
     {
       pcache1.nCurrentPage--;
     }
     pcache1Free( ref p.pPgHdr );//PGHDR1_TO_PAGE( p );
   }
 }
 /*
 ** 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;
 }
Exemple #33
0
 /*
 ** When a PgHdr1 structure is allocated, the associated PCache1.szPage
 ** bytes of data are located directly before it in memory (i.e. the total
 ** size of the allocation is sizeof(PgHdr1)+PCache1.szPage byte). The
 ** PGHDR1_TO_PAGE() macro takes a pointer to a PgHdr1 structure as
 ** an argument and returns a pointer to the associated block of szPage
 ** bytes. The PAGE_TO_PGHDR1() macro does the opposite: its argument is
 ** a pointer to a block of szPage bytes of data and the return value is
 ** a pointer to the associated PgHdr1 structure.
 **
 **   Debug.Assert( PGHDR1_TO_PAGE(PAGE_TO_PGHDR1(pCache, X))==X );
 */
 //#define PGHDR1_TO_PAGE(p)    (void)(((char)p) - p.pCache.szPage)
 static PgHdr PGHDR1_TO_PAGE(PgHdr1 p)
 {
     return(p.pPgHdr);
 }
 /*
 ** When a PgHdr1 structure is allocated, the associated PCache1.szPage
 ** bytes of data are located directly before it in memory (i.e. the total
 ** size of the allocation is sizeof(PgHdr1)+PCache1.szPage byte). The
 ** PGHDR1_TO_PAGE() macro takes a pointer to a PgHdr1 structure as
 ** an argument and returns a pointer to the associated block of szPage
 ** bytes. The PAGE_TO_PGHDR1() macro does the opposite: its argument is
 ** a pointer to a block of szPage bytes of data and the return value is
 ** a pointer to the associated PgHdr1 structure.
 **
 **   assert( PGHDR1_TO_PAGE(PAGE_TO_PGHDR1(pCache, X))==X );
 */
 //#define PGHDR1_TO_PAGE(p)    (void*)(((char*)p) - p->pCache->szPage)
 static PgHdr PGHDR1_TO_PAGE( PgHdr1 p ) { return p.pPgHdr; }
    /*
    ** 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--;
    }
 /*
 ** This function is used internally to remove the page pPage from the
 ** global LRU list, if is part of it. If pPage is not part of the global
 ** LRU list, then this function is a no-op.
 **
 ** The global mutex must be held when this function is called.
 */
 static void pcache1PinPage( PgHdr1 pPage )
 {
   Debug.Assert( sqlite3_mutex_held( pcache1.mutex ) );
   if ( pPage != null && ( pPage.pLruNext != null || pPage == pcache1.pLruTail ) )
   {
     if ( pPage.pLruPrev != null )
     {
       pPage.pLruPrev.pLruNext = pPage.pLruNext;
     }
     if ( pPage.pLruNext != null )
     {
       pPage.pLruNext.pLruPrev = pPage.pLruPrev;
     }
     if ( pcache1.pLruHead == pPage )
     {
       pcache1.pLruHead = pPage.pLruNext;
     }
     if ( pcache1.pLruTail == pPage )
     {
       pcache1.pLruTail = pPage.pLruPrev;
     }
     pPage.pLruNext = null;
     pPage.pLruPrev = null;
     pPage.pCache.nRecyclable--;
   }
 }
Exemple #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);
        }
Exemple #38
0
      public void Clear()
      {
        this.pData = null;
        this.pExtra = null;
        this.pDirty = null;
        this.pgno = 0;
        this.pPager = null;
#if SQLITE_CHECK_PAGES
this.pageHash=0;
#endif
        this.flags = 0;
        this.nRef = 0;
        this.pCache = null;
        this.pDirtyNext = null;
        this.pDirtyPrev = null;
        this.pPgHdr1 = null;
      }