📄 pcache.c
字号:
if( p ){ sz = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz); } return p; }}void *sqlite3PageMalloc(sz){ void *p; pcacheEnterMutex(); p = pcacheMalloc(sz, 0); pcacheExitMutex(); return p;}/*** Release a pager memory allocation*/void pcacheFree(void *p){ assert( sqlite3_mutex_held(pcache.mutex) ); if( p==0 ) return; if( p>=pcache.pStart && p<pcache.pEnd ){ PgFreeslot *pSlot; sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1); pSlot = (PgFreeslot*)p; pSlot->pNext = pcache.pFree; pcache.pFree = pSlot; }else{ int iSize = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize); sqlite3_free(p); }}void sqlite3PageFree(void *p){ pcacheEnterMutex(); pcacheFree(p); pcacheExitMutex();}/*** Allocate a new page.*/static PgHdr *pcachePageAlloc(PCache *pCache){ PgHdr *p; int sz = sizeof(*p) + pCache->szPage + pCache->szExtra; assert( sqlite3_mutex_held(pcache.mutex) ); p = pcacheMalloc(sz, pCache); if( p==0 ) return 0; memset(p, 0, sizeof(PgHdr)); p->pData = (void*)&p[1]; p->pExtra = (void*)&((char*)p->pData)[pCache->szPage]; if( pCache->bPurgeable ){ pcache.nCurrentPage++; } return p;}/*** Deallocate a page*/static void pcachePageFree(PgHdr *p){ assert( sqlite3_mutex_held(pcache.mutex) ); if( p->pCache->bPurgeable ){ pcache.nCurrentPage--; } pcacheFree(p->apSave[0]); pcacheFree(p->apSave[1]); pcacheFree(p);}#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT/*** Return the number of bytes that will be returned to the heap when** the argument is passed to pcachePageFree().*/static int pcachePageSize(PgHdr *p){ assert( sqlite3_mutex_held(pcache.mutex) ); assert( !pcache.pStart ); assert( p->apSave[0]==0 ); assert( p->apSave[1]==0 ); assert( p && p->pCache ); return sqlite3MallocSize(p);}#endif/*** Attempt to 'recycle' a page from the global LRU list. Only clean,** unreferenced pages from purgeable caches are eligible for recycling.**** This function removes page pcache.pLruTail from the global LRU list,** and from the hash-table and PCache.pClean list of the owner pcache.** There should be no other references to the page.**** A pointer to the recycled page is returned, or NULL if no page is** eligible for recycling.*/static PgHdr *pcacheRecyclePage(){ PgHdr *p = 0; assert( sqlite3_mutex_held(pcache.mutex) ); if( (p=pcache.pLruTail) ){ assert( (p->flags&PGHDR_DIRTY)==0 ); pcacheRemoveFromLruList(p); pcacheRemoveFromHash(p); pcacheRemoveFromList(&p->pCache->pClean, p); } return p;}/*** Obtain space for a page. Try to recycle an old page if the limit on the ** number of pages has been reached. If the limit has not been reached or** there are no pages eligible for recycling, allocate a new page.**** Return a pointer to the new page, or NULL if an OOM condition occurs.*/static int pcacheRecycleOrAlloc(PCache *pCache, PgHdr **ppPage){ PgHdr *p = 0; int szPage = pCache->szPage; int szExtra = pCache->szExtra; assert( pcache.isInit ); assert( sqlite3_mutex_held(pcache.mutex) ); *ppPage = 0; /* If we have reached the limit for pinned/dirty pages, and there is at ** least one dirty page, invoke the xStress callback to cause a page to ** become clean. */ expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) ); expensive_assert( pcacheCheckSynced(pCache) ); if( pCache->xStress && pCache->pDirty && pCache->nPinned>=(pcache.nMaxPage+pCache->nMin-pcache.nMinPage) ){ PgHdr *pPg; assert(pCache->pDirtyTail); for(pPg=pCache->pSynced; pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC)); pPg=pPg->pPrev ); if( !pPg ){ for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pPrev); } if( pPg ){ int rc; pcacheExitMutex(); rc = pCache->xStress(pCache->pStress, pPg); pcacheEnterMutex(); if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ return rc; } } } /* If the global page limit has been reached, try to recycle a page. */ if( pCache->bPurgeable && pcache.nCurrentPage>=pcache.nMaxPage ){ p = pcacheRecyclePage(); } /* If a page has been recycled but it is the wrong size, free it. */ if( p && (p->pCache->szPage!=szPage || p->pCache->szPage!=szExtra) ){ pcachePageFree(p); p = 0; } if( !p ){ p = pcachePageAlloc(pCache); } *ppPage = p; return (p?SQLITE_OK:SQLITE_NOMEM);}/*************************************************** General Interfaces ********** Initialize and shutdown the page cache subsystem. Neither of these ** functions are threadsafe.*/int sqlite3PcacheInitialize(void){ assert( pcache.isInit==0 ); memset(&pcache, 0, sizeof(pcache)); if( sqlite3Config.bCoreMutex ){ /* No need to check the return value of sqlite3_mutex_alloc(). ** Allocating a static mutex cannot fail. */ pcache.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU); } pcache.isInit = 1; return SQLITE_OK;}void sqlite3PcacheShutdown(void){ memset(&pcache, 0, sizeof(pcache));}/*** Return the size in bytes of a PCache object.*/int sqlite3PcacheSize(void){ return sizeof(PCache); }/*** Create a new PCache object. Storage space to hold the object** has already been allocated and is passed in as the p pointer.*/void sqlite3PcacheOpen( int szPage, /* Size of every page */ int szExtra, /* Extra space associated with each page */ int bPurgeable, /* True if pages are on backing store */ void (*xDestroy)(PgHdr*), /* Called to destroy a page */ int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */ void *pStress, /* Argument to xStress */ PCache *p /* Preallocated space for the PCache */){ assert( pcache.isInit ); memset(p, 0, sizeof(PCache)); p->szPage = szPage; p->szExtra = szExtra; p->bPurgeable = bPurgeable; p->xDestroy = xDestroy; p->xStress = xStress; p->pStress = pStress; p->nMax = 100; p->nMin = 10; pcacheEnterMutex(); if( bPurgeable ){ pcache.nMaxPage += p->nMax; pcache.nMinPage += p->nMin; } pcacheExitMutex();}/*** Change the page size for PCache object. This can only happen** when the cache is empty.*/void sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ assert(pCache->nPage==0); pCache->szPage = szPage;}/*** Try to obtain a page from the cache.*/int sqlite3PcacheFetch( PCache *pCache, /* Obtain the page from this cache */ Pgno pgno, /* Page number to obtain */ int createFlag, /* If true, create page if it does not exist already */ PgHdr **ppPage /* Write the page here */){ int rc = SQLITE_OK; PgHdr *pPage = 0; assert( pcache.isInit ); assert( pCache!=0 ); assert( pgno>0 ); expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) ); pcacheEnterMutex(); /* Search the hash table for the requested page. Exit early if it is found. */ if( pCache->apHash ){ u32 h = pgno % pCache->nHash; for(pPage=pCache->apHash[h]; pPage; pPage=pPage->pNextHash){ if( pPage->pgno==pgno ){ if( pPage->nRef==0 ){ if( 0==(pPage->flags&PGHDR_DIRTY) ){ pcacheRemoveFromLruList(pPage); pCache->nPinned++; } pCache->nRef++; } pPage->nRef++; break; } } } if( !pPage && createFlag ){ if( pCache->nHash<=pCache->nPage ){ rc = pcacheResizeHash(pCache, pCache->nHash<256 ? 256 : pCache->nHash*2); } if( rc==SQLITE_OK ){ rc = pcacheRecycleOrAlloc(pCache, &pPage); } if( rc==SQLITE_OK ){ pPage->pPager = 0; pPage->flags = 0; pPage->pDirty = 0; pPage->pgno = pgno; pPage->pCache = pCache; pPage->nRef = 1; pCache->nRef++; pCache->nPinned++; pcacheAddToList(&pCache->pClean, pPage); pcacheAddToHash(pPage); } } pcacheExitMutex(); *ppPage = pPage; expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) ); assert( pPage || !createFlag || rc!=SQLITE_OK ); return rc;}/*** Dereference a page. When the reference count reaches zero,** move the page to the LRU list if it is clean.*/void sqlite3PcacheRelease(PgHdr *p){ assert( p->nRef>0 ); p->nRef--; if( p->nRef==0 ){ PCache *pCache = p->pCache; if( p->pCache->xDestroy ){ p->pCache->xDestroy(p); } pCache->nRef--; if( (p->flags&PGHDR_DIRTY)==0 ){ pCache->nPinned--; pcacheEnterMutex(); if( pcache.nCurrentPage>pcache.nMaxPage ){ pcacheRemoveFromList(&pCache->pClean, p); pcacheRemoveFromHash(p); pcachePageFree(p); }else{ pcacheAddToLruList(p); } pcacheExitMutex(); }else{ /* Move the page to the head of the caches dirty list. */ pcacheRemoveFromList(&pCache->pDirty, p); pcacheAddToList(&pCache->pDirty, p); } }}void sqlite3PcacheRef(PgHdr *p){ assert(p->nRef>0); p->nRef++;}/*** Drop a page from the cache. There must be exactly one reference to the** page. This function deletes that reference, so after it returns the** page pointed to by p is invalid.*/void sqlite3PcacheDrop(PgHdr *p){ PCache *pCache; assert( p->nRef==1 ); assert( 0==(p->flags&PGHDR_DIRTY) ); pCache = p->pCache; pCache->nRef--; pCache->nPinned--; pcacheEnterMutex(); pcacheRemoveFromList(&pCache->pClean, p); pcacheRemoveFromHash(p); pcachePageFree(p); pcacheExitMutex();}/*** Make sure the page is marked as dirty. If it isn't dirty already,** make it so.*/void sqlite3PcacheMakeDirty(PgHdr *p){ PCache *pCache; p->flags &= ~PGHDR_DONT_WRITE; if( p->flags & PGHDR_DIRTY ) return; assert( (p->flags & PGHDR_DIRTY)==0 ); assert( p->nRef>0 ); pCache = p->pCache; pcacheEnterMutex(); pcacheRemoveFromList(&pCache->pClean, p); pcacheAddToList(&pCache->pDirty, p); pcacheExitMutex(); p->flags |= PGHDR_DIRTY;}void pcacheMakeClean(PgHdr *p){ PCache *pCache = p->pCache; assert( p->apSave[0]==0 && p->apSave[1]==0 ); assert( p->flags & PGHDR_DIRTY ); pcacheRemoveFromList(&pCache->pDirty, p); pcacheAddToList(&pCache->pClean, p); p->flags &= ~PGHDR_DIRTY; if( p->nRef==0 ){ pcacheAddToLruList(p); pCache->nPinned--; } expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );}/*** Make sure the page is marked as clean. If it isn't clean already,** make it so.*/void sqlite3PcacheMakeClean(PgHdr *p){ if( (p->flags & PGHDR_DIRTY) ){ pcacheEnterMutex(); pcacheMakeClean(p); pcacheExitMutex(); }}/*** Make every page in the cache clean.*/void sqlite3PcacheCleanAll(PCache *pCache){ PgHdr *p; pcacheEnterMutex(); while( (p = pCache->pDirty)!=0 ){ assert( p->apSave[0]==0 && p->apSave[1]==0 ); pcacheRemoveFromList(&pCache->pDirty, p); p->flags &= ~PGHDR_DIRTY;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -