From a85f7e36e8fc5aa88625726b113be6704ce3792a Mon Sep 17 00:00:00 2001
From: drh <drh@noemail.net>
Date: Thu, 28 Aug 2008 02:26:07 +0000
Subject: [PATCH] Miscellaneous cleanup in the new pcache code. (CVS 5629)

FossilOrigin-Name: da1777259f53c2e20c7ced06bf6f2a550f0ea0fc
---
 manifest        |  20 ++--
 manifest.uuid   |   2 +-
 src/pager.c     |   9 +-
 src/pcache.c    | 274 ++++++++++++++++++++++++++----------------------
 src/pcache.h    |  22 ++--
 src/test_func.c |   7 +-
 6 files changed, 183 insertions(+), 151 deletions(-)

diff --git a/manifest b/manifest
index 2b436e05ac..ad116edebf 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C If\sany\serror\soccurs\sduring\ssqlite3_open(),\smove\sthe\sdatabase\shandle\sinto\s"sick"\sstate.\sWhen\sin\sthe\ssick\sstate\sthe\suser\scan\suse\ssqlite3_errcode()\sand\ssqlite3_errmsg(),\sbut\snot\smuch\selse.\s(CVS\s5628)
-D 2008-08-27T19:01:58
+C Miscellaneous\scleanup\sin\sthe\snew\spcache\scode.\s(CVS\s5629)
+D 2008-08-28T02:26:07
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in 689e14735f862a5553bceef206d8c13e29504e44
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -134,11 +134,11 @@ F src/os_common.h 24525d8b7bce66c374dfc1810a6c9043f3359b60
 F src/os_os2.c e391fc95adc744bbdcefd4d11e3066998185a0a0
 F src/os_unix.c 4665cef7639dd937893c3ea076f0e8a8f215bb32
 F src/os_win.c aefe9ee26430678a19a058a874e4e2bd91398142
-F src/pager.c 84f4c171a0cb4561291f212508b1a91a8a325abb
+F src/pager.c 032d11049af4ec49bdbaa3584e7ce9887098b66a
 F src/pager.h 914103bb62dbcc3d8e9f14baec812d027264d457
 F src/parse.y d0f76d2cb8d6883d5600dc20beb961a6022b94b8
-F src/pcache.c 3d9d933bb22f10956ab78d83798d88ca9a147e86
-F src/pcache.h 7a50b77f06c220ff7696be1a9f2a17c9e6ddc486
+F src/pcache.c aa609c9b1fc1a2b00f7d7bfb3d06b1e507767b93
+F src/pcache.h bd373ee3e4db310d6bbe7fa6d8d971de9678edd8
 F src/pragma.c f5b271b090af7fcedd308d7c5807a5503f7a853d
 F src/prepare.c c197041e0c4770672cda75e6bfe10242f885e510
 F src/printf.c 785f87120589c1db672e37c6eb1087c456e6f84d
@@ -167,7 +167,7 @@ F src/test_autoext.c f53b0cdf7bf5f08100009572a5d65cdb540bd0ad
 F src/test_btree.c 7170e0c922ed3979f2d38f4a3f84728e5740dfc3
 F src/test_config.c 224f699a34d45eb8ac5c22a7ad6cdbb8edf0ba28
 F src/test_devsym.c 6012cb8e3acf812513511025a4fa5d626e0ba19b
-F src/test_func.c bc648b7747320e037d756acfa1037bd8dedf3f8b
+F src/test_func.c a55c4d5479ff2eb5c0a22d4d88e9528ab59c953b
 F src/test_hexio.c 2f1122aa3f012fa0142ee3c36ce5c902a70cd12f
 F src/test_loadext.c 97dc8800e46a46ed002c2968572656f37e9c0dd9
 F src/test_malloc.c 49abbf5d9c71fb06cf7a7cf96f9b9a799b77a421
@@ -624,7 +624,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 1dbced29de5f59ba2ebf877edcadf171540374d1
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
-P 39c34e2238c27b2a2f4f0b896126ccbd299114c5
-R 0bf03a77bd82f6ac6f412ad9339ae6a4
-U danielk1977
-Z ac6800a3d7725cc034896ca6e2243181
+P ce9c74eaab459ddde213c828e821940f5d6cb354
+R 55c280ec697a3a6039a240cf488ad9ad
+U drh
+Z 7df830e688305e6be422e59df78b53fe
diff --git a/manifest.uuid b/manifest.uuid
index 9e981efe54..609a5bfb32 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-ce9c74eaab459ddde213c828e821940f5d6cb354
\ No newline at end of file
+da1777259f53c2e20c7ced06bf6f2a550f0ea0fc
\ No newline at end of file
diff --git a/src/pager.c b/src/pager.c
index b5296fbcd6..9b063b7b2c 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -18,7 +18,7 @@
 ** file simultaneously, or one process from reading the database while
 ** another is writing.
 **
