📄 pcache1.c
字号:
/*** 2008 November 05**** The author disclaims copyright to this source code. In place of** a legal notice, here is a blessing:**** May you do good and not evil.** May you find forgiveness for yourself and forgive others.** May you share freely, never taking more than you give.******************************************************************************* This file implements the default page cache implementation (the** sqlite3_pcache interface). It also contains part of the implementation** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features.** If the default page cache implementation is overriden, then neither of** these two features are available.**** @(#) $Id: pcache1.c,v 1.8 2009/01/23 16:45:01 danielk1977 Exp $*/#include "sqliteInt.h"typedef struct PCache1 PCache1;typedef struct PgHdr1 PgHdr1;typedef struct PgFreeslot PgFreeslot;/* Pointers to structures of this type are cast and returned as ** opaque sqlite3_pcache* handles*/struct PCache1 { /* Cache configuration parameters. Page size (szPage) and the purgeable ** flag (bPurgeable) are set when the cache is created. nMax may be ** modified at any time by a call to the pcache1CacheSize() method. ** The global mutex must be held when accessing nMax. */ int szPage; /* Size of allocated pages in bytes */ int bPurgeable; /* True if cache is purgeable */ unsigned int nMin; /* Minimum number of pages reserved */ unsigned int nMax; /* Configured "cache_size" value */ /* Hash table of all pages. The following variables may only be accessed ** when the accessor is holding the global mutex (see pcache1EnterMutex() ** and pcache1LeaveMutex()). */ unsigned int nRecyclable; /* Number of pages in the LRU list */ unsigned int nPage; /* Total number of pages in apHash */ unsigned int nHash; /* Number of slots in apHash[] */ PgHdr1 **apHash; /* Hash table for fast lookup by key */ unsigned int iMaxKey; /* Largest key seen since xTruncate() */};/*** Each cache entry is represented by an instance of the following ** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated ** directly after the structure in memory (see the PGHDR1_TO_PAGE() ** macro below).*/struct PgHdr1 { unsigned int iKey; /* Key value (page number) */ PgHdr1 *pNext; /* Next in hash table chain */ PCache1 *pCache; /* Cache that currently owns this page */ PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */};/*** Free slots in the allocator used to divide up the buffer provided using** the SQLITE_CONFIG_PAGECACHE mechanism.*/struct PgFreeslot { PgFreeslot *pNext; /* Next free slot */};/*** Global data used by this cache.*/static SQLITE_WSD struct PCacheGlobal { sqlite3_mutex *mutex; /* static mutex MUTEX_STATIC_LRU */ int nMaxPage; /* Sum of nMaxPage for purgeable caches */ int nMinPage; /* Sum of nMinPage for purgeable caches */ int nCurrentPage; /* Number of purgeable pages allocated */ PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */ /* Variables related to SQLITE_CONFIG_PAGECACHE settings. */ int szSlot; /* Size of each free slot */ void *pStart, *pEnd; /* Bounds of pagecache malloc range */ PgFreeslot *pFree; /* Free page blocks */} pcache1_g;/*** All code in this file should access the global structure above via the** alias "pcache1". This ensures that the WSD emulation is used when** compiling for systems that do not support real WSD.*/#define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g))/*** When a PgHdr1 structure is allocated, the associated PCache1.szPage** bytes of data are located directly after 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(X))==X );*/#define PGHDR1_TO_PAGE(p) (void *)(&((unsigned char *)p)[sizeof(PgHdr1)])#define PAGE_TO_PGHDR1(p) (PgHdr1 *)(&((unsigned char *)p)[-1*(int)sizeof(PgHdr1)])/*** Macros to enter and leave the global LRU mutex.*/#define pcache1EnterMutex() sqlite3_mutex_enter(pcache1.mutex)#define pcache1LeaveMutex() sqlite3_mutex_leave(pcache1.mutex)/******************************************************************************//******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************//*** This function is called during initialization if a static buffer is ** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE** verb to sqlite3_config(). Parameter pBuf points to an allocation large** enough to contain 'n' buffers of 'sz' bytes each.*/void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ PgFreeslot *p; sz &= ~7; pcache1.szSlot = sz; pcache1.pStart = pBuf; pcache1.pFree = 0; while( n-- ){ p = (PgFreeslot*)pBuf; p->pNext = pcache1.pFree; pcache1.pFree = p; pBuf = (void*)&((char*)pBuf)[sz]; } pcache1.pEnd = pBuf;}/*** Malloc function used within this file to allocate space from the buffer** configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no ** such buffer exists or there is no space left in it, this function falls ** back to sqlite3Malloc().*/static void *pcache1Alloc(int nByte){ void *p; assert( sqlite3_mutex_held(pcache1.mutex) ); if( nByte<=pcache1.szSlot && pcache1.pFree ){ p = (PgHdr1 *)pcache1.pFree; pcache1.pFree = pcache1.pFree->pNext; sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1); }else{ /* Allocate a new buffer using sqlite3Malloc. Before doing so, exit the ** global pcache mutex and unlock the pager-cache object pCache. This is ** so that if the attempt to allocate a new buffer causes the the ** configured soft-heap-limit to be breached, it will be possible to ** reclaim memory from this pager-cache. */ pcache1LeaveMutex(); p = sqlite3Malloc(nByte); pcache1EnterMutex(); if( p ){ int sz = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz); } } return p;}/*** Free an allocated buffer obtained from pcache1Alloc().*/static void pcache1Free(void *p){ assert( sqlite3_mutex_held(pcache1.mutex) ); if( p==0 ) return; if( p>=pcache1.pStart && p<pcache1.pEnd ){ PgFreeslot *pSlot; sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1); pSlot = (PgFreeslot*)p; pSlot->pNext = pcache1.pFree; pcache1.pFree = pSlot; }else{ int iSize = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize); sqlite3_free(p); }}/*** Allocate a new page object initially associated with cache pCache.*/static PgHdr1 *pcache1AllocPage(PCache1 *pCache){ int nByte = sizeof(PgHdr1) + pCache->szPage; PgHdr1 *p = (PgHdr1 *)pcache1Alloc(nByte); if( p ){ if( pCache->bPurgeable ){ pcache1.nCurrentPage++; } } return p;}/*** Free a page object allocated by pcache1AllocPage().*/static void pcache1FreePage(PgHdr1 *p){ if( p ){ if( p->pCache->bPurgeable ){ pcache1.nCurrentPage--; } pcache1Free(p); }}/*** Malloc function used by SQLite to obtain space from the buffer configured** using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no such buffer** exists, this function falls back to sqlite3Malloc().*/void *sqlite3PageMalloc(int sz){ void *p; pcache1EnterMutex(); p = pcache1Alloc(sz); pcache1LeaveMutex(); return p;}/*** Free an allocated buffer obtained from sqlite3PageMalloc().*/void sqlite3PageFree(void *p){ pcache1EnterMutex(); pcache1Free(p); pcache1LeaveMutex();}/******************************************************************************//******** 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; unsigned int nNew; unsigned int i; assert( sqlite3_mutex_held(pcache1.mutex) ); nNew = p->nHash*2; if( nNew<256 ){ nNew = 256; } pcache1LeaveMutex(); if( p->nHash ){ sqlite3BeginBenignMalloc(); } apNew = (PgHdr1 **)sqlite3_malloc(sizeof(PgHdr1 *)*nNew); if( p->nHash ){ sqlite3EndBenignMalloc(); } pcache1EnterMutex(); if( apNew ){ memset(apNew, 0, sizeof(PgHdr1 *)*nNew); for(i=0; i<p->nHash; i++){ PgHdr1 *pPage; PgHdr1 *pNext = p->apHash[i]; while( (pPage = pNext)!=0 ){ unsigned int h = pPage->iKey % nNew; pNext = pPage->pNext; pPage->pNext = apNew[h]; apNew[h] = pPage; } } sqlite3_free(p->apHash); p->apHash = apNew; p->nHash = nNew; } return (p->apHash ? SQLITE_OK : SQLITE_NOMEM);}/*** 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){ assert( sqlite3_mutex_held(pcache1.mutex) ); if( pPage && (pPage->pLruNext || pPage==pcache1.pLruTail) ){ if( pPage->pLruPrev ){ pPage->pLruPrev->pLruNext = pPage->pLruNext; } if( pPage->pLruNext ){ pPage->pLruNext->pLruPrev = pPage->pLruPrev; } if( pcache1.pLruHead==pPage ){ pcache1.pLruHead = pPage->pLruNext; } if( pcache1.pLruTail==pPage ){ pcache1.pLruTail = pPage->pLruPrev; } pPage->pLruNext = 0; pPage->pLruPrev = 0; pPage->pCache->nRecyclable--; }}/*** 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){ unsigned int h; PCache1 *pCache = pPage->pCache; PgHdr1 **pp; h = pPage->iKey % pCache->nHash; for(pp=&pCache->apHash[h]; (*pp)!=pPage; pp=&(*pp)->pNext); *pp = (*pp)->pNext; pCache->nPage--;}/*** 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(void){ assert( sqlite3_mutex_held(pcache1.mutex) ); while( pcache1.nCurrentPage>pcache1.nMaxPage && pcache1.pLruTail ){ PgHdr1 *p = pcache1.pLruTail; pcache1PinPage(p); pcache1RemoveFromHash(p); pcache1FreePage(p); }}/*** 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, unsigned int iLimit ){ unsigned int h; assert( sqlite3_mutex_held(pcache1.mutex) ); for(h=0; h<pCache->nHash; h++){ PgHdr1 **pp = &pCache->apHash[h]; PgHdr1 *pPage; while( (pPage = *pp)!=0 ){ if( pPage->iKey>=iLimit ){ pcache1PinPage(pPage); *pp = pPage->pNext; pcache1FreePage(pPage); }else{ pp = &pPage->pNext; } } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -