/ Check-in [93dbc542]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:When recycling a page, try to find one that does not require a call to xSync() on the journal file. Also simplify some of the mutex related things in pcache. (CVS 5597)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:93dbc5427bebaa0b3d726731027caad3f70611c7
User & Date: danielk1977 2008-08-22 16:22:17
Context
2008-08-22
16:29
Enhanced test coverage. (CVS 5598) check-in: cc36b4e0 user: drh tags: trunk
16:22
When recycling a page, try to find one that does not require a call to xSync() on the journal file. Also simplify some of the mutex related things in pcache. (CVS 5597) check-in: 93dbc542 user: danielk1977 tags: trunk
14:41
Make sure the function context is fully initialized before invoking the function finalizer. Ticket #3326. (CVS 5596) check-in: 8496f4a0 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/pager.c.

    14     14   ** The pager is used to access a database disk file.  It implements
    15     15   ** atomic commit and rollback through the use of a journal file that
    16     16   ** is separate from the database file.  The pager also implements file
    17     17   ** locking to prevent two processes from writing the same database
    18     18   ** file simultaneously, or one process from reading the database while
    19     19   ** another is writing.
    20     20   **
    21         -** @(#) $Id: pager.c,v 1.474 2008/08/22 12:57:09 drh Exp $
           21  +** @(#) $Id: pager.c,v 1.475 2008/08/22 16:22:17 danielk1977 Exp $
    22     22   */
    23     23   #ifndef SQLITE_OMIT_DISKIO
    24     24   #include "sqliteInt.h"
    25     25   
    26     26   /*
    27     27   ** Macros for troubleshooting.  Normally turned off
    28     28   */
................................................................................
  1703   1703     vfsFlags |=  SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
  1704   1704               SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE;
  1705   1705     rc = sqlite3OsOpen(pPager->pVfs, 0, pFile, vfsFlags, 0);
  1706   1706     assert( rc!=SQLITE_OK || pFile->pMethods );
  1707   1707     return rc;
  1708   1708   }
  1709   1709   
  1710         -static int pagerStress(void *);
         1710  +static int pagerStress(void *,PgHdr *);
  1711   1711   
  1712   1712   /*
  1713   1713   ** Create a new page cache and put a pointer to the page cache in *ppPager.
  1714   1714   ** The file to be cached need not exist.  The file is not locked until
  1715   1715   ** the first call to sqlite3PagerGet() and is only held open until the
  1716   1716   ** last page is released using sqlite3PagerUnref().
  1717   1717   **
................................................................................
  2362   2362   #endif
  2363   2363   
  2364   2364     return rc;
  2365   2365   }
  2366   2366   
  2367   2367   /*
  2368   2368   ** Given a list of pages (connected by the PgHdr.pDirty pointer) write
  2369         -** every one of those pages out to the database file and mark them all
  2370         -** as clean.
         2369  +** every one of those pages out to the database file. No calls are made
         2370  +** to the page-cache to mark the pages as clean. It is the responsibility
         2371  +** of the caller to use PcacheCleanAll() or PcacheMakeClean() to mark
         2372  +** the pages as clean.
  2371   2373   */
  2372   2374   static int pager_write_pagelist(PgHdr *pList){
  2373   2375     Pager *pPager;
  2374   2376     int rc;
  2375   2377   
  2376   2378     if( pList==0 ) return SQLITE_OK;
  2377   2379     pPager = pList->pPager;
................................................................................
  2429   2431         PAGERTRACE3("NOSTORE %d page %d\n", PAGERID(pPager), pList->pgno);
  2430   2432       }
  2431   2433   #endif
  2432   2434       if( rc ) return rc;
  2433   2435   #ifdef SQLITE_CHECK_PAGES
  2434   2436       pList->pageHash = pager_pagehash(pList);
  2435   2437   #endif
  2436         -    makeClean(pList);
         2438  +    /* makeClean(pList); */
  2437   2439       pList = pList->pDirty;
  2438   2440     }
  2439   2441   
  2440   2442     /* sqlite3PcacheCleanAll(pPager->pPCache); */
  2441   2443     return SQLITE_OK;
  2442   2444   }
  2443   2445   
