Index: src/malloc.c ================================================================== --- src/malloc.c +++ src/malloc.c @@ -10,11 +10,11 @@ ** ************************************************************************* ** ** Memory allocation functions used throughout sqlite. ** -** $Id: malloc.c,v 1.35 2008/08/20 14:49:24 danielk1977 Exp $ +** $Id: malloc.c,v 1.36 2008/08/21 12:19:44 danielk1977 Exp $ */ #include "sqliteInt.h" #include #include @@ -60,12 +60,15 @@ ** held by SQLite. An example of non-essential memory is memory used to ** cache database pages that are not currently in use. */ int sqlite3_release_memory(int n){ #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT - int nRet = sqlite3VdbeReleaseMemory(n); - nRet += sqlite3PagerReleaseMemory(n-nRet); + int nRet = 0; +#if 0 + nRet += sqlite3VdbeReleaseMemory(n); +#endif + nRet += sqlite3PcacheReleaseMemory(n-nRet); return nRet; #else return SQLITE_OK; #endif } Index: src/pager.c ================================================================== --- src/pager.c +++ src/pager.c @@ -16,11 +16,11 @@ ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.472 2008/08/21 04:35:19 danielk1977 Exp $ +** @(#) $Id: pager.c,v 1.473 2008/08/21 12:19:44 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" /* @@ -2451,26 +2451,30 @@ static int pagerStress(void *p){ Pager *pPager = (Pager *)p; PgHdr *pPg = sqlite3PcacheDirtyPage(pPager->pPCache); int rc = SQLITE_OK; - if( pPg && pPager->errCode==SQLITE_OK ){ + if( pPg ){ assert( pPg->flags&PGHDR_DIRTY ); - if( pPg->flags&PGHDR_NEED_SYNC ){ - rc = syncJournal(pPager); - if( rc==SQLITE_OK && pPager->fullSync - && !(sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND) - ){ - pPager->nRec = 0; - rc = writeJournalHdr(pPager); - } - } - if( rc==SQLITE_OK ){ - rc = pager_write_pagelist(pPg); - } - if( rc!=SQLITE_OK ){ - pager_error(pPager, rc); + if( pPager->errCode==SQLITE_OK ){ + if( pPg->flags&PGHDR_NEED_SYNC ){ + rc = syncJournal(pPager); + if( rc==SQLITE_OK && pPager->fullSync && + !(sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND) + ){ + pPager->nRec = 0; + rc = writeJournalHdr(pPager); + } + } + if( rc==SQLITE_OK ){ + rc = pager_write_pagelist(pPg); + } + if( rc!=SQLITE_OK ){ + pager_error(pPager, rc); + } + }else{ + sqlite3PcacheMakeClean(pPg); } } return rc; } @@ -2521,25 +2525,10 @@ } return rc; } -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT -/* -** This function is called to free superfluous dynamically allocated memory -** held by the pager system. Memory in use by any SQLite pager allocated -** by the current thread may be sqlite3_free()ed. -** -** nReq is the number of bytes of memory required. Once this much has -** been released, the function returns. The return value is the total number -** of bytes of memory released. -*/ -int sqlite3PagerReleaseMemory(int nReq){ - return 0; -} -#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */ - /* ** Read the content of page pPg out of the database file. */ static int readDbPage(Pager *pPager, PgHdr *pPg, Pgno pgno){ int rc; Index: src/pager.h ================================================================== --- src/pager.h +++ src/pager.h @@ -11,11 +11,11 @@ ************************************************************************* ** This header file defines the interface that the sqlite page cache ** subsystem. The page cache subsystem reads and writes a file a page ** at a time and provides a journal for rollback. ** -** @(#) $Id: pager.h,v 1.78 2008/08/20 14:49:25 danielk1977 Exp $ +** @(#) $Id: pager.h,v 1.79 2008/08/21 12:19:44 danielk1977 Exp $ */ #ifndef _PAGER_H_ #define _PAGER_H_ @@ -113,14 +113,10 @@ i64 sqlite3PagerJournalSizeLimit(Pager *, i64); void *sqlite3PagerTempSpace(Pager*); int sqlite3PagerSync(Pager *pPager); void sqlite3PagerAlwaysRollback(Pager *pPager); -#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) && !defined(SQLITE_OMIT_DISKIO) - int sqlite3PagerReleaseMemory(int); -#endif - #ifdef SQLITE_HAS_CODEC void sqlite3PagerSetCodec(Pager*,void*(*)(void*,void*,Pgno,int),void*); #endif #if !defined(NDEBUG) || defined(SQLITE_TEST) Index: src/pcache.c ================================================================== --- src/pcache.c +++ src/pcache.c @@ -9,11 +9,11 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file implements that page cache. ** -** @(#) $Id: pcache.c,v 1.3 2008/08/21 04:41:02 danielk1977 Exp $ +** @(#) $Id: pcache.c,v 1.4 2008/08/21 12:19:44 danielk1977 Exp $ */ #include "sqliteInt.h" /* ** A complete page cache is an instance of this structure. @@ -329,11 +329,14 @@ pcache.pFree = p->pNext; sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, sz); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1); return (void*)p; }else{ - void *p = sqlite3Malloc(sz); + void *p; + pcacheExitGlobal(); + p = sqlite3Malloc(sz); + pcacheEnterGlobal(); if( p ){ sz = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz); } return p; @@ -403,10 +406,69 @@ } pcacheFree(p->apSave[0]); pcacheFree(p->apSave[1]); pcacheFree(p); } + +/* +** 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_lru) ); + assert( !pcache.pStart ); + assert( p->apSave[0]==0 ); + assert( p->apSave[1]==0 ); + assert( p && p->pCache ); + return sqlite3MallocSize(p); +} + +static PgHdr *pcacheRecycle(PCache *pCache){ + PCache *pCsr; + PgHdr *p = 0; + + assert( pcache.isInit ); + assert( sqlite3_mutex_held(pcache.mutex_lru) ); + + if( !pcache.pLruTail && SQLITE_OK==sqlite3_mutex_try(pcache.mutex_mem2) ){ + + /* Invoke xStress() callbacks until the LRU list contains at least one + ** page that can be reused or until the xStress() callback of all + ** caches has been invoked. + */ + for(pCsr=pcache.pAll; pCsr&&!pcache.pLruTail; pCsr=pCsr->pNextAll){ + assert( pCsr->iInUseMM==0 ); + pCsr->iInUseMM = 1; + if( pCsr->xStress && (pCsr->iInUseDB==0 || pCache==pCsr) ){ + pcacheExitGlobal(); + pCsr->xStress(pCsr->pStress); + pcacheEnterGlobal(); + } + pCsr->iInUseMM = 0; + } + + sqlite3_mutex_leave(pcache.mutex_mem2); + } + + p = pcache.pLruTail; + + if( p ){ + pcacheRemoveFromLruList(p); + pcacheRemoveFromHash(p); + pcacheRemoveFromList(&p->pCache->pClean, p); + + /* If the always-rollback flag is set on the page being recycled, set + ** the always-rollback flag on the corresponding pager. + */ + if( p->flags&PGHDR_ALWAYS_ROLLBACK ){ + assert(p->pPager); + sqlite3PagerAlwaysRollback(p->pPager); + } + } + + 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. @@ -426,55 +488,18 @@ pcacheEnterGlobal(); if( (pcache.mxPage && pcache.nPage>=pcache.mxPage) || (!pcache.mxPage && bPurg && pcache.nPurgeable>=pcache.mxPagePurgeable) ){ - PCache *pCsr; - - /* If the above test succeeds, then a page will be obtained by recycling - ** an existing page. - */ - if( !pcache.pLruTail && SQLITE_OK==sqlite3_mutex_try(pcache.mutex_mem2) ){ - - /* Invoke xStress() callbacks until the LRU list contains at least one - ** page that can be reused or until the xStress() callback of all - ** caches has been invoked. - */ - for(pCsr=pcache.pAll; pCsr&&!pcache.pLruTail; pCsr=pCsr->pNextAll){ - assert( pCsr->iInUseMM==0 ); - pCsr->iInUseMM = 1; - if( pCsr->xStress && (pCsr->iInUseDB==0 || pCache==pCsr) ){ - pcacheExitGlobal(); - pCsr->xStress(pCsr->pStress); - pcacheEnterGlobal(); - } - pCsr->iInUseMM = 0; - } - - sqlite3_mutex_leave(pcache.mutex_mem2); - } - - p = pcache.pLruTail; - } - - if( p ){ - pcacheRemoveFromLruList(p); - pcacheRemoveFromHash(p); - pcacheRemoveFromList(&p->pCache->pClean, p); - - /* If the always-rollback flag is set on the page being recycled, set - ** the always-rollback flag on the corresponding pager. - */ - if( p->flags&PGHDR_ALWAYS_ROLLBACK ){ - assert(p->pPager); - sqlite3PagerAlwaysRollback(p->pPager); - } - - if( p->pCache->szPage!=szPage || p->pCache->szExtra!=szExtra ){ - pcachePageFree(p); - p = 0; - } + /* If the above test succeeds, then try to obtain a buffer by recycling + ** an existing page. */ + p = pcacheRecycle(pCache); + } + + if( p && (p->pCache->szPage!=szPage || p->pCache->szExtra!=szExtra) ){ + pcachePageFree(p); + p = 0; } if( !p ){ /* Allocate a new page object. */ p = pcachePageAlloc(szPage, szExtra, bPurg); @@ -1139,5 +1164,31 @@ */ void sqlite3PcacheUnlock(PCache *pCache){ pCache->iInUseDB--; assert( pCache->iInUseDB>=0 ); } + +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT +/* +** This function is called to free superfluous dynamically allocated memory +** held by the pager system. Memory in use by any SQLite pager allocated +** by the current thread may be sqlite3_free()ed. +** +** nReq is the number of bytes of memory required. Once this much has +** been released, the function returns. The return value is the total number +** of bytes of memory released. +*/ +int sqlite3PcacheReleaseMemory(int nReq){ + int nFree = 0; + if( pcache.pStart==0 ){ + PgHdr *p; + pcacheEnterGlobal(); + while( (nReq<0 || nFree