-** @(#) $Id: pager.c,v 1.484 2008/08/27 18:03:20 drh Exp $
+** @(#) $Id: pager.c,v 1.485 2008/08/28 02:26:07 drh Exp $
 */
 #ifndef SQLITE_OMIT_DISKIO
 #include "sqliteInt.h"
@@ -3271,15 +3271,14 @@ static int pager_write(PgHdr *pPg){
      && !pageInStatement(pPg) 
      && (int)pPg->pgno<=pPager->stmtSize 
     ){
-      assert( 
-        (pPg->flags&PGHDR_IN_JOURNAL) || (int)pPg->pgno>pPager->origDbSize );
+      assert( (pPg->flags&PGHDR_IN_JOURNAL) 
+                 || (int)pPg->pgno>pPager->origDbSize );
       if( MEMDB ){
         rc = sqlite3PcachePreserve(pPg, 1);
         if( rc!=SQLITE_OK ){
           return rc;
         }
         PAGERTRACE3("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno);
-        /* page_add_to_stmt_list(pPg); */
       }else{
         i64 offset = pPager->stmtNRec*(4+pPager->pageSize);
         char *pData2 = CODEC2(pPager, pData, pPg->pgno, 7);
@@ -3507,7 +3506,7 @@ void sqlite3PagerDontRollback(DbPage *pPg){
   assert( !MEMDB );    /* For a memdb, pPager->journalOpen is always 0 */
 
 #ifdef SQLITE_SECURE_DELETE
-  if( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize ){
+  if( (pPg->flags & PGHDR_IN_JOURNAL)!=0 || (int)pPg->pgno>pPager->origDbSize ){
     return;
   }
 #endif
diff --git a/src/pcache.c b/src/pcache.c
index b0127120b4..50616bf88b 100644
--- a/src/pcache.c
+++ b/src/pcache.c
@@ -11,30 +11,50 @@
 *************************************************************************
 ** This file implements that page cache.
 **
-** @(#) $Id: pcache.c,v 1.18 2008/08/27 16:38:57 danielk1977 Exp $
+** @(#) $Id: pcache.c,v 1.19 2008/08/28 02:26:07 drh Exp $
 */
 #include "sqliteInt.h"
 
 /*
 ** A complete page cache is an instance of this structure.
+**
+** A cache may only be deleted by its owner and while holding the
+** SQLITE_MUTEX_STATUS_LRU mutex.
 */
 struct PCache {
-  int szPage;                         /* Size of every page in this cache */
-  int szExtra;                        /* Size of extra space for each page */
-  int nHash;                          /* Number of slots in apHash[] */
-  int nPage;                          /* Total number of pages in apHash */
-  int nMax;                           /* Configured cache size */
-  int nMin;                           /* Configured minimum cache size */
-  PgHdr **apHash;                     /* Hash table for fast lookup by pgno */
-  int bPurgeable;                     /* True if pages are on backing store */
-  void (*xDestroy)(PgHdr*);           /* Called when refcnt goes 1->0 */
-  int (*xStress)(void*,PgHdr*);       /* Call to try make a page clean */
-  void *pStress;                      /* Argument to xStress */
-  PgHdr *pClean;                      /* List of clean pages in use */
+  /*********************************************************************
+  ** The first group of elements may be read or written at any time by
+  ** the cache owner without holding the mutex.  No thread other than the
+  ** cache owner is permitted to access these elements at any time.
+  */
   PgHdr *pDirty, *pDirtyTail;         /* List of dirty pages in LRU order */
   PgHdr *pSynced;                     /* Last synced page in dirty page list */
   int nRef;                           /* Number of pinned pages */
   int nPinned;                        /* Number of pinned and/or dirty pages */
+  int nMax;                           /* Configured cache size */
+  int nMin;                           /* Configured minimum cache size */
+  /**********************************************************************
+  ** The next group of elements are fixed when the cache is created and
+  ** may not be changed afterwards.  These elements can read at any time by
+  ** the cache owner or by any thread holding the the mutex.  Non-owner
+  ** threads must hold the mutex when reading these elements to prevent
+  ** the entire PCache object from being deleted during the read.
+  */
+  int szPage;                         /* Size of every page in this cache */
+  int szExtra;                        /* Size of extra space for each page */
+  int bPurgeable;                     /* True if pages are on backing store */
+  void (*xDestroy)(PgHdr*);           /* Called when refcnt goes 1->0 */
+  int (*xStress)(void*,PgHdr*);       /* Call to try make a page clean */
+  void *pStress;                      /* Argument to xStress */
+  /**********************************************************************
+  ** The final group of elements can only be accessed while holding the
+  ** mutex.  Both the cache owner and any other thread must hold the mutex
+  ** to read or write any of these elements.
+  */
+  int nPage;                          /* Total number of pages in apHash */
+  int nHash;                          /* Number of slots in apHash[] */
+  PgHdr **apHash;                     /* Hash table for fast lookup by pgno */
+  PgHdr *pClean;                      /* List of clean pages in use */
 };
 
 /*
@@ -47,20 +67,6 @@ struct PgFreeslot {
 
 /*
 ** Global data for the page cache.
-**
-** The maximum number of cached pages stored by the system is determined
-** by the pcache.mxPage and pcache.mxPagePurgeable variables. If
-** mxPage is non-zero, then the system tries to limit the number of
-** cached pages stored to mxPage. In this case mxPagePurgeable is not 
-** used.
-**
-** If mxPage is zero, then the system tries to limit the number of
-** pages held by purgable caches to mxPagePurgeable.
-**
-** The doubly-linked list that runs between pcache.pLruHead and 
-** pcache.pLruTail contains all clean purgable pages in the system 
-** with a zero reference count. pcache.pLruTail is the next page to
-** be recycled.
 */
 static struct PCacheGlobal {
   int isInit;                         /* True when initialized */
@@ -78,29 +84,46 @@ static struct PCacheGlobal {
 } pcache = {0};
 
 /*
-** All global variables used by this module (most of which are grouped 
+** All global variables used by this module (all of which are grouped 
 ** together in global structure "pcache" above) are protected by the static 
 ** SQLITE_MUTEX_STATIC_LRU mutex. A pointer to this mutex is stored in
 ** variable "pcache.mutex".
 **
-** Access to the contents of the individual PCache structures is not 
-** protected. It is the job of the caller to ensure that these structures
-** are accessed in a thread-safe manner.
+** Some elements of the PCache and PgHdr structures are protected by the 
+** SQLITE_MUTEX_STATUS_LRU mutex and other are not.  The protected
+** elements are grouped at the end of the structures and are clearly
+** marked.
+**
+** Use the following macros must surround all access (read or write)
+** of protected elements.  The mutex is not recursive and may not be
+** entered more than once.  The pcacheMutexHeld() macro should only be
+** used within an assert() to verify that the mutex is being held.
 */
+#define pcacheEnterMutex() sqlite3_mutex_enter(pcache.mutex)
+#define pcacheExitMutex()  sqlite3_mutex_leave(pcache.mutex)
+#define pcacheMutexHeld()  sqlite3_mutex_held(pcache.mutex)
 
-#define pcacheEnterGlobal() sqlite3_mutex_enter(pcache.mutex)
-#define pcacheExitGlobal()  sqlite3_mutex_leave(pcache.mutex)
+/*
+** Some of the assert() macros in this code are too expensive to run
+** even during normal debugging.  Use them only rarely on long-running
+** tests.  Enable the expensive asserts using the
+** -DSQLITE_ENABLE_EXPENSIVE_ASSERT=1 compile-time option.
+*/
+#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
+# define expensive_assert(X)  assert(X)
+#else
+# define expensive_assert(X)
+#endif
 
 /********************************** Linked List Management ********************/
 
-#ifndef NDEBUG
+#if !defined(NDEBUG) && defined(SQLITE_ENABLE_EXPENSIVE_ASSERT)
 /*
 ** This routine verifies that the number of entries in the hash table
 ** is pCache->nPage.  This routine is used within assert() statements
 ** only and is therefore disabled during production builds.
 */
 static int pcacheCheckHashCount(PCache *pCache){
-#if 0
   int i;
   int nPage = 0;
   for(i=0; i<pCache->nHash; i++){
@@ -110,19 +133,20 @@ static int pcacheCheckHashCount(PCache *pCache){
     }
   }
   assert( nPage==pCache->nPage );
-#endif
   return 1;
 }
+#endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */
 
+
+#if !defined(NDEBUG) && defined(SQLITE_ENABLE_EXPENSIVE_ASSERT)
 /*
 ** Based on the current value of PCache.nRef and the contents of the
 ** PCache.pDirty list, return the expected value of the PCache.nPinned
 ** counter. This is only used in debugging builds, as follows:
 **
-**   assert( pCache->nPinned==pcachePinnedCount(pCache) );
+**   expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
 */
 static int pcachePinnedCount(PCache *pCache){
-#if 0
   PgHdr *p;
   int nPinned = pCache->nRef;
   for(p=pCache->pDirty; p; p=p->pNext){
@@ -131,34 +155,35 @@ static int pcachePinnedCount(PCache *pCache){
     }
   }
   return nPinned;
-#endif
-  return pCache->nPinned;
 }
+#endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */
 
+
+#if !defined(NDEBUG) && defined(SQLITE_ENABLE_EXPENSIVE_ASSERT)
 /*
 ** Check that the pCache->pSynced variable is set correctly. If it
 ** is not, either fail an assert or return zero. Otherwise, return
 ** non-zero. This is only used in debugging builds, as follows:
 **
-**   assert( pcacheCheckSynced(pCache) );
+**   expensive_assert( pcacheCheckSynced(pCache) );
 */
 static int pcacheCheckSynced(PCache *pCache){
-#if 0
   PgHdr *p = pCache->pDirtyTail;
   for(p=pCache->pDirtyTail; p!=pCache->pSynced; p=p->pPrev){
     assert( p->nRef || (p->flags&PGHDR_NEED_SYNC) );
   }
   return (p==0 || p->nRef || (p->flags&PGHDR_NEED_SYNC)==0);
-#endif
   return 1;
 }
+#endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */
+
 
-#endif
 
 /*
 ** Remove a page from its hash table (PCache.apHash[]).
 */
 static void pcacheRemoveFromHash(PgHdr *pPage){
+  /* assert( pcacheMutexHeld() ); *** FIXME ****/
   if( pPage->pPrevHash ){
     pPage->pPrevHash->pNextHash = pPage->pNextHash;
   }else{
@@ -171,15 +196,18 @@ static void pcacheRemoveFromHash(PgHdr *pPage){
     pPage->pNextHash->pPrevHash = pPage->pPrevHash;
   }
   pPage->pCache->nPage--;
-  assert( pcacheCheckHashCount(pPage->pCache) );
+  expensive_assert( pcacheCheckHashCount(pPage->pCache) );
 }
 
 /*
 ** Insert a page into the hash table
+**
+** The mutex must be held by the caller.
 */
 static void pcacheAddToHash(PgHdr *pPage){
   PCache *pCache = pPage->pCache;
   u32 h = pPage->pgno % pCache->nHash;
+  /* assert( pcacheMutexHeld() ); *** FIXME *****/
   pPage->pNextHash = pCache->apHash[h];
   pPage->pPrevHash = 0;
   if( pCache->apHash[h] ){
@@ -187,7 +215,7 @@ static void pcacheAddToHash(PgHdr *pPage){
   }
   pCache->apHash[h] = pPage;
   pCache->nPage++;
-  assert( pcacheCheckHashCount(pCache) );
+  expensive_assert( pcacheCheckHashCount(pCache) );
 }
 
 /*
@@ -195,29 +223,29 @@ static void pcacheAddToHash(PgHdr *pPage){
 ** at least nHash buckets.
 */
 static int pcacheResizeHash(PCache *pCache, int nHash){
+  PgHdr *p;
+  PgHdr **pNew;
+  /* assert( pcacheMutexHeld() ); **** FIXME *****/
 #ifdef SQLITE_MALLOC_SOFT_LIMIT
   if( nHash*sizeof(PgHdr*)>SQLITE_MALLOC_SOFT_LIMIT ){
     nHash = SQLITE_MALLOC_SOFT_LIMIT/sizeof(PgHdr *);
   }
 #endif
-  if( nHash>pCache->nHash ){
-    PgHdr *p;
-    PgHdr **pNew = (PgHdr **)sqlite3_malloc(sizeof(PgHdr*)*nHash);
-    if( !pNew ){
-      return SQLITE_NOMEM;
-    }
-    memset(pNew, 0, sizeof(PgHdr *)*nHash);
-    sqlite3_free(pCache->apHash);
-    pCache->apHash = pNew;
-    pCache->nHash = nHash;
-    pCache->nPage = 0;
-   
-    for(p=pCache->pClean; p; p=p->pNext){
-      pcacheAddToHash(p);
-    }
-    for(p=pCache->pDirty; p; p=p->pNext){
-      pcacheAddToHash(p);
-    }
+  pNew = (PgHdr **)sqlite3_malloc(sizeof(PgHdr*)*nHash);
+  if( !pNew ){
+    return SQLITE_NOMEM;
+  }
+  memset(pNew, 0, sizeof(PgHdr *)*nHash);
+  sqlite3_free(pCache->apHash);
+  pCache->apHash = pNew;
+  pCache->nHash = nHash;
+  pCache->nPage = 0;
+ 
+  for(p=pCache->pClean; p; p=p->pNext){
+    pcacheAddToHash(p);
+  }
+  for(p=pCache->pDirty; p; p=p->pNext){
+    pcacheAddToHash(p);
   }
   return SQLITE_OK;
 }
@@ -229,6 +257,7 @@ static int pcacheResizeHash(PCache *pCache, int nHash){
 static void pcacheRemoveFromList(PgHdr **ppHead, PgHdr *pPage){
   int isDirtyList = (ppHead==&pPage->pCache->pDirty);
   assert( ppHead==&pPage->pCache->pClean || ppHead==&pPage->pCache->pDirty );
+  /* assert( pcacheMutexHeld() || ppHead!=&pPage->pCache->pClean ); *** FIXME */
 
   if( pPage->pPrev ){
     pPage->pPrev->pNext = pPage->pNext;
@@ -384,13 +413,11 @@ void *pcacheMalloc(int sz, PCache *pCache){
     ** 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. Because sqlite3PcacheLock() 
-    ** might block on the MEM2 mutex, it has to be called before re-entering
-    ** the global LRU mutex.
+    ** reclaim memory from this pager-cache.
     */
-    pcacheExitGlobal();
+    pcacheExitMutex();
     p = sqlite3Malloc(sz);
-    pcacheEnterGlobal();
+    pcacheEnterMutex();
 
     if( p ){
       sz = sqlite3MallocSize(p);
@@ -401,9 +428,9 @@ void *pcacheMalloc(int sz, PCache *pCache){
 }
 void *sqlite3PageMalloc(sz){
   void *p;
-  pcacheEnterGlobal();
+  pcacheEnterMutex();
   p = pcacheMalloc(sz, 0);
-  pcacheExitGlobal();
+  pcacheExitMutex();
   return p;
 }
 
@@ -426,9 +453,9 @@ void pcacheFree(void *p){
   }
 }
 void sqlite3PageFree(void *p){
-  pcacheEnterGlobal();
+  pcacheEnterMutex();
   pcacheFree(p);
-  pcacheExitGlobal();
+  pcacheExitMutex();
 }
 
 /*
@@ -519,14 +546,14 @@ static int pcacheRecycleOrAlloc(PCache *pCache, PgHdr **ppPage){
   assert( sqlite3_mutex_notheld(pcache.mutex) );
 
   *ppPage = 0;
-  pcacheEnterGlobal();
+  pcacheEnterMutex();
 
   /* 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.
   */
-  assert( pCache->nPinned==pcachePinnedCount(pCache) );
-  assert( pcacheCheckSynced(pCache) );
+  expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
+  expensive_assert( pcacheCheckSynced(pCache) );
   if( pCache->xStress
    && pCache->pDirty
    && pCache->nPinned>=(pcache.nMaxPage+pCache->nMin-pcache.nMinPage)
@@ -543,12 +570,12 @@ static int pcacheRecycleOrAlloc(PCache *pCache, PgHdr **ppPage){
     }
     if( pPg ){
       int rc;
-      pcacheExitGlobal();
+      pcacheExitMutex();
       rc = pCache->xStress(pCache->pStress, pPg);
       if( rc!=SQLITE_OK ){
         return rc;
       }
-      pcacheEnterGlobal();
+      pcacheEnterMutex();
     }
   }
 
@@ -558,7 +585,7 @@ static int pcacheRecycleOrAlloc(PCache *pCache, PgHdr **ppPage){
   }
 
   /* If a page has been recycled but it is the wrong size, free it. */
-  if( p && (p->pCache->szPage!=szPage || p->pCache->szExtra!=szExtra) ){
+  if( p && (p->pCache->szPage!=szPage || p->pCache->szPage!=szExtra) ){
     pcachePageFree(p);
     p = 0;
   }
@@ -567,7 +594,7 @@ static int pcacheRecycleOrAlloc(PCache *pCache, PgHdr **ppPage){
     p = pcachePageAlloc(pCache);
   }
 
-  pcacheExitGlobal();
+  pcacheExitMutex();
   *ppPage = p;
   return (p?SQLITE_OK:SQLITE_NOMEM);
 }
@@ -622,13 +649,13 @@ void sqlite3PcacheOpen(
   p->nMax = 100;
   p->nMin = 10;
 
-  pcacheEnterGlobal();
+  pcacheEnterMutex();
   if( bPurgeable ){
     pcache.nMaxPage += p->nMax;
     pcache.nMinPage += p->nMin;
   }
 
-  pcacheExitGlobal();
+  pcacheExitMutex();
 }
 
 /*
@@ -653,7 +680,7 @@ int sqlite3PcacheFetch(
   assert( pcache.isInit );
   assert( pCache!=0 );
   assert( pgno>0 );
-  assert( pCache->nPinned==pcachePinnedCount(pCache) );
+  expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
 
   /* Search the hash table for the requested page. Exit early if it is found. */
   if( pCache->apHash ){
@@ -662,9 +689,9 @@ int sqlite3PcacheFetch(
       if( pPage->pgno==pgno ){
         if( pPage->nRef==0 ){
           if( 0==(pPage->flags&PGHDR_DIRTY) ){
-            pcacheEnterGlobal();
+            pcacheEnterMutex();
             pcacheRemoveFromLruList(pPage);
-            pcacheExitGlobal();
+            pcacheExitMutex();
             pCache->nPinned++;
           }
           pCache->nRef++;
@@ -679,7 +706,7 @@ int sqlite3PcacheFetch(
   if( createFlag ){
     int rc = SQLITE_OK;
     if( pCache->nHash<=pCache->nPage ){
-      rc = pcacheResizeHash(pCache, pCache->nHash<256?256:pCache->nHash*2);
+      rc = pcacheResizeHash(pCache, pCache->nHash<256 ? 256 : pCache->nHash*2);
       if( rc!=SQLITE_OK ){
         return rc;
       }
@@ -704,7 +731,7 @@ int sqlite3PcacheFetch(
     *ppPage = 0;
   }
 
-  assert( pCache->nPinned==pcachePinnedCount(pCache) );
+  expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
   return SQLITE_OK;
 }
 
@@ -723,9 +750,9 @@ void sqlite3PcacheRelease(PgHdr *p){
     pCache->nRef--;
     if( (p->flags&PGHDR_DIRTY)==0 ){
       pCache->nPinned--;
-      pcacheEnterGlobal();
+      pcacheEnterMutex();
       pcacheAddToLruList(p);
-      pcacheExitGlobal();
+      pcacheExitMutex();
     }else{
       /* Move the page to the head of the caches dirty list. */
       pcacheRemoveFromList(&pCache->pDirty, p);
@@ -753,9 +780,9 @@ void sqlite3PcacheDrop(PgHdr *p){
   pCache->nPinned--;
   pcacheRemoveFromList(&pCache->pClean, p);
   pcacheRemoveFromHash(p);
-  pcacheEnterGlobal();
+  pcacheEnterMutex();
   pcachePageFree(p);
-  pcacheExitGlobal();
+  pcacheExitMutex();
 }
 
 /*
@@ -785,15 +812,15 @@ void sqlite3PcacheMakeClean(PgHdr *p){
   assert( p->flags & PGHDR_DIRTY );
   pCache = p->pCache;
   pcacheRemoveFromList(&pCache->pDirty, p);
-  pcacheEnterGlobal();
+  pcacheEnterMutex();
   pcacheAddToList(&pCache->pClean, p);
   p->flags &= ~PGHDR_DIRTY;
   if( p->nRef==0 ){
     pcacheAddToLruList(p);
     pCache->nPinned--;
   }
-  assert( pCache->nPinned==pcachePinnedCount(pCache) );
-  pcacheExitGlobal();
+  expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
+  pcacheExitMutex();
 }
 
 /*
@@ -801,7 +828,7 @@ void sqlite3PcacheMakeClean(PgHdr *p){
 */
 void sqlite3PcacheCleanAll(PCache *pCache){
   PgHdr *p;
-  pcacheEnterGlobal();
+  pcacheEnterMutex();
   while( (p = pCache->pDirty)!=0 ){
     assert( p->apSave[0]==0 && p->apSave[1]==0 );
     pcacheRemoveFromList(&pCache->pDirty, p);
@@ -813,8 +840,8 @@ void sqlite3PcacheCleanAll(PCache *pCache){
     }
   }
   sqlite3PcacheAssertFlags(pCache, 0, PGHDR_DIRTY);
-  assert( pCache->nPinned==pcachePinnedCount(pCache) );
-  pcacheExitGlobal();
+  expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
+  pcacheExitMutex();
 }
 
 /*
@@ -828,10 +855,10 @@ void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
   p->pgno = newPgno;
   if( newPgno==0 ){
     p->flags |= PGHDR_REUSE_UNLIKELY;
-    pcacheEnterGlobal();
+    pcacheEnterMutex();
     pcacheFree(p->apSave[0]);
     pcacheFree(p->apSave[1]);
-    pcacheExitGlobal();
+    pcacheExitMutex();
     p->apSave[0] = 0;
     p->apSave[1] = 0;
     sqlite3PcacheMakeClean(p);
@@ -869,7 +896,7 @@ void pcacheClear(PCache *pCache){
 void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
   PgHdr *p, *pNext;
   PgHdr *pDirty = pCache->pDirty;
-  pcacheEnterGlobal();
+  pcacheEnterMutex();
   for(p=pCache->pClean; p||pDirty; p=pNext){
     if( !p ){
       p = pDirty;
@@ -895,7 +922,7 @@ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
       }
     }
   }
-  pcacheExitGlobal();
+  pcacheExitMutex();
 }
 
 
@@ -903,7 +930,7 @@ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
 ** Close a cache.
 */
 void sqlite3PcacheClose(PCache *pCache){
-  pcacheEnterGlobal();
+  pcacheEnterMutex();
 
   /* Free all the pages used by this pager and remove them from the LRU list. */
   pcacheClear(pCache);
@@ -913,12 +940,14 @@ void sqlite3PcacheClose(PCache *pCache){
   }
   sqlite3_free(pCache->apHash);
 
-  pcacheExitGlobal();
+  pcacheExitMutex();
 }
 
 /*
-** Preserve the content of the page, if it has not been preserved
-** already.  If idJournal==0 then this is for the overall transaction.
+** Preserve the content of the page.  It is assumed that the content
+** has not been preserved already.
+**
+** If idJournal==0 then this is for the overall transaction.
 ** If idJournal==1 then this is for the statement journal.
 **
 ** This routine is used for in-memory databases only.
@@ -929,12 +958,11 @@ int sqlite3PcachePreserve(PgHdr *p, int idJournal){
   void *x;
   int sz;
   assert( p->pCache->bPurgeable==0 );
-  if( !p->apSave[idJournal] ){
-    sz = p->pCache->szPage;
-    p->apSave[idJournal] = x = sqlite3PageMalloc( sz );
-    if( x==0 ) return SQLITE_NOMEM;
-    memcpy(x, p->pData, sz);
-  }
+  assert( p->apSave[idJournal]==0 );
+  sz = p->pCache->szPage;
+  p->apSave[idJournal] = x = sqlite3PageMalloc( sz );
+  if( x==0 ) return SQLITE_NOMEM;
+  memcpy(x, p->pData, sz);
   return SQLITE_OK;
 }
 
@@ -943,14 +971,14 @@ int sqlite3PcachePreserve(PgHdr *p, int idJournal){
 */
 void sqlite3PcacheCommit(PCache *pCache, int idJournal){
   PgHdr *p;
-  pcacheEnterGlobal();     /* Mutex is required to call pcacheFree() */
+  pcacheEnterMutex();     /* Mutex is required to call pcacheFree() */
   for(p=pCache->pDirty; p; p=p->pNext){
     if( p->apSave[idJournal] ){
       pcacheFree(p->apSave[idJournal]);
       p->apSave[idJournal] = 0;
     }
   }
-  pcacheExitGlobal();
+  pcacheExitMutex();
 }
 
 /*
@@ -959,7 +987,7 @@ void sqlite3PcacheCommit(PCache *pCache, int idJournal){
 void sqlite3PcacheRollback(PCache *pCache, int idJournal){
   PgHdr *p;
   int sz;
-  pcacheEnterGlobal();     /* Mutex is required to call pcacheFree() */
+  pcacheEnterMutex();     /* Mutex is required to call pcacheFree() */
   sz = pCache->szPage;
   for(p=pCache->pDirty; p; p=p->pNext){
     if( p->apSave[idJournal] ){
@@ -968,7 +996,7 @@ void sqlite3PcacheRollback(PCache *pCache, int idJournal){
       p->apSave[idJournal] = 0;
     }
   }
-  pcacheExitGlobal();
+  pcacheExitMutex();
 }
 
 /* 
@@ -991,9 +1019,9 @@ void sqlite3PcacheAssertFlags(PCache *pCache, int trueMask, int falseMask){
 */
 int sqlite3PcacheClear(PCache *pCache){
   assert(pCache->nRef==0);
-  pcacheEnterGlobal();
+  pcacheEnterMutex();
   pcacheClear(pCache);
-  pcacheExitGlobal();
+  pcacheExitMutex();
   return SQLITE_OK;
 }
 
@@ -1125,7 +1153,7 @@ void sqlite3PcacheSetFlags(PCache *pCache, int andMask, int orMask){
   /* Obtain the global mutex before modifying any PgHdr.flags variables 
   ** or traversing the LRU list.
   */ 
-  pcacheEnterGlobal();
+  pcacheEnterMutex();
 
   for(p=pCache->pDirty; p; p=p->pNext){
     p->flags = (p->flags&andMask)|orMask;
@@ -1139,7 +1167,7 @@ void sqlite3PcacheSetFlags(PCache *pCache, int andMask, int orMask){
     assert( !pCache->pSynced || (pCache->pSynced->flags&PGHDR_NEED_SYNC)==0 );
   }
 
-  pcacheExitGlobal();
+  pcacheExitMutex();
 }
 
 /*
@@ -1157,10 +1185,10 @@ void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){
     mxPage = 10;
   }
   if( pCache->bPurgeable ){
-    pcacheEnterGlobal();
+    pcacheEnterMutex();
     pcache.nMaxPage -= pCache->nMax;
     pcache.nMaxPage += mxPage;
-    pcacheExitGlobal();
+    pcacheExitMutex();
   }
   pCache->nMax = mxPage;
 }
@@ -1179,12 +1207,12 @@ int sqlite3PcacheReleaseMemory(int nReq){
   int nFree = 0;
   if( pcache.pStart==0 ){
     PgHdr *p;
-    pcacheEnterGlobal();
+    pcacheEnterMutex();
     while( (nReq<0 || nFree<nReq) && (p=pcacheRecyclePage()) ){
       nFree += pcachePageSize(p);
       pcachePageFree(p);
     }
-    pcacheExitGlobal();
+    pcacheExitMutex();
   }
   return nFree;
 }
diff --git a/src/pcache.h b/src/pcache.h
index 8dcc3655aa..d3fd7a9331 100644
--- a/src/pcache.h
+++ b/src/pcache.h
@@ -12,7 +12,7 @@
 ** This header file defines the interface that the sqlite page cache
 ** subsystem. 
 **
-** @(#) $Id: pcache.h,v 1.7 2008/08/27 15:16:34 danielk1977 Exp $
+** @(#) $Id: pcache.h,v 1.8 2008/08/28 02:26:07 drh Exp $
 */
 
 #ifndef _PCACHE_H_
@@ -25,22 +25,30 @@ typedef struct PCache PCache;
 ** structure.
 */
 struct PgHdr {
-  u32 flags;                     /* PGHDR flags defined below */
   void *pData;                   /* Content of this page */
   void *pExtra;                  /* Extra content */
   PgHdr *pDirty;                 /* Transient list of dirty pages */
   Pgno pgno;                     /* Page number for this page */
-  Pager *pPager;
+  Pager *pPager;                 /* The pager this page is part of */
 #ifdef SQLITE_CHECK_PAGES
-  u32 pageHash;
+  u32 pageHash;                  /* Hash of page content */
 #endif
-  /*** Public data is above. All that follows is private to pcache.c ***/
+  u16 flags;                     /* PGHDR flags defined below */
+  /**********************************************************************
+  ** Elements above are public.  All that follows is private to pcache.c
+  ** and should not be accessed by other modules.
+  */
+  i16 nRef;                      /* Number of users of this page */
   PCache *pCache;                /* Cache that owns this page */
+  void *apSave[2];               /* Journal entries for in-memory databases */
+  /**********************************************************************
+  ** Elements above are accessible at any time by the owner of the cache
+  ** without the need for a mutex.  The elements that follow can only be
+  ** accessed while holding the SQLITE_MUTEX_STATIC_LRU mutex.
+  */
   PgHdr *pNextHash, *pPrevHash;  /* Hash collision chain for PgHdr.pgno */
   PgHdr *pNext, *pPrev;          /* List of clean or dirty pages */
   PgHdr *pNextLru, *pPrevLru;    /* Part of global LRU list */
-  int nRef;                      /* Number of users of this page */
-  void *apSave[2];               /* Journal entries for in-memory databases */
 };
 
 /* Bit values for PgHdr.flags */
diff --git a/src/test_func.c b/src/test_func.c
index 2812382816..20f3307185 100644
--- a/src/test_func.c
+++ b/src/test_func.c
@@ -12,7 +12,7 @@
 ** Code for testing all sorts of SQLite interfaces.  This code
 ** implements new SQL functions used by the test scripts.
 **
-** $Id: test_func.c,v 1.12 2008/08/27 15:21:35 drh Exp $
+** $Id: test_func.c,v 1.13 2008/08/28 02:26:07 drh Exp $
 */
 #include "sqlite3.h"
 #include "tcl.h"
@@ -217,10 +217,7 @@ static void counterFunc(
   int nArg,                /* Number of function arguments */
   sqlite3_value **argv     /* Values for all function arguments */
 ){
-  int i;
-  int *pCounter;
-
-  pCounter = (int*)sqlite3_get_auxdata(pCtx, 0);
+  int *pCounter = (int*)sqlite3_get_auxdata(pCtx, 0);
   if( pCounter==0 ){
     pCounter = sqlite3_malloc( sizeof(*pCounter) );
     if( pCounter==0 ){