................................................................................
  2444   2446   /*
  2445   2447   ** This function is called by the pcache layer when it has reached some
  2446   2448   ** soft memory limit. The argument is a pointer to a purgeable Pager 
  2447   2449   ** object. This function attempts to make a single dirty page that has no
  2448   2450   ** outstanding references (if one exists) clean so that it can be recycled 
  2449   2451   ** by the pcache layer.
  2450   2452   */
  2451         -static int pagerStress(void *p){
         2453  +static int pagerStress(void *p, PgHdr *pPg){
  2452   2454     Pager *pPager = (Pager *)p;
  2453         -  PgHdr *pPg = sqlite3PcacheDirtyPage(pPager->pPCache);
  2454   2455     int rc = SQLITE_OK;
  2455   2456   
  2456         -  if( pPg ){
  2457         -    assert( pPg->flags&PGHDR_DIRTY );
  2458         -    if( pPager->errCode==SQLITE_OK ){
  2459         -      if( pPg->flags&PGHDR_NEED_SYNC ){
  2460         -        rc = syncJournal(pPager);
  2461         -        if( rc==SQLITE_OK && pPager->fullSync && 
  2462         -          !(sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
  2463         -        ){
  2464         -          pPager->nRec = 0;
  2465         -          rc = writeJournalHdr(pPager);
  2466         -        }
  2467         -      }
  2468         -      if( rc==SQLITE_OK ){
  2469         -        rc = pager_write_pagelist(pPg);
  2470         -      }
  2471         -      if( rc!=SQLITE_OK ){
  2472         -        pager_error(pPager, rc);
  2473         -      }
  2474         -    }else{
  2475         -      sqlite3PcacheMakeClean(pPg);
  2476         -    }
         2457  +  assert( pPg->flags&PGHDR_DIRTY );
         2458  +  if( pPager->errCode==SQLITE_OK ){
         2459  +    if( pPg->flags&PGHDR_NEED_SYNC ){
         2460  +      rc = syncJournal(pPager);
         2461  +      if( rc==SQLITE_OK && pPager->fullSync && 
         2462  +        !(sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
         2463  +      ){
         2464  +        pPager->nRec = 0;
         2465  +        rc = writeJournalHdr(pPager);
         2466  +      }
         2467  +    }
         2468  +    if( rc==SQLITE_OK ){
         2469  +      pPg->pDirty = 0;
         2470  +      rc = pager_write_pagelist(pPg);
         2471  +    }
         2472  +    if( rc!=SQLITE_OK ){
         2473  +      pager_error(pPager, rc);
         2474  +    }
         2475  +  }
         2476  +
         2477  +  if( rc==SQLITE_OK ){
         2478  +    sqlite3PcacheMakeClean(pPg);
  2477   2479     }
  2478   2480     return rc;
  2479   2481   }
  2480   2482   
  2481   2483   
  2482   2484   /*
  2483   2485   ** Return 1 if there is a hot journal on the given pager.

Changes to src/pcache.c.

     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This file implements that page cache.
    13     13   **
    14         -** @(#) $Id: pcache.c,v 1.7 2008/08/21 20:21:35 drh Exp $
           14  +** @(#) $Id: pcache.c,v 1.8 2008/08/22 16:22:17 danielk1977 Exp $
    15     15   */
    16     16   #include "sqliteInt.h"
    17     17   
    18     18   /*
    19     19   ** A complete page cache is an instance of this structure.
    20     20   */
    21     21   struct PCache {
................................................................................
    24     24     int szExtra;                        /* Size of extra space for each page */
    25     25     int nHash;                          /* Number of slots in apHash[] */
    26     26     int nPage;                          /* Total number of pages in apHash */
    27     27     int nMax;                           /* Configured cache size */
    28     28     PgHdr **apHash;                     /* Hash table for fast lookup by pgno */
    29     29     int bPurgeable;                     /* True if pages are on backing store */
    30     30     void (*xDestroy)(PgHdr*);           /* Called when refcnt goes 1->0 */
    31         -  int (*xStress)(void*);              /* Call to try to make pages clean */
           31  +  int (*xStress)(void*,PgHdr*);       /* Call to try make a page clean */
    32     32     void *pStress;                      /* Argument to xStress */
    33     33     PgHdr *pClean;                      /* List of clean pages in use */
    34     34     PgHdr *pDirty;                      /* List of dirty pages */
    35     35     int nRef;                           /* Number of outstanding page refs */
    36     36   
    37     37     int iInUseMM;
    38     38     int iInUseDB;
................................................................................
    53     53   ** by the pcache.mxPage and pcache.mxPagePurgeable variables. If
    54     54   ** mxPage is non-zero, then the system tries to limit the number of
    55     55   ** cached pages stored to mxPage. In this case mxPagePurgeable is not 
    56     56   ** used.
    57     57   **
    58     58   ** If mxPage is zero, then the system tries to limit the number of
    59     59   ** pages held by purgable caches to mxPagePurgeable.
           60  +**
           61  +** The doubly-linked list that runs between pcache.pLruHead and 
           62  +** pcache.pLruTail contains all pages in the system with a zero 
           63  +** reference count. The pcache.pLruSynced variable points to the last
           64  +** (closest to pcache.pLruTail) entry in this list that does not have
           65  +** the PGHDR_NEED_SYNC flag set. This is the page that the pcacheRecycle()
           66  +** function will try to recycle.
    60     67   */
    61     68   static struct PCacheGlobal {
    62     69     int isInit;                         /* True when initialized */
    63     70     sqlite3_mutex *mutex_mem2;          /* static mutex MUTEX_STATIC_MEM2 */
    64     71     sqlite3_mutex *mutex_lru;           /* static mutex MUTEX_STATIC_LRU */
    65     72     PCache *pAll;                       /* list of all page caches */
    66     73     int nPage;                          /* Number of pages */
    67     74     int nPurgeable;                     /* Number of pages in purgable caches */
    68     75     int mxPage;                         /* Globally configured page maximum */
    69     76     int mxPagePurgeable;                /* Purgeable page maximum */
    70     77     PgHdr *pLruHead, *pLruTail;         /* Global LRU list of unused pages */
           78  +  PgHdr *pLruSynced;                  /* Last synced entry in LRU list  */
           79  +
           80  +  /* Variables related to SQLITE_CONFIG_PAGECACHE settings. */
    71     81     int szSlot;                         /* Size of each free slot */
    72     82     void *pStart, *pEnd;                /* Bounds of pagecache malloc range */
    73     83     PgFreeslot *pFree;                  /* Free page blocks */
    74     84   } pcache = {0};
    75     85   
    76     86   /*
    77     87   ** All global variables used by this module (most of which are grouped 
    78     88   ** together in global structure "pcache" above) except the list of all
    79     89   ** pager-caches starting with pcache.pAll, are protected by the static 
    80     90   ** SQLITE_MUTEX_STATIC_LRU mutex. A pointer to this mutex is stored in
    81     91   ** variable "pcache.mutex_lru".
    82     92   **
    83         -** The list of all pager-caches (PCache structures) headed by pcache.pAll 
    84         -** is protected by SQLITE_MUTEX_STATIC_MEM2.
    85         -**
    86     93   ** Access to the contents of the individual PCache structures is not 
    87     94   ** protected. It is the job of the caller to ensure that these structures
    88     95   ** are accessed in a thread-safe manner. However, this module provides the
    89     96   ** functions sqlite3PcacheLock() and sqlite3PcacheUnlock() that may be used
    90     97   ** by the caller to increment/decrement a lock-count on an individual 
    91     98   ** pager-cache object. This module guarantees that the xStress() callback
    92     99   ** will not be invoked on a pager-cache with a non-zero lock-count except
    93    100   ** from within a call to sqlite3PcacheFetch() on the same pager. A call
    94    101   ** to sqlite3PcacheLock() may block if such an xStress() call is currently 
    95    102   ** underway.
    96    103   **
    97    104   ** Before the xStress callback of a pager-cache (PCache) is invoked, the
    98         -** SQLITE_MUTEX_STATIC_MEM2 mutex is obtained and the SQLITE_MUTEX_STATIC_LRU 
    99         -** mutex released (in that order) before making the call.
          105  +** SQLITE_MUTEX_STATIC_MEM2 mutex is obtained.
   100    106   **
   101    107   ** Deadlock within the module is avoided by never blocking on the MEM2 
   102    108   ** mutex while the LRU mutex is held.
   103    109   */
   104    110   
   105    111   #define pcacheEnterGlobal() sqlite3_mutex_enter(pcache.mutex_lru)
   106    112   #define pcacheExitGlobal()  sqlite3_mutex_leave(pcache.mutex_lru)
................................................................................
   245    251   
   246    252   /*
   247    253   ** Remove a page from the global LRU list
   248    254   */
   249    255   static void pcacheRemoveFromLruList(PgHdr *pPage){
   250    256     assert( sqlite3_mutex_held(pcache.mutex_lru) );
   251    257     if( pPage->pCache->bPurgeable==0 ) return;
          258  +  if( pPage==pcache.pLruSynced ){
          259  +    PgHdr *p;
          260  +    for(p=pPage->pPrevLru; p && (p->flags&PGHDR_NEED_SYNC); p=p->pPrevLru);
          261  +    pcache.pLruSynced = p;
          262  +  }
   252    263     if( pPage->pNextLru ){
   253    264       pPage->pNextLru->pPrevLru = pPage->pPrevLru;
   254    265     }else{
   255    266       assert( pcache.pLruTail==pPage );
   256    267       pcache.pLruTail = pPage->pPrevLru;
   257    268     }
   258    269     if( pPage->pPrevLru ){
................................................................................
   278    289       */
   279    290       assert( pcache.pLruHead );
   280    291       pPage->pNextLru = 0;
   281    292       pPage->pPrevLru = pcache.pLruTail;
   282    293       pcache.pLruTail->pNextLru = pPage;
   283    294       pcache.pLruTail = pPage;
   284    295       pPage->flags &= ~PGHDR_REUSE_UNLIKELY;
          296  +    if( 0==(pPage->flags&PGHDR_NEED_SYNC) ){
          297  +      pcache.pLruSynced = pPage;
          298  +    }
   285    299     }else{
   286    300       /* If reuse is possible. the page goes at the beginning of the LRU
   287    301       ** list so that it will be the last to be recycled.
   288    302       */
   289    303       if( pcache.pLruHead ){
   290    304         pcache.pLruHead->pPrevLru = pPage;
   291    305       }
   292    306       pPage->pNextLru = pcache.pLruHead;
   293    307       pcache.pLruHead = pPage;
   294    308       pPage->pPrevLru = 0;
   295    309       if( pcache.pLruTail==0 ){
   296    310         pcache.pLruTail = pPage;
          311  +    }
          312  +    if( pcache.pLruSynced==0 && 0==(pPage->flags&PGHDR_NEED_SYNC) ){
          313  +      pcache.pLruSynced = pPage;
   297    314       }
   298    315     }
   299    316   }
   300    317   
   301    318   /*********************************************** Memory Allocation ***********
   302    319   **
   303    320   ** Initialize the page cache memory pool.
................................................................................
   433    450     assert( p->apSave[0]==0 );
   434    451     assert( p->apSave[1]==0 );
   435    452     assert( p && p->pCache );
   436    453     return sqlite3MallocSize(p);
   437    454   }
   438    455   
   439    456   static PgHdr *pcacheRecycle(PCache *pCache){
   440         -  PCache *pCsr;
   441    457     PgHdr *p = 0;
   442    458   
   443    459     assert( pcache.isInit );
   444    460     assert( sqlite3_mutex_held(pcache.mutex_lru) );
   445    461   
   446         -  if( !pcache.pLruTail && SQLITE_OK==sqlite3_mutex_try(pcache.mutex_mem2) ){
   447         -
   448         -    /* Invoke xStress() callbacks until the LRU list contains at least one
   449         -    ** page that can be reused or until the xStress() callback of all
   450         -    ** caches has been invoked.
   451         -    */
   452         -    for(pCsr=pcache.pAll; pCsr&&!pcache.pLruTail; pCsr=pCsr->pNextAll){
   453         -      assert( pCsr->iInUseMM==0 );
   454         -      pCsr->iInUseMM = 1;
   455         -      if( pCsr->xStress && (pCsr->iInUseDB==0 || pCache==pCsr) ){
   456         -        pcacheExitGlobal();
   457         -        pCsr->xStress(pCsr->pStress);
   458         -        pcacheEnterGlobal();
          462  +  p = pcache.pLruSynced;
          463  +  if( !p ){
          464  +    p = pcache.pLruTail;
          465  +  }
          466  +  if( p && (p->flags&PGHDR_DIRTY) ){
          467  +    if( SQLITE_OK==sqlite3_mutex_try(pcache.mutex_mem2) ){
          468  +      int iInUseDB;
          469  +      PCache *pC = p->pCache;
          470  +
          471  +      if( pCache==pC ){
          472  +        /* If trying to recycle a page from pCache, then set the iInUseDb
          473  +        ** flag to zero. This is done so that the sqlite3PcacheSetFlags()
          474  +        ** can tell that the LRU mutex is already held.
          475  +        **
          476  +        ** It is quite safe to modify the iInUseDB variable, because we 
          477  +        ** know no other thread will attempt to use pCache (because this
          478  +        ** call is being made from within a call to sqlite3PcacheFetch()
          479  +        ** on pCache). 
          480  +        */
          481  +        assert( pCache->iInUseDB );
          482  +        iInUseDB = pCache->iInUseDB;
          483  +        pCache->iInUseDB = 0;
          484  +      }
          485  +
          486  +      assert( pC->iInUseMM==0 );
          487  +      pC->iInUseMM = 1;
          488  +      if( pC->xStress && pC->iInUseDB==0 ){
          489  +        pC->xStress(pC->pStress, p);
          490  +      }
          491  +      if( pCache==pC ){
          492  +        pCache->iInUseDB = iInUseDB;
   459    493         }
   460         -      pCsr->iInUseMM = 0;
          494  +      pC->iInUseMM = 0;
          495  +      sqlite3_mutex_leave(pcache.mutex_mem2);
   461    496       }
   462         -
   463         -    sqlite3_mutex_leave(pcache.mutex_mem2);
   464    497     }
   465         -
   466         -  p = pcache.pLruTail;
          498  +  if( p && (p->flags&PGHDR_DIRTY) ){
          499  +    p = 0;
          500  +  }
   467    501   
   468    502     if( p ){
   469    503       pcacheRemoveFromLruList(p);
   470    504       pcacheRemoveFromHash(p);
   471    505       pcacheRemoveFromList(&p->pCache->pClean, p);
   472    506   
   473    507       /* If the always-rollback flag is set on the page being recycled, set 
................................................................................
   554    588   ** has already been allocated and is passed in as the p pointer.
   555    589   */
   556    590   void sqlite3PcacheOpen(
   557    591     int szPage,                  /* Size of every page */
   558    592     int szExtra,                 /* Extra space associated with each page */
   559    593     int bPurgeable,              /* True if pages are on backing store */
   560    594     void (*xDestroy)(PgHdr*),    /* Called to destroy a page */
   561         -  int (*xStress)(void*),       /* Call to try to make pages clean */
          595  +  int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */
   562    596     void *pStress,               /* Argument to xStress */
   563    597     PCache *p                    /* Preallocated space for the PCache */
   564    598   ){
   565    599     assert( pcache.isInit );
   566    600     memset(p, 0, sizeof(PCache));
   567    601     p->szPage = szPage;
   568    602     p->szExtra = szExtra;
................................................................................
   575    609     if( bPurgeable ){
   576    610       pcacheEnterGlobal();
   577    611       pcache.mxPagePurgeable += p->nMax;
   578    612       pcacheExitGlobal();
   579    613     }
   580    614   
   581    615     /* Add the new pager-cache to the list of caches starting at pcache.pAll */
   582         -  assert( sqlite3_mutex_notheld(pcache.mutex_lru) );
   583         -  sqlite3_mutex_enter(pcache.mutex_mem2);
          616  +  pcacheEnterGlobal();
   584    617     p->pNextAll = pcache.pAll;
   585    618     if( pcache.pAll ){
   586    619       pcache.pAll->pPrevAll = p;
   587    620     }
   588    621     p->pPrevAll = 0;
   589    622     pcache.pAll = p;
   590         -  sqlite3_mutex_leave(pcache.mutex_mem2);
          623  +  pcacheExitGlobal();
   591    624   }
   592    625   
   593    626   /*
   594    627   ** Change the page size for PCache object.  This can only happen
   595    628   ** when the cache is empty.
   596    629   */
   597    630   void sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
................................................................................
   615    648     assert( pCache->iInUseDB || pCache->iInUseMM );
   616    649   
   617    650     /* Search the hash table for the requested page. Exit early if it is found. */
   618    651     if( pCache->apHash ){
   619    652       u32 h = pgno % pCache->nHash;
   620    653       for(pPage=pCache->apHash[h]; pPage; pPage=pPage->pNextHash){
   621    654         if( pPage->pgno==pgno ){
   622         -        if( pPage->nRef==0 && (pPage->flags & PGHDR_DIRTY)==0 ){
          655  +        if( pPage->nRef==0 /* && (pPage->flags & PGHDR_DIRTY)==0 */ ){
   623    656             pcacheEnterGlobal();
   624    657             pcacheRemoveFromLruList(pPage);
   625    658             pcacheExitGlobal();
   626    659           }
   627    660           pcacheRef(pPage, 1);
   628    661           *ppPage = pPage;
   629    662           return SQLITE_OK;
................................................................................
   669    702     assert( p->nRef>0 );
   670    703     assert( p->pCache->iInUseDB || p->pCache->iInUseMM );
   671    704     pcacheRef(p, -1);
   672    705     if( p->nRef!=0 ) return;
   673    706     if( p->pCache->xDestroy ){
   674    707       p->pCache->xDestroy(p);
   675    708     }
          709  +#if 0
   676    710     if( (p->flags & PGHDR_DIRTY)!=0 ) return;
          711  +#endif
   677    712     pcacheEnterGlobal();
   678    713     pcacheAddToLruList(p);
   679    714     pcacheExitGlobal();
   680    715   }
   681    716   
   682    717   void sqlite3PcacheRef(PgHdr *p){
   683    718     assert(p->nRef>=0);
................................................................................
   727    762   */
   728    763   void sqlite3PcacheMakeClean(PgHdr *p){
   729    764     PCache *pCache;
   730    765     assert( p->pCache->iInUseDB || p->pCache->iInUseMM );
   731    766     if( (p->flags & PGHDR_DIRTY)==0 ) return;
   732    767     assert( p->apSave[0]==0 && p->apSave[1]==0 );
   733    768     assert( p->flags & PGHDR_DIRTY );
   734         -  /* assert( p->nRef>0 ); */
          769  +  assert( p->nRef>0 || sqlite3_mutex_held(pcache.mutex_lru) );
   735    770     pCache = p->pCache;
   736    771     pcacheRemoveFromList(&pCache->pDirty, p);
   737    772     pcacheAddToList(&pCache->pClean, p);
   738    773     p->flags &= ~PGHDR_DIRTY;
   739         -  if( p->nRef==0 ){
   740         -    pcacheEnterGlobal();
   741         -    pcacheAddToLruList(p);
   742         -    pcacheExitGlobal();
   743         -  }
   744    774   }
   745    775   
   746    776   /*
   747    777   ** Make every page in the cache clean.
   748    778   */
   749    779   void sqlite3PcacheCleanAll(PCache *pCache){
   750    780     PgHdr *p;
   751    781     assert( pCache->iInUseDB );
          782  +  pcacheEnterGlobal();
   752    783     while( (p = pCache->pDirty)!=0 ){
   753    784       assert( p->apSave[0]==0 && p->apSave[1]==0 );
   754    785       pcacheRemoveFromList(&pCache->pDirty, p);
   755    786       pcacheAddToList(&pCache->pClean, p);
   756    787       p->flags &= ~PGHDR_DIRTY;
   757         -    if( p->nRef==0 ){
   758         -      pcacheEnterGlobal();
   759         -      pcacheAddToLruList(p);
   760         -      pcacheExitGlobal();
   761         -    }
   762    788     }
          789  +  sqlite3PcacheAssertFlags(pCache, 0, PGHDR_DIRTY);
          790  +  pcacheExitGlobal();
   763    791   }
   764    792   
   765    793   /*
   766    794   ** Change the page number of page p to newPgno. If newPgno is 0, then the
   767    795   ** page object is added to the clean-list and the PGHDR_REUSE_UNLIKELY 
   768    796   ** flag set.
   769    797   */
................................................................................
   802    830     for(p=pCache->pClean; p; p=pNext){
   803    831       pNext = p->pNext;
   804    832       pcacheRemoveFromLruList(p);
   805    833       pcachePageFree(p);
   806    834     }
   807    835     for(p=pCache->pDirty; p; p=pNext){
   808    836       pNext = p->pNext;
          837  +    pcacheRemoveFromLruList(p);
   809    838       pcachePageFree(p);
   810    839     }
   811    840     pCache->pClean = 0;
   812    841     pCache->pDirty = 0;
   813    842     pCache->nPage = 0;
   814    843     memset(pCache->apHash, 0, pCache->nHash*sizeof(pCache->apHash[0]));
   815    844   }
................................................................................
   831    860       pNext = p->pNext;
   832    861       if( p->pgno>pgno ){
   833    862         if( p->nRef==0 ){
   834    863           pcacheRemoveFromHash(p);
   835    864           if( p->flags&PGHDR_DIRTY ){
   836    865             pcacheRemoveFromList(&pCache->pDirty, p);
   837    866           }else{
   838         -          pcacheRemoveFromLruList(p);
   839    867             pcacheRemoveFromList(&pCache->pClean, p);
   840    868           }
          869  +        pcacheRemoveFromLruList(p);
   841    870           pcachePageFree(p);
   842    871         }else{
   843    872           /* If there are references to the page, it cannot be freed. In this
   844    873           ** case, zero the page content instead.
   845    874           */
   846    875           memset(p->pData, 0, pCache->szPage);
   847    876         }
................................................................................
   852    881   
   853    882   
   854    883   /*
   855    884   ** Close a cache.
   856    885   */
   857    886   void sqlite3PcacheClose(PCache *pCache){
   858    887     assert( pCache->iInUseDB==1 );
   859         -
   860         -  /* Free all the pages used by this pager and remove them from the LRU
   861         -  ** list. This requires the protection of the MUTEX_STATIC_LRU mutex.
   862         -  */
   863    888     pcacheEnterGlobal();
          889  +
          890  +  /* Free all the pages used by this pager and remove them from the LRU list. */
   864    891     pcacheClear(pCache);
   865    892     if( pCache->bPurgeable ){
   866    893       pcache.mxPagePurgeable -= pCache->nMax;
   867    894     }
   868    895     sqlite3_free(pCache->apHash);
   869         -  pcacheExitGlobal();
   870    896   
   871    897     /* Now remove the pager-cache structure itself from the list of
   872         -  ** all such structures headed by pcache.pAll. This required the
   873         -  ** MUTEX_STATIC_MEM2 mutex.
          898  +  ** all such structures headed by pcache.pAll. 
   874    899     */
   875         -  assert( sqlite3_mutex_notheld(pcache.mutex_lru) );
   876         -  sqlite3_mutex_enter(pcache.mutex_mem2);
   877    900     assert(pCache==pcache.pAll || pCache->pPrevAll);
   878    901     assert(pCache->pNextAll==0 || pCache->pNextAll->pPrevAll==pCache);
   879    902     assert(pCache->pPrevAll==0 || pCache->pPrevAll->pNextAll==pCache);
   880    903     if( pCache->pPrevAll ){
   881    904       pCache->pPrevAll->pNextAll = pCache->pNextAll;
   882    905     }else{
   883    906       pcache.pAll = pCache->pNextAll;
   884    907     }
   885    908     if( pCache->pNextAll ){
   886    909       pCache->pNextAll->pPrevAll = pCache->pPrevAll;
   887    910     }
   888         -  sqlite3_mutex_leave(pcache.mutex_mem2);
          911  +
          912  +  pcacheExitGlobal();
   889    913   }
   890    914   
   891    915   /*
   892    916   ** Preserve the content of the page, if it has not been preserved
   893    917   ** already.  If idJournal==0 then this is for the overall transaction.
   894    918   ** If idJournal==1 then this is for the statement journal.
   895    919   **
................................................................................
  1056   1080     assert( pCache->iInUseDB );
  1057   1081     for(p=pCache->pDirty; p; p=p->pNext){
  1058   1082       p->pDirty = p->pNext;
  1059   1083     }
  1060   1084     return pcacheSortDirtyList(pCache->pDirty);
  1061   1085   }
  1062   1086   
  1063         -/*
  1064         -** This function searches cache pCache for a dirty page for which the
  1065         -** reference count is zero. If such a page can be found, the PgHdr.pDirty
  1066         -** pointer is set to 0 and a pointer to the page is returned. If no
  1067         -** such page is found, 0 is returned.
  1068         -**
  1069         -** This is used by the pager module to implement the xStress callback.
  1070         -*/
  1071         -PgHdr *sqlite3PcacheDirtyPage(PCache *pCache){
  1072         -  PgHdr *p = 0;
  1073         -#if 1
  1074         -  PgHdr *pIter;
  1075         -  Pgno min_pgno;
  1076         -  assert( pCache->iInUseMM );
  1077         -  for(pIter=pCache->pDirty; pIter; pIter=pIter->pNext){
  1078         -    if( pIter->nRef==0 && (p==0 || pIter->pgno<min_pgno) ){
  1079         -      p = pIter;
  1080         -      min_pgno = pIter->pgno;
  1081         -    }
  1082         -  }
  1083         -#else
  1084         -  assert( pCache->iInUseMM );
  1085         -  for(p=pCache->pDirty; p && p->nRef; p=p->pNext);
  1086         -#endif
  1087         -  assert( pCache->iInUseMM );
  1088         -  if( p ){
  1089         -    p->pDirty = 0;
  1090         -  }
  1091         -  return p;
  1092         -}
  1093         -
  1094   1087   /* 
  1095   1088   ** Return the total number of outstanding page references.
  1096   1089   */
  1097   1090   int sqlite3PcacheRefCount(PCache *pCache){
  1098   1091     return pCache->nRef;
  1099   1092   }
  1100   1093   
................................................................................
  1126   1119   #endif
  1127   1120   
  1128   1121   /* 
  1129   1122   ** Set flags on all pages in the page cache 
  1130   1123   */
  1131   1124   void sqlite3PcacheSetFlags(PCache *pCache, int andMask, int orMask){
  1132   1125     PgHdr *p;
         1126  +
         1127  +  assert( (orMask&PGHDR_NEED_SYNC)==0 );
  1133   1128     assert( pCache->iInUseDB || pCache->iInUseMM );
         1129  +
         1130  +  /* If this call is being made from within a call to the xStress callback
         1131  +  ** of a pager-cache (i.e. from within pagerRecycle()), then the 
         1132  +  ** PCache.iInUseDB will be set to zero. In this case, the LRU mutex is
         1133  +  ** already held. Otherwise, obtain it before modifying any PgHdr.flags
         1134  +  ** variables or traversing the LRU list.
         1135  +  */ 
         1136  +  if( pCache->iInUseDB ){
         1137  +    pcacheEnterGlobal();
         1138  +  }
         1139  +  assert( sqlite3_mutex_held(pcache.mutex_lru) );
         1140  +
  1134   1141     for(p=pCache->pDirty; p; p=p->pNext){
  1135   1142       p->flags = (p->flags&andMask)|orMask;
  1136   1143     }
  1137   1144     for(p=pCache->pClean; p; p=p->pNext){
  1138   1145       p->flags = (p->flags&andMask)|orMask;
  1139   1146     }
         1147  +
         1148  +  if( 0==(andMask&PGHDR_NEED_SYNC) ){
         1149  +    for(p=pcache.pLruTail; p && (p->flags&PGHDR_NEED_SYNC); p=p->pPrevLru);
         1150  +    pcache.pLruSynced = p;
         1151  +  }
         1152  +
         1153  +  if( pCache->iInUseDB ){
         1154  +    pcacheExitGlobal();
         1155  +  }
  1140   1156   }
  1141   1157   
  1142   1158   /*
  1143   1159   ** Set the suggested cache-size value.
  1144   1160   */
  1145   1161   int sqlite3PcacheGetCachesize(PCache *pCache){
  1146   1162     return pCache->nMax;

Changes to src/pcache.h.

     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This header file defines the interface that the sqlite page cache
    13     13   ** subsystem. 
    14     14   **
    15         -** @(#) $Id: pcache.h,v 1.3 2008/08/21 20:21:35 drh Exp $
           15  +** @(#) $Id: pcache.h,v 1.4 2008/08/22 16:22:17 danielk1977 Exp $
    16     16   */
    17     17   
    18     18   #ifndef _PCACHE_H_
    19     19   
    20     20   typedef struct PgHdr PgHdr;
    21     21   typedef struct PCache PCache;
    22     22   
................................................................................
    66     66   void sqlite3PCacheFree(void*);
    67     67   
    68     68   /* Create a new pager cache.
    69     69   ** Under memory stress, invoke xStress to try to make pages clean.
    70     70   ** Only clean and unpinned pages can be reclaimed.
    71     71   */
    72     72   void sqlite3PcacheOpen(
    73         -  int szPage,                  /* Size of every page */
    74         -  int szExtra,                 /* Extra space associated with each page */
    75         -  int bPurgeable,              /* True if pages are on backing store */
    76         -  void (*xDestroy)(PgHdr *),   /* Called to destroy a page */
    77         -  int (*xStress)(void*),       /* Call to try to make pages clean */
    78         -  void *pStress,               /* Argument to xStress */
    79         -  PCache *pToInit              /* Preallocated space for the PCache */
           73  +  int szPage,                    /* Size of every page */
           74  +  int szExtra,                   /* Extra space associated with each page */
           75  +  int bPurgeable,                /* True if pages are on backing store */
           76  +  void (*xDestroy)(PgHdr *),     /* Called to destroy a page */
           77  +  int (*xStress)(void*, PgHdr*), /* Call to try to make pages clean */
           78  +  void *pStress,                 /* Argument to xStress */
           79  +  PCache *pToInit                /* Preallocated space for the PCache */
    80     80   );
    81     81   
    82     82   /* Modify the page-size after the cache has been created. */
    83     83   void sqlite3PcacheSetPageSize(PCache *, int);
    84     84   
    85     85   /* Return the size in bytes of a PCache object.  Used to preallocate
    86     86   ** storage space.
................................................................................
   114    114   int sqlite3PcachePreserve(PgHdr*, int);    /* Preserve current page content */
   115    115   void sqlite3PcacheCommit(PCache*, int);    /* Drop preserved copy */
   116    116   void sqlite3PcacheRollback(PCache*, int);  /* Rollback to preserved copy */
   117    117   
   118    118   /* Get a list of all dirty pages in the cache, sorted by page number */
   119    119   PgHdr *sqlite3PcacheDirtyList(PCache*);
   120    120   
   121         -/* Query the cache for a dirty page with a zero ref-count */
   122         -PgHdr *sqlite3PcacheDirtyPage(PCache *);
   123         -
   124    121   /* Reset and close the cache object */
   125    122   void sqlite3PcacheClose(PCache*);
   126    123   
   127    124   /* Set flags on all pages in the page cache */
   128    125   void sqlite3PcacheSetFlags(PCache*, int andMask, int orMask);
   129    126   
   130    127   /* Assert flags settings on all pages.  Debugging only */

Changes to test/mutex1.test.

     5      5   #
     6      6   #    May you do good and not evil.
     7      7   #    May you find forgiveness for yourself and forgive others.
     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   #
    12         -# $Id: mutex1.test,v 1.12 2008/08/21 12:19:44 danielk1977 Exp $
           12  +# $Id: mutex1.test,v 1.13 2008/08/22 16:22:17 danielk1977 Exp $
    13     13   
    14     14   set testdir [file dirname $argv0]
    15     15   source $testdir/tester.tcl
    16     16   
    17     17   if {[info exists tester_do_binarylog]} {
    18     18     finish_test
    19     19     return
................................................................................
    92     92   #   * Multi-threaded mode,
    93     93   #   * Single-threaded mode.
    94     94   #
    95     95   set enable_shared_cache [sqlite3_enable_shared_cache 1]
    96     96   ifcapable threadsafe {
    97     97     foreach {mode mutexes} {
    98     98       singlethread {}
    99         -    multithread  {fast static_lru static_master static_mem static_mem2 static_prng }
   100         -    serialized   {fast recursive static_lru static_master static_mem static_mem2 static_prng }
           99  +    multithread  {fast static_lru static_master static_mem static_prng }
          100  +    serialized   {fast recursive static_lru static_master static_mem static_prng }
   101    101     } {
   102    102   
   103    103       do_test mutex1.2.$mode.1 {
   104    104         catch {db close}
   105    105         sqlite3_shutdown
   106    106         sqlite3_config $mode
   107    107       } SQLITE_OK