SQLite4
Check-in [abe0420724]
Not logged in

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

Overview
Comment:Allow accessing a prefix of the database file using mmap and the remainder using read and write.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | prefix-mmap
Files: files | file ages | folders
SHA1:abe0420724ae2a7caf4a3b346d1ed6c12d55baab
User & Date: dan 2013-03-06 20:15:16
Context
2013-03-07
18:36
When recycling an lsm cursor, reset the flags field. check-in: 4d830d87e4 user: dan tags: prefix-mmap
2013-03-06
20:15
Allow accessing a prefix of the database file using mmap and the remainder using read and write. check-in: abe0420724 user: dan tags: prefix-mmap
2013-03-04
22:37
New autoconf/make apparatus. Usage:

./autogen.sh ./configure make check-in: 7bf5b6c8d0 user: owensmk tags: trunk

Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/lsm.h.

   223    223   **   This means that this option may cause the connection to perform a 
   224    224   **   checkpoint even if the current connection has itself written very little
   225    225   **   data into the database file.
   226    226   **
   227    227   **   The default value is 2048 (checkpoint every 2MB).
   228    228   **
   229    229   ** LSM_CONFIG_MMAP:
   230         -**   A read/write integer parameter. True to use mmap() to access the 
   231         -**   database file. False otherwise.
          230  +**   A read/write integer parameter. If this value is set to 0, then the 
          231  +**   database file is accessed using ordinary read/write IO functions. Or,
          232  +**   if it is set to 1, then the database file is memory mapped and accessed
          233  +**   that way. If this parameter is set to any value N greater than 1, then
          234  +**   up to the first N KB of the file are memory mapped, and any remainder
          235  +**   accessed using read/write IO.
          236  +**
          237  +**   The default value is 1 on 64-bit platforms and 32768 on 32-bit platforms.
          238  +**   
   232    239   **
   233    240   ** LSM_CONFIG_USE_LOG:
   234    241   **   A read/write boolean parameter. True (the default) to use the log
   235    242   **   file normally. False otherwise.
   236    243   **
   237    244   ** LSM_CONFIG_AUTOMERGE:
   238    245   **   A read/write integer parameter. The minimum number of segments to

Changes to src/lsmInt.h.

    46     46   #define LSM_DFLT_BLOCK_SIZE         (1 * 1024 * 1024)
    47     47   #define LSM_DFLT_AUTOFLUSH          (1 * 1024 * 1024)
    48     48   #define LSM_DFLT_AUTOCHECKPOINT     (i64)(2 * 1024 * 1024)
    49     49   #define LSM_DFLT_AUTOWORK           1
    50     50   #define LSM_DFLT_LOG_SIZE           (128*1024)
    51     51   #define LSM_DFLT_AUTOMERGE          4
    52     52   #define LSM_DFLT_SAFETY             LSM_SAFETY_NORMAL
    53         -#define LSM_DFLT_MMAP               LSM_IS_64_BIT
           53  +#define LSM_DFLT_MMAP               (LSM_IS_64_BIT ? 1 : 32768)
    54     54   #define LSM_DFLT_MULTIPLE_PROCESSES 1
    55     55   #define LSM_DFLT_USE_LOG            1
    56     56   
    57     57   /* Initial values for log file checksums. These are only used if the 
    58     58   ** database file does not contain a valid checkpoint.  */
    59     59   #define LSM_CKSUM0_INIT 42
    60     60   #define LSM_CKSUM1_INIT 42
................................................................................
   331    331     int bAutowork;                  /* Configured by LSM_CONFIG_AUTOWORK */
   332    332     int nTreeLimit;                 /* Configured by LSM_CONFIG_AUTOFLUSH */
   333    333     int nMerge;                     /* Configured by LSM_CONFIG_AUTOMERGE */
   334    334     int bUseLog;                    /* Configured by LSM_CONFIG_USE_LOG */
   335    335     int nDfltPgsz;                  /* Configured by LSM_CONFIG_PAGE_SIZE */
   336    336     int nDfltBlksz;                 /* Configured by LSM_CONFIG_BLOCK_SIZE */
   337    337     int nMaxFreelist;               /* Configured by LSM_CONFIG_MAX_FREELIST */
   338         -  int bMmap;                      /* Configured by LSM_CONFIG_MMAP */
          338  +  int iMmap;                      /* Configured by LSM_CONFIG_MMAP */
   339    339     i64 nAutockpt;                  /* Configured by LSM_CONFIG_AUTOCHECKPOINT */
   340    340     int bMultiProc;                 /* Configured by L_C_MULTIPLE_PROCESSES */
   341    341     int bReadonly;                  /* Configured by LSM_CONFIG_READONLY */
   342    342     lsm_compress compress;          /* Compression callbacks */
   343    343     lsm_compress_factory factory;   /* Compression callback factory */
   344    344   
   345    345     /* Sub-system handles */

Changes to src/lsm_file.c.

   153    153   #include <sys/stat.h>
   154    154   #include <fcntl.h>
   155    155   
   156    156   /*
   157    157   ** File-system object. Each database connection allocates a single instance
   158    158   ** of the following structure. It is used for all access to the database and
   159    159   ** log files.
          160  +**
          161  +** The database file may be accessed via two methods - using mmap() or using
          162  +** read() and write() calls. In the general case both methods are used - a
          163  +** prefix of the file is mapped into memory and the remainder accessed using
          164  +** read() and write(). This is helpful when accessing very large files (or
          165  +** files that may grow very large during the lifetime of a database
          166  +** connection) on systems with 32-bit address spaces. However, it also requires
          167  +** that this object manage two distinct types of Page objects simultaneously -
          168  +** those that carry pointers to the mapped file and those that carry arrays
          169  +** populated by read() calls.
          170  +**
          171  +** pFree:
          172  +**   The head of a singly-linked list that containing currently unused Page 
          173  +**   structures suitable for use as mmap-page handles. Connected by the
          174  +**   Page.pFreeNext pointers.
          175  +**
          176  +** pMapped:
          177  +**   The head of a singly-linked list that contains all pages that currently
          178  +**   carry pointers to the mapped region. This is used if the region is
          179  +**   every remapped - the pointers carried by existing pages can be adjusted
          180  +**   to account for the remapping. Connected by the Page.pMappedNext pointers.
          181  +**
          182  +** pWaiting:
          183  +**   When the upper layer wishes to append a new b-tree page to a segment,
          184  +**   it allocates a Page object that carries a malloc'd block of memory -
          185  +**   regardless of the mmap-related configuration. The page is not assigned
          186  +**   a page number at first. When the upper layer has finished constructing
          187  +**   the page contents, it calls lsmFsPagePersist() to assign a page number
          188  +**   to it. At this point it is likely that N pages have been written to the
          189  +**   segment, the (N+1)th page is still outstanding and the b-tree page is
          190  +**   assigned page number (N+2). To avoid writing page (N+2) before page 
          191  +**   (N+1), the recently completed b-tree page is held in the singly linked
          192  +**   list headed by pWaiting until page (N+1) has been written. 
          193  +**
          194  +**   Function lsmFsFlushWaiting() is responsible for eventually writing 
          195  +**   waiting pages to disk.
          196  +**
          197  +**
          198  +** apHash/nHash:
          199  +**   Hash table used to store all Page objects that carry malloc'd arrays,
          200  +**   except those b-tree pages that have not yet been assigned page numbers.
          201  +**   Once they have been assigned page numbers - they are added to this
          202  +**   hash table.
          203  +**
          204  +**   Hash table overflow chains are connected using the Page.pHashNext
          205  +**   pointers.
   160    206   **
   161    207   ** pLruFirst, pLruLast:
   162         -**   The first and last entries in a doubly-linked list of pages. The 
   163         -**   Page.pLruNext and Page.pLruPrev pointers are used to link the list
   164         -**   elements together.
   165         -**
   166         -**   In mmap() mode, this list contains all currently allocated pages that
   167         -**   are carrying pointers into the database file mapping (pMap/nMap). If the
   168         -**   file has to be unmapped and then remapped (required to grow the mapping
   169         -**   as the file grows), the Page.aData pointers are updated by iterating
   170         -**   through the contents of this list.
   171         -**
   172         -**   In non-mmap() mode, this list is an LRU list of cached pages with 
   173         -**   nRef==0.
          208  +**   The first and last entries in a doubly-linked list of pages. This
          209  +**   list contains all pages with malloc'd data that are present in the
          210  +**   hash table and have a ref-count of zero.
   174    211   */
   175    212   struct FileSystem {
   176    213     lsm_db *pDb;                    /* Database handle that owns this object */
   177    214     lsm_env *pEnv;                  /* Environment pointer */
   178    215     char *zDb;                      /* Database file name */
   179    216     char *zLog;                     /* Database file name */
   180    217     int nMetasize;                  /* Size of meta pages in bytes */
................................................................................
   188    225     int szSector;                   /* Database file sector size */
   189    226   
   190    227     /* If this is a compressed database, a pointer to the compression methods.
   191    228     ** For an uncompressed database, a NULL pointer.  */
   192    229     lsm_compress *pCompress;
   193    230     u8 *aIBuffer;                   /* Buffer to compress to */
   194    231     u8 *aOBuffer;                   /* Buffer to uncompress from */
   195         -  int nBuffer;                    /* Allocated size of aBuffer[] in bytes */
          232  +  int nBuffer;                    /* Allocated size of above buffers in bytes */
   196    233   
   197         -  /* mmap() mode things */
   198         -  int bUseMmap;                   /* True to use mmap() to access db file */
          234  +  /* mmap() page related things */
          235  +  i64 nMapLimit;                  /* Maximum bytes of file to map */
   199    236     void *pMap;                     /* Current mapping of database file */
   200    237     i64 nMap;                       /* Bytes mapped at pMap */
   201         -  Page *pFree;
          238  +  Page *pFree;                    /* Unused Page structures */
          239  +  Page *pMapped;                  /* List of Page structs that point to pMap */
   202    240   
   203         -  Page *pWaiting;                 /* b-tree pages waiting to be written */
   204         -
   205         -  /* Statistics */
   206         -  int nWrite;                     /* Total number of pages written */
   207         -  int nRead;                      /* Total number of pages read */
   208         -
   209         -  /* Page cache parameters for non-mmap() mode */
   210         -  int nOut;                       /* Number of outstanding pages */
          241  +  /* Page cache parameters for non-mmap() pages */
   211    242     int nCacheMax;                  /* Configured cache size (in pages) */
   212    243     int nCacheAlloc;                /* Current cache size (in pages) */
   213    244     Page *pLruFirst;                /* Head of the LRU list */
   214    245     Page *pLruLast;                 /* Tail of the LRU list */
   215    246     int nHash;                      /* Number of hash slots in hash table */
   216    247     Page **apHash;                  /* nHash Hash slots */
          248  +
          249  +  /* Both types of pages */
          250  +  Page *pWaiting;                 /* b-tree pages waiting to be written */
          251  +
          252  +  /* Statistics */
          253  +  int nOut;                       /* Number of outstanding pages */
          254  +  int nWrite;                     /* Total number of pages written */
          255  +  int nRead;                      /* Total number of pages read */
   217    256   };
   218    257   
   219    258   /*
   220    259   ** Database page handle.
   221    260   **
   222    261   ** pSeg:
   223    262   **   When lsmFsSortedAppend() is called on a compressed database, the new
................................................................................
   241    280     FileSystem *pFS;                /* File system that owns this page */
   242    281   
   243    282     /* Only used in compressed database mode: */
   244    283     int nCompress;                  /* Compressed size (or 0 for uncomp. db) */
   245    284     int nCompressPrev;              /* Compressed size of prev page */
   246    285     Segment *pSeg;                  /* Segment this page will be written to */
   247    286   
   248         -  /* Fix this up somehow */
   249         -  Page *pNextWaiting;
          287  +  /* Pointers for singly linked lists */
          288  +  Page *pWaitingNext;             /* Next page in FileSystem.pWaiting list */
          289  +  Page *pFreeNext;                /* Next page in FileSystem.pFree list */
          290  +  Page *pMappedNext;              /* Next page in FileSystem.pMapped list */
   250    291   };
   251    292   
   252    293   /*
   253    294   ** Meta-data page handle. There are two meta-data pages at the start of
   254    295   ** the database file, each FileSystem.nMetasize bytes in size.
   255    296   */
   256    297   struct MetaPage {
................................................................................
   285    326   static int IOERR_WRAPPER(int rc){
   286    327     if( rc!=LSM_OK ) lsmIoerrBkpt();
   287    328     return rc;
   288    329   }
   289    330   #else
   290    331   # define IOERR_WRAPPER(rc) (rc)
   291    332   #endif
          333  +
          334  +#ifdef NDEBUG
          335  +# define assert_lists_are_ok(x)
          336  +#else
          337  +static Page *fsPageFindInHash(FileSystem *pFS, Pgno iPg, int *piHash);
          338  +
          339  +static void assert_lists_are_ok(FileSystem *pFS){
          340  +  Page *p;
          341  +
          342  +  assert( pFS->nMapLimit>=0 );
          343  +
          344  +  /* Check that all pages in the LRU list have nRef==0, pointers to buffers
          345  +  ** in heap memory, and corresponding entries in the hash table.  */
          346  +  for(p=pFS->pLruFirst; p; p=p->pLruNext){
          347  +    assert( p==pFS->pLruFirst || p->pLruPrev!=0 );
          348  +    assert( p==pFS->pLruLast || p->pLruNext!=0 );
          349  +    assert( p->pLruPrev==0 || p->pLruPrev->pLruNext==p );
          350  +    assert( p->pLruNext==0 || p->pLruNext->pLruPrev==p );
          351  +    assert( p->nRef==0 );
          352  +    assert( p->flags & PAGE_FREE );
          353  +    assert( p==fsPageFindInHash(pFS, p->iPg, 0) );
          354  +  }
          355  +}
          356  +#endif
   292    357   
   293    358   /*
   294    359   ** Wrappers around the VFS methods of the lsm_env object:
   295    360   **
   296    361   **     lsmEnvOpen()
   297    362   **     lsmEnvRead()
   298    363   **     lsmEnvWrite()
................................................................................
   450    515     zDel = lsmMallocPrintf(pFS->pEnv, "%s-log", pFS->zDb);
   451    516     if( zDel ){
   452    517       lsmEnvUnlink(pFS->pEnv, zDel);
   453    518       lsmFree(pFS->pEnv, zDel);
   454    519     }
   455    520     return LSM_OK;
   456    521   }
          522  +
          523  +/*
          524  +** Return true if page iReal of the database should be accessed using mmap.
          525  +** False otherwise.
          526  +*/
          527  +static int fsMmapPage(FileSystem *pFS, Pgno iReal){
          528  +  return ((i64)iReal*pFS->nPagesize <= pFS->nMapLimit);
          529  +}
   457    530   
   458    531   /*
   459    532   ** Given that there are currently nHash slots in the hash table, return 
   460    533   ** the hash key for file iFile, page iPg.
   461    534   */
   462    535   static int fsHashKey(int nHash, int iPg){
   463    536     return (iPg % nHash);
................................................................................
   599    672     FileSystem *pFS = db->pFS;
   600    673     if( pFS ){
   601    674       lsm_env *pEnv = pFS->pEnv;
   602    675       Page *pPg;
   603    676   
   604    677       assert( pFS->nOut==0 );
   605    678       assert( pFS->pWaiting==0 );
          679  +    assert( pFS->pMapped==0 );
   606    680   
   607    681       /* Reset any compression/decompression buffers already allocated */
   608    682       lsmFree(pEnv, pFS->aIBuffer);
   609    683       lsmFree(pEnv, pFS->aOBuffer);
   610    684       pFS->nBuffer = 0;
   611    685   
   612    686       /* Unmap the file, if it is currently mapped */
   613    687       if( pFS->pMap ){
   614    688         lsmEnvRemap(pEnv, pFS->fdDb, -1, &pFS->pMap, &pFS->nMap);
   615         -      pFS->bUseMmap = 0;
          689  +      pFS->nMapLimit = 0;
   616    690       }
   617    691   
   618         -    /* Free all allocate page structures */
          692  +    /* Free all allocated page structures */
   619    693       pPg = pFS->pLruFirst;
   620    694       while( pPg ){
   621    695         Page *pNext = pPg->pLruNext;
   622         -      if( pPg->flags & PAGE_FREE ) lsmFree(pEnv, pPg->aData);
          696  +      assert( pPg->flags & PAGE_FREE );
          697  +      lsmFree(pEnv, pPg->aData);
          698  +      lsmFree(pEnv, pPg);
          699  +      pPg = pNext;
          700  +    }
          701  +
          702  +    pPg = pFS->pFree;
          703  +    while( pPg ){
          704  +      Page *pNext = pPg->pFreeNext;
   623    705         lsmFree(pEnv, pPg);
   624    706         pPg = pNext;
   625    707       }
   626    708   
   627    709       /* Zero pointers that point to deleted page objects */
   628    710       pFS->nCacheAlloc = 0;
   629    711       pFS->pLruFirst = 0;
   630    712       pFS->pLruLast = 0;
   631    713       pFS->pFree = 0;
          714  +    if( pFS->apHash ){
          715  +      memset(pFS->apHash, 0, pFS->nHash*sizeof(pFS->apHash[0]));
          716  +    }
   632    717   
   633    718       /* Configure the FileSystem object */
   634    719       if( db->compress.xCompress ){
   635    720         pFS->pCompress = &db->compress;
   636         -      pFS->bUseMmap = 0;
          721  +      pFS->nMapLimit = 0;
   637    722       }else{
   638    723         pFS->pCompress = 0;
   639         -      pFS->bUseMmap = db->bMmap;
          724  +      if( db->iMmap==1 ){
          725  +        /* Unlimited */
          726  +        pFS->nMapLimit = (i64)1 << 60;
          727  +      }else{
          728  +        pFS->nMapLimit = (i64)db->iMmap * 1024;
          729  +      }
   640    730       }
   641    731     }
   642    732   
   643    733     return LSM_OK;
   644    734   }
   645    735   
   646    736   /*
................................................................................
   882    972   static void fsPageRemoveFromHash(FileSystem *pFS, Page *pPg){
   883    973     int iHash;
   884    974     Page **pp;
   885    975   
   886    976     iHash = fsHashKey(pFS->nHash, pPg->iPg);
   887    977     for(pp=&pFS->apHash[iHash]; *pp!=pPg; pp=&(*pp)->pHashNext);
   888    978     *pp = pPg->pHashNext;
          979  +  pPg->pHashNext = 0;
   889    980   }
   890    981   
   891    982   
   892    983   /*
   893         -** Purge the page cache of all entries with nRef==0.
          984  +** Purge the cache of all non-mmap pages with nRef==0.
   894    985   */
   895    986   void lsmFsPurgeCache(FileSystem *pFS){
   896         -  if( pFS->bUseMmap==0 ){
   897         -    Page *pPg;
          987  +  Page *pPg;
   898    988   
   899         -    pPg = pFS->pLruFirst;
   900         -    while( pPg ){
   901         -      Page *pNext = pPg->pLruNext;
   902         -      fsPageRemoveFromHash(pFS, pPg);
   903         -      if( pPg->flags & PAGE_FREE ){
   904         -        lsmFree(pFS->pEnv, pPg->aData);
   905         -      }
   906         -      lsmFree(pFS->pEnv, pPg);
   907         -      pPg = pNext;
   908         -      pFS->nCacheAlloc--;
   909         -    }
   910         -    pFS->pLruFirst = 0;
   911         -    pFS->pLruLast = 0;
          989  +  pPg = pFS->pLruFirst;
          990  +  while( pPg ){
          991  +    Page *pNext = pPg->pLruNext;
          992  +    assert( pPg->flags & PAGE_FREE );
          993  +    fsPageRemoveFromHash(pFS, pPg);
          994  +    lsmFree(pFS->pEnv, pPg->aData);
          995  +    lsmFree(pFS->pEnv, pPg);
          996  +    pPg = pNext;
          997  +    pFS->nCacheAlloc--;
          998  +  }
          999  +  pFS->pLruFirst = 0;
         1000  +  pFS->pLruLast = 0;
   912   1001   
   913         -    assert( pFS->nCacheAlloc<=pFS->nOut && pFS->nCacheAlloc>=0 );
   914         -  }
         1002  +  assert( pFS->nCacheAlloc<=pFS->nOut && pFS->nCacheAlloc>=0 );
   915   1003   }
   916   1004   
   917   1005   /*
   918   1006   ** Search the hash-table for page iPg. If an entry is round, return a pointer
   919   1007   ** to it. Otherwise, return NULL.
   920   1008   **
   921   1009   ** Either way, if argument piHash is not NULL set *piHash to the hash slot
................................................................................
   928   1016     if( piHash ) *piHash = iHash;
   929   1017     for(p=pFS->apHash[iHash]; p; p=p->pHashNext){
   930   1018       if( p->iPg==iPg) break;
   931   1019     }
   932   1020     return p;
   933   1021   }
   934   1022   
         1023  +/*
         1024  +** Allocate and return a non-mmap Page object. If there are already 
         1025  +** nCacheMax such Page objects outstanding, try to recycle an existing 
         1026  +** Page instead.
         1027  +*/
   935   1028   static int fsPageBuffer(
   936   1029     FileSystem *pFS, 
   937   1030     Page **ppOut
   938   1031   ){
   939   1032     int rc = LSM_OK;
   940   1033     Page *pPage = 0;
   941         -  if( pFS->bUseMmap || pFS->pLruFirst==0 || pFS->nCacheAlloc<pFS->nCacheMax ){
         1034  +  if( pFS->pLruFirst==0 || pFS->nCacheAlloc<pFS->nCacheMax ){
   942   1035       pPage = lsmMallocZero(pFS->pEnv, sizeof(Page));
   943   1036       if( !pPage ){
   944   1037         rc = LSM_NOMEM_BKPT;
   945   1038       }else{
   946   1039         pPage->aData = (u8 *)lsmMalloc(pFS->pEnv, pFS->nPagesize);
   947         -      pPage->flags = PAGE_FREE;
   948   1040         if( !pPage->aData ){
   949   1041           lsmFree(pFS->pEnv, pPage);
   950   1042           rc = LSM_NOMEM_BKPT;
   951   1043           pPage = 0;
   952   1044         }
   953   1045         pFS->nCacheAlloc++;
   954   1046       }
................................................................................
   956   1048       u8 *aData;
   957   1049       pPage = pFS->pLruFirst;
   958   1050       aData = pPage->aData;
   959   1051       fsPageRemoveFromLru(pFS, pPage);
   960   1052       fsPageRemoveFromHash(pFS, pPage);
   961   1053   
   962   1054       memset(pPage, 0, sizeof(Page));
   963         -    pPage->flags = PAGE_FREE;
   964   1055       pPage->aData = aData;
   965   1056     }
   966   1057   
         1058  +  if( pPage ){
         1059  +    pPage->flags = PAGE_FREE;
         1060  +  }
   967   1061     *ppOut = pPage;
   968   1062     return rc;
   969   1063   }
   970   1064   
   971   1065   static void fsPageBufferFree(Page *pPg){
   972         -  if( pPg->flags & PAGE_FREE ){
   973         -    lsmFree(pPg->pFS->pEnv, pPg->aData);
   974         -  }
   975         -  else if( pPg->pFS->bUseMmap ){
   976         -    fsPageRemoveFromLru(pPg->pFS, pPg);
   977         -  }
         1066  +  assert( pPg->pLruNext==0 );
         1067  +  assert( pPg->pLruPrev==0 );
         1068  +  assert( pPg->pFS->pLruFirst!=pPg );
         1069  +  assert( pPg->pFS->pLruLast!=pPg );
         1070  +
         1071  +  lsmFree(pPg->pFS->pEnv, pPg->aData);
   978   1072     lsmFree(pPg->pFS->pEnv, pPg);
   979   1073   }
   980   1074   
   981   1075   static void fsGrowMapping(
   982   1076     FileSystem *pFS,
   983   1077     i64 iSz,
   984   1078     int *pRc
................................................................................
   990   1084     if( *pRc==LSM_OK && iSz>pFS->nMap ){
   991   1085       int rc;
   992   1086       u8 *aOld = pFS->pMap;
   993   1087       rc = lsmEnvRemap(pFS->pEnv, pFS->fdDb, iSz, &pFS->pMap, &pFS->nMap);
   994   1088       if( rc==LSM_OK && pFS->pMap!=aOld ){
   995   1089         Page *pFix;
   996   1090         i64 iOff = (u8 *)pFS->pMap - aOld;
   997         -      for(pFix=pFS->pLruFirst; pFix; pFix=pFix->pLruNext){
         1091  +      for(pFix=pFS->pMapped; pFix; pFix=pFix->pMappedNext){
   998   1092           pFix->aData += iOff;
   999   1093         }
  1000   1094         lsmSortedRemap(pFS->pDb);
  1001   1095       }
  1002   1096       *pRc = rc;
  1003   1097     }
  1004   1098   }
  1005   1099   
  1006   1100   
  1007   1101   /*
  1008   1102   ** fsync() the database file.
  1009   1103   */
  1010   1104   int lsmFsSyncDb(FileSystem *pFS, int nBlock){
         1105  +#if 0
  1011   1106     if( nBlock && pFS->bUseMmap ){
  1012   1107       int rc = LSM_OK;
  1013   1108       i64 nMin = (i64)nBlock * (i64)pFS->nBlocksize;
  1014   1109       fsGrowMapping(pFS, nMin, &rc);
  1015   1110       if( rc!=LSM_OK ) return rc;
  1016   1111     }
         1112  +#endif
  1017   1113     return lsmEnvSync(pFS->pEnv, pFS->fdDb);
  1018   1114   }
  1019   1115   
  1020   1116   static int fsPageGet(FileSystem *, Segment *, Pgno, int, Page **, int *);
  1021   1117   
  1022   1118   static int fsRedirectBlock(Redirect *p, int iBlk){
  1023   1119     if( p ){
................................................................................
  1074   1170     
  1075   1171     if( pSeg ){
  1076   1172       iRead = fsRedirectBlock(pSeg->pRedirect, iBlock);
  1077   1173     }else{
  1078   1174       iRead = iBlock;
  1079   1175     }
  1080   1176   
  1081         -  assert( pFS->bUseMmap==0 || pFS->pCompress==0 );
         1177  +  assert( pFS->nMapLimit==0 || pFS->pCompress==0 );
  1082   1178     if( pFS->pCompress ){
  1083   1179       i64 iOff;                     /* File offset to read data from */
  1084   1180       u8 aNext[4];                  /* 4-byte pointer read from db file */
  1085   1181   
  1086   1182       iOff = (i64)iRead * pFS->nBlocksize - sizeof(aNext);
  1087   1183       rc = lsmEnvRead(pFS->pEnv, pFS->fdDb, iOff, aNext, sizeof(aNext));
  1088   1184       if( rc==LSM_OK ){
................................................................................
  1154   1250     FileSystem *pFS,                /* File-system object handle */
  1155   1251     Segment *pSeg,                  /* Use this segment for block redirects */
  1156   1252     int iBlock,                     /* Read field from this block */
  1157   1253     int *piPrev                     /* OUT: Previous block in linked list */
  1158   1254   ){
  1159   1255     int rc = LSM_OK;                /* Return code */
  1160   1256   
  1161         -  assert( pFS->bUseMmap==0 || pFS->pCompress==0 );
         1257  +  assert( pFS->nMapLimit==0 || pFS->pCompress==0 );
  1162   1258     assert( iBlock>0 );
  1163   1259   
  1164   1260     if( pFS->pCompress ){
  1165   1261       i64 iOff = fsFirstPageOnBlock(pFS, iBlock) - 4;
  1166   1262       u8 aPrev[4];                  /* 4-byte pointer read from db file */
  1167   1263       rc = lsmEnvRead(pFS->pEnv, pFS->fdDb, iOff, aPrev, sizeof(aPrev));
  1168   1264       if( rc==LSM_OK ){
................................................................................
  1342   1438     int rc = LSM_OK;
  1343   1439   
  1344   1440     /* In most cases iReal is the same as iPg. Except, if pSeg->pRedirect is 
  1345   1441     ** not NULL, and the block containing iPg has been redirected, then iReal
  1346   1442     ** is the page number after redirection.  */
  1347   1443     Pgno iReal = lsmFsRedirectPage(pFS, (pSeg ? pSeg->pRedirect : 0), iPg);
  1348   1444   
         1445  +  assert_lists_are_ok(pFS);
  1349   1446     assert( iPg>=fsFirstPageOnBlock(pFS, 1) );
  1350   1447     assert( iReal>=fsFirstPageOnBlock(pFS, 1) );
  1351   1448     *ppPg = 0;
  1352   1449   
  1353         -  assert( pFS->bUseMmap==0 || pFS->pCompress==0 );
  1354         -  if( pFS->bUseMmap ){
  1355         -    Page *pTest;
  1356         -    i64 iEnd = (i64)iReal * pFS->nPagesize;
  1357         -    fsGrowMapping(pFS, iEnd, &rc);
  1358         -    if( rc!=LSM_OK ) return rc;
  1359   1450   
  1360         -    p = 0;
  1361         -    for(pTest=pFS->pWaiting; pTest; pTest=pTest->pNextWaiting){
  1362         -      if( pTest->iPg==iReal ){
  1363         -        assert( iReal==iPg );
  1364         -        p = pTest;
  1365         -        p->nRef++;
  1366         -        *ppPg = p;
  1367         -        return LSM_OK;
  1368         -      }
  1369         -    }
  1370         -    if( pFS->pFree ){
  1371         -      p = pFS->pFree;
  1372         -      pFS->pFree = p->pHashNext;
  1373         -      assert( p->nRef==0 );
  1374         -    }else{
  1375         -      p = lsmMallocZeroRc(pFS->pEnv, sizeof(Page), &rc);
  1376         -      if( rc ) return rc;
  1377         -      fsPageAddToLru(pFS, p);
  1378         -      p->pFS = pFS;
  1379         -    }
  1380         -    p->aData = &((u8 *)pFS->pMap)[pFS->nPagesize * (iReal-1)];
  1381         -    p->iPg = iReal;
  1382         -    assert( (p->flags & PAGE_FREE)==0 );
         1451  +  /* Search the hash-table for the page */
         1452  +  p = fsPageFindInHash(pFS, iReal, &iHash);
         1453  +
         1454  +  if( p ){
         1455  +    assert( p->flags & PAGE_FREE );
         1456  +    if( p->nRef==0 ) fsPageRemoveFromLru(pFS, p);
  1383   1457     }else{
  1384   1458   
  1385         -    /* Search the hash-table for the page */
  1386         -    iHash = fsHashKey(pFS->nHash, iReal);
  1387         -    for(p=pFS->apHash[iHash]; p; p=p->pHashNext){
  1388         -      if( p->iPg==iReal) break;
  1389         -    }
         1459  +    if( fsMmapPage(pFS, iReal) ){
         1460  +      i64 iEnd = (i64)iReal * pFS->nPagesize;
         1461  +      fsGrowMapping(pFS, iEnd, &rc);
         1462  +      if( rc!=LSM_OK ) return rc;
  1390   1463   
  1391         -    if( p==0 ){
         1464  +      if( pFS->pFree ){
         1465  +        p = pFS->pFree;
         1466  +        pFS->pFree = p->pFreeNext;
         1467  +        assert( p->nRef==0 );
         1468  +      }else{
         1469  +        p = lsmMallocZeroRc(pFS->pEnv, sizeof(Page), &rc);
         1470  +        if( rc ) return rc;
         1471  +        p->pFS = pFS;
         1472  +      }
         1473  +      p->aData = &((u8 *)pFS->pMap)[pFS->nPagesize * (iReal-1)];
         1474  +      p->iPg = iReal;
         1475  +
         1476  +      /* This page now carries a pointer to the mapping. Link it in to
         1477  +      ** the FileSystem.pMapped list.  */
         1478  +      assert( p->pMappedNext==0 );
         1479  +      p->pMappedNext = pFS->pMapped;
         1480  +      pFS->pMapped = p;
         1481  +
         1482  +      assert( pFS->pCompress==0 );
         1483  +      assert( (p->flags & PAGE_FREE)==0 );
         1484  +    }else{
  1392   1485         rc = fsPageBuffer(pFS, &p);
  1393   1486         if( rc==LSM_OK ){
  1394   1487           int nSpace = 0;
  1395   1488           p->iPg = iReal;
  1396   1489           p->nRef = 0;
  1397   1490           p->pFS = pFS;
  1398   1491           assert( p->flags==0 || p->flags==PAGE_FREE );
................................................................................
  1409   1502               i64 iOff = (i64)(iReal-1) * pFS->nPagesize;
  1410   1503               rc = lsmEnvRead(pFS->pEnv, pFS->fdDb, iOff, p->aData, nByte);
  1411   1504             }
  1412   1505             pFS->nRead++;
  1413   1506           }
  1414   1507   
  1415   1508           /* If the xRead() call was successful (or not attempted), link the
  1416         -         ** page into the page-cache hash-table. Otherwise, if it failed,
  1417         -         ** free the buffer. */
         1509  +        ** page into the page-cache hash-table. Otherwise, if it failed,
         1510  +        ** free the buffer. */
  1418   1511           if( rc==LSM_OK && nSpace==0 ){
  1419   1512             p->pHashNext = pFS->apHash[iHash];
  1420   1513             pFS->apHash[iHash] = p;
  1421   1514           }else{
  1422   1515             fsPageBufferFree(p);
  1423   1516             p = 0;
  1424   1517             if( pnSpace ) *pnSpace = nSpace;
  1425   1518           }
  1426   1519         }
  1427         -    }else if( p->nRef==0 ){
  1428         -      fsPageRemoveFromLru(pFS, p);
  1429   1520       }
  1430   1521   
  1431   1522       assert( (rc==LSM_OK && (p || (pnSpace && *pnSpace)))
  1432   1523            || (rc!=LSM_OK && p==0) 
  1433   1524       );
  1434   1525     }
  1435   1526   
................................................................................
  1461   1552   ** may be garbage. It is the callers responsibility to deal with this.
  1462   1553   */
  1463   1554   int lsmFsReadSyncedId(lsm_db *db, int iMeta, i64 *piVal){
  1464   1555     FileSystem *pFS = db->pFS;
  1465   1556     int rc = LSM_OK;
  1466   1557   
  1467   1558     assert( iMeta==1 || iMeta==2 );
  1468         -  if( pFS->bUseMmap ){
         1559  +  if( pFS->nMapLimit>0 ){
  1469   1560       fsGrowMapping(pFS, iMeta*LSM_META_PAGE_SIZE, &rc);
  1470   1561       if( rc==LSM_OK ){
  1471   1562         *piVal = (i64)lsmGetU64(&((u8 *)pFS->pMap)[(iMeta-1)*LSM_META_PAGE_SIZE]);
  1472   1563       }
  1473   1564     }else{
  1474   1565       MetaPage *pMeta = 0;
  1475   1566       rc = lsmFsMetaPageGet(pFS, 0, iMeta, &pMeta);
................................................................................
  2027   2118     MetaPage *pPg;
  2028   2119     assert( iPg==1 || iPg==2 );
  2029   2120   
  2030   2121     pPg = lsmMallocZeroRc(pFS->pEnv, sizeof(Page), &rc);
  2031   2122   
  2032   2123     if( pPg ){
  2033   2124       i64 iOff = (iPg-1) * pFS->nMetasize;
  2034         -    if( pFS->bUseMmap ){
         2125  +    if( pFS->nMapLimit>0 ){
  2035   2126         fsGrowMapping(pFS, 2*pFS->nMetasize, &rc);
  2036   2127         pPg->aData = (u8 *)(pFS->pMap) + iOff;
  2037   2128       }else{
  2038   2129         pPg->aData = lsmMallocRc(pFS->pEnv, pFS->nMetasize, &rc);
  2039   2130         if( rc==LSM_OK && bWrite==0 ){
  2040   2131           rc = lsmEnvRead(pFS->pEnv, pFS->fdDb, iOff, pPg->aData, pFS->nMetasize);
  2041   2132         }
................................................................................
  2048   2139         else if( rc==LSM_OK ){
  2049   2140           memset( pPg->aData, 0x77, pFS->nMetasize );
  2050   2141         }
  2051   2142   #endif
  2052   2143       }
  2053   2144   
  2054   2145       if( rc!=LSM_OK ){
  2055         -      if( pFS->bUseMmap==0 ) lsmFree(pFS->pEnv, pPg->aData);
         2146  +      if( pFS->nMapLimit==0 ) lsmFree(pFS->pEnv, pPg->aData);
  2056   2147         lsmFree(pFS->pEnv, pPg);
  2057   2148         pPg = 0;
  2058   2149       }else{
  2059   2150         pPg->iPg = iPg;
  2060   2151         pPg->bWrite = bWrite;
  2061   2152         pPg->pFS = pFS;
  2062   2153       }
................................................................................
  2070   2161   ** Release a meta-page reference obtained via a call to lsmFsMetaPageGet().
  2071   2162   */
  2072   2163   int lsmFsMetaPageRelease(MetaPage *pPg){
  2073   2164     int rc = LSM_OK;
  2074   2165     if( pPg ){
  2075   2166       FileSystem *pFS = pPg->pFS;
  2076   2167   
  2077         -    if( pFS->bUseMmap==0 ){
         2168  +    if( pFS->nMapLimit==0 ){
  2078   2169         if( pPg->bWrite ){
  2079   2170           i64 iOff = (pPg->iPg==2 ? pFS->nMetasize : 0);
  2080   2171           int nWrite = pFS->nMetasize;
  2081   2172           rc = lsmEnvWrite(pFS->pEnv, pFS->fdDb, iOff, pPg->aData, nWrite);
  2082   2173         }
  2083   2174         lsmFree(pFS->pEnv, pPg->aData);
  2084   2175       }
................................................................................
  2127   2218   ** It is safe to assume that there are no outstanding references to pages 
  2128   2219   ** on block iTo. And that block iFrom is not currently being written. In
  2129   2220   ** other words, the data can be read and written directly.
  2130   2221   */
  2131   2222   int lsmFsMoveBlock(FileSystem *pFS, Segment *pSeg, int iTo, int iFrom){
  2132   2223     Snapshot *p = pFS->pDb->pWorker;
  2133   2224     int rc = LSM_OK;
         2225  +  i64 nMap;
  2134   2226   
  2135   2227     i64 iFromOff = (i64)(iFrom-1) * pFS->nBlocksize;
  2136   2228     i64 iToOff = (i64)(iTo-1) * pFS->nBlocksize;
  2137   2229     
  2138   2230     assert( iTo!=1 );
  2139   2231     assert( iFrom>iTo );
  2140   2232   
  2141         -  if( pFS->bUseMmap ){
  2142         -    fsGrowMapping(pFS, (i64)iFrom * pFS->nBlocksize, &rc);
  2143         -    if( rc==LSM_OK ){
  2144         -      u8 *aMap = (u8 *)(pFS->pMap);
  2145         -      memcpy(&aMap[iToOff], &aMap[iFromOff], pFS->nBlocksize);
  2146         -    }
  2147         -  }else{
         2233  +  /* Grow the mapping as required. */
         2234  +  nMap = LSM_MIN(pFS->nMapLimit, (i64)iFrom * pFS->nBlocksize);
         2235  +  fsGrowMapping(pFS, nMap, &rc);
         2236  +
         2237  +  if( rc==LSM_OK ){
         2238  +    const int nPagePerBlock = (pFS->nBlocksize / pFS->nPagesize);
  2148   2239       int nSz = pFS->nPagesize;
  2149         -    u8 *aData = (u8 *)lsmMallocRc(pFS->pEnv, nSz, &rc);
  2150         -    if( rc==LSM_OK ){
  2151         -      const int nPagePerBlock = (pFS->nBlocksize / pFS->nPagesize);
  2152         -      int i;
  2153         -      for(i=0; rc==LSM_OK && i<nPagePerBlock; i++){
  2154         -        i64 iOff = iFromOff + i*nSz;
         2240  +    int i;
         2241  +    u8 *aBuf = 0;
         2242  +    u8 *aData = 0;
         2243  +
         2244  +    for(i=0; rc==LSM_OK && i<nPagePerBlock; i++){
         2245  +      i64 iOff = iFromOff + i*nSz;
         2246  +      Page *pPg;
         2247  +
         2248  +      /* Set aData to point to a buffer containing the from page */
         2249  +      if( (iOff+nSz)<=pFS->nMapLimit ){
         2250  +        u8 *aMap = (u8 *)(pFS->pMap);
         2251  +        aData = &aMap[iOff];
         2252  +      }else{
         2253  +        if( aBuf==0 ){
         2254  +          aBuf = (u8 *)lsmMallocRc(pFS->pEnv, nSz, &rc);
         2255  +          if( aBuf==0 ) break;
         2256  +        }
         2257  +        aData = aBuf;
  2155   2258           rc = lsmEnvRead(pFS->pEnv, pFS->fdDb, iOff, aData, nSz);
  2156         -        if( rc==LSM_OK ){
  2157         -          iOff = iToOff + i*nSz;
         2259  +      }
         2260  +
         2261  +      /* Copy aData to the to page */
         2262  +      if( rc==LSM_OK ){
         2263  +        iOff = iToOff + i*nSz;
         2264  +        if( (iOff+nSz)<=pFS->nMapLimit ){
         2265  +          u8 *aMap = (u8 *)(pFS->pMap);
         2266  +          memcpy(&aMap[iOff], aData, nSz);
         2267  +        }else{
  2158   2268             rc = lsmEnvWrite(pFS->pEnv, pFS->fdDb, iOff, aData, nSz);
  2159   2269           }
  2160   2270         }
  2161         -      lsmFsPurgeCache(pFS);
  2162   2271       }
         2272  +    lsmFree(pFS->pEnv, aBuf);
         2273  +    lsmFsPurgeCache(pFS);
  2163   2274     }
  2164   2275   
  2165   2276     /* Update append-point list if necessary */
  2166   2277     if( rc==LSM_OK ){
  2167   2278       int i;
  2168   2279       for(i=0; i<LSM_APPLIST_SZ; i++){
  2169   2280         if( fsPageToBlock(pFS, p->aiAppend[i])==iFrom ){
................................................................................
  2343   2454     int rc = *pRc;
  2344   2455     Page *pPg;
  2345   2456   
  2346   2457     pPg = pFS->pWaiting;
  2347   2458     pFS->pWaiting = 0;
  2348   2459   
  2349   2460     while( pPg ){
  2350         -    Page *pNext = pPg->pNextWaiting;
         2461  +    Page *pNext = pPg->pWaitingNext;
  2351   2462       if( rc==LSM_OK ) rc = lsmFsPagePersist(pPg);
  2352   2463       assert( pPg->nRef==1 );
  2353   2464       lsmFsPageRelease(pPg);
  2354   2465       pPg = pNext;
  2355   2466     }
  2356   2467     *pRc = rc;
  2357   2468   }
................................................................................
  2359   2470   static void fsRemoveHashEntry(FileSystem *pFS, Pgno iPg){
  2360   2471     Page *p;
  2361   2472     int iHash = fsHashKey(pFS->nHash, iPg);
  2362   2473   
  2363   2474     for(p=pFS->apHash[iHash]; p && p->iPg!=iPg; p=p->pHashNext);
  2364   2475   
  2365   2476     if( p ){
  2366         -    assert( p->nRef==0 );
         2477  +    assert( p->nRef==0 || (p->flags & PAGE_FREE)==0 );
  2367   2478       fsPageRemoveFromHash(pFS, p);
  2368   2479       p->iPg = 0;
  2369   2480       iHash = fsHashKey(pFS->nHash, 0);
  2370   2481       p->pHashNext = pFS->apHash[iHash];
  2371   2482       pFS->apHash[iHash] = p;
  2372   2483     }
  2373   2484   }
................................................................................
  2419   2530           ** lsmFsPagePersist() to write an out-of-order page. Instead a page 
  2420   2531           ** number is assigned here so that the page data will be appended
  2421   2532           ** to the current segment.
  2422   2533           */
  2423   2534           Page **pp;
  2424   2535           int iPrev = 0;
  2425   2536           int iNext = 0;
         2537  +        int iHash;
  2426   2538   
  2427   2539           assert( pPg->pSeg->iFirst );
  2428   2540           assert( pPg->flags & PAGE_FREE );
  2429   2541           assert( (pPg->flags & PAGE_HASPREV)==0 );
  2430   2542           assert( pPg->nData==pFS->nPagesize-4 );
  2431   2543   
  2432   2544           rc = fsAppendPage(pFS, pPg->pSeg, &pPg->iPg, &iPrev, &iNext);
  2433   2545           if( rc!=LSM_OK ) return rc;
  2434   2546   
  2435         -        if( pFS->bUseMmap==0 ){
  2436         -          int iHash = fsHashKey(pFS->nHash, pPg->iPg);
  2437         -          fsRemoveHashEntry(pFS, pPg->iPg);
  2438         -          pPg->pHashNext = pFS->apHash[iHash];
  2439         -          pFS->apHash[iHash] = pPg;
  2440         -          assert( pPg->pHashNext==0 || pPg->pHashNext->iPg!=pPg->iPg );
  2441         -        }
         2547  +        assert( pPg->flags & PAGE_FREE );
         2548  +        iHash = fsHashKey(pFS->nHash, pPg->iPg);
         2549  +        fsRemoveHashEntry(pFS, pPg->iPg);
         2550  +        pPg->pHashNext = pFS->apHash[iHash];
         2551  +        pFS->apHash[iHash] = pPg;
         2552  +        assert( pPg->pHashNext==0 || pPg->pHashNext->iPg!=pPg->iPg );
  2442   2553   
  2443   2554           if( iPrev ){
  2444   2555             assert( iNext==0 );
  2445   2556             memmove(&pPg->aData[4], pPg->aData, pPg->nData);
  2446   2557             lsmPutU32(pPg->aData, iPrev);
  2447   2558             pPg->flags |= PAGE_HASPREV;
  2448   2559             pPg->aData += 4;
................................................................................
  2452   2563           }else{
  2453   2564             int nData = pPg->nData;
  2454   2565             pPg->nData += 4;
  2455   2566             lsmSortedExpandBtreePage(pPg, nData);
  2456   2567           }
  2457   2568   
  2458   2569           pPg->nRef++;
  2459         -        for(pp=&pFS->pWaiting; *pp; pp=&(*pp)->pNextWaiting);
         2570  +        for(pp=&pFS->pWaiting; *pp; pp=&(*pp)->pWaitingNext);
  2460   2571           *pp = pPg;
  2461         -        assert( pPg->pNextWaiting==0 );
         2572  +        assert( pPg->pWaitingNext==0 );
  2462   2573   
  2463   2574         }else{
  2464   2575           i64 iOff;                   /* Offset to write within database file */
  2465   2576   
  2466   2577           iOff = (i64)pFS->nPagesize * (i64)(pPg->iPg-1);
  2467         -        if( pFS->bUseMmap==0 ){
         2578  +        if( fsMmapPage(pFS, pPg->iPg)==0 ){
  2468   2579             u8 *aData = pPg->aData - (pPg->flags & PAGE_HASPREV);
  2469   2580             rc = lsmEnvWrite(pFS->pEnv, pFS->fdDb, iOff, aData, pFS->nPagesize);
  2470   2581           }else if( pPg->flags & PAGE_FREE ){
  2471   2582             fsGrowMapping(pFS, iOff + pFS->nPagesize, &rc);
  2472   2583             if( rc==LSM_OK ){
  2473   2584               u8 *aTo = &((u8 *)(pFS->pMap))[iOff];
  2474   2585               u8 *aFrom = pPg->aData - (pPg->flags & PAGE_HASPREV);
  2475   2586               memcpy(aTo, aFrom, pFS->nPagesize);
  2476   2587               lsmFree(pFS->pEnv, aFrom);
         2588  +            pFS->nCacheAlloc--;
  2477   2589               pPg->aData = aTo + (pPg->flags & PAGE_HASPREV);
  2478   2590               pPg->flags &= ~PAGE_FREE;
  2479         -            fsPageAddToLru(pFS, pPg);
         2591  +            fsPageRemoveFromHash(pFS, pPg);
         2592  +            pPg->pMappedNext = pFS->pMapped;
         2593  +            pFS->pMapped = pPg;
  2480   2594             }
  2481   2595           }
  2482   2596   
  2483   2597           lsmFsFlushWaiting(pFS, &rc);
  2484   2598           pPg->flags &= ~PAGE_DIRTY;
  2485   2599           pFS->nWrite++;
  2486   2600         }
................................................................................
  2572   2686         assert( pPg->pFS->pCompress 
  2573   2687              || fsIsFirst(pPg->pFS, pPg->iPg)==0 
  2574   2688              || (pPg->flags & PAGE_HASPREV)
  2575   2689         );
  2576   2690         pPg->aData -= (pPg->flags & PAGE_HASPREV);
  2577   2691         pPg->flags &= ~PAGE_HASPREV;
  2578   2692   
  2579         -      if( pFS->bUseMmap ){
  2580         -        pPg->pHashNext = pFS->pFree;
         2693  +      if( (pPg->flags & PAGE_FREE)==0 ){
         2694  +        /* Removed from mapped list */
         2695  +        Page **pp;
         2696  +        for(pp=&pFS->pMapped; (*pp)!=pPg; pp=&(*pp)->pMappedNext);
         2697  +        *pp = pPg->pMappedNext;
         2698  +        pPg->pMappedNext = 0;
         2699  +
         2700  +        /* Add to free list */
         2701  +        pPg->pFreeNext = pFS->pFree;
  2581   2702           pFS->pFree = pPg;
  2582   2703         }else{
  2583   2704   #if 0
  2584   2705           assert( pPg->pLruNext==0 );
  2585   2706           assert( pPg->pLruPrev==0 );
  2586   2707           fsPageRemoveFromHash(pFS, pPg);
  2587   2708           fsPageBufferFree(pPg);

Changes to src/lsm_main.c.

    92     92     pDb->nDfltBlksz = LSM_DFLT_BLOCK_SIZE;
    93     93     pDb->nMerge = LSM_DFLT_AUTOMERGE;
    94     94     pDb->nMaxFreelist = LSM_MAX_FREELIST_ENTRIES;
    95     95     pDb->bUseLog = LSM_DFLT_USE_LOG;
    96     96     pDb->iReader = -1;
    97     97     pDb->iRwclient = -1;
    98     98     pDb->bMultiProc = LSM_DFLT_MULTIPLE_PROCESSES;
    99         -  pDb->bMmap = LSM_DFLT_MMAP;
           99  +  pDb->iMmap = LSM_DFLT_MMAP;
   100    100     pDb->xLog = xLog;
   101    101     pDb->compress.iId = LSM_COMPRESSION_NONE;
   102    102     return LSM_OK;
   103    103   }
   104    104   
   105    105   lsm_env *lsm_get_env(lsm_db *pDb){
   106    106     assert( pDb->pEnv );
................................................................................
   325    325         }
   326    326         *piVal = pDb->eSafety;
   327    327         break;
   328    328       }
   329    329   
   330    330       case LSM_CONFIG_MMAP: {
   331    331         int *piVal = va_arg(ap, int *);
   332         -      if( pDb->iReader<0 && *piVal>=0 && *piVal<=1 ){
   333         -        pDb->bMmap = *piVal;
          332  +      if( pDb->iReader<0 && *piVal>=0 ){
          333  +        pDb->iMmap = *piVal;
   334    334           rc = lsmFsConfigure(pDb);
   335    335         }
   336         -      *piVal = pDb->bMmap;
          336  +      *piVal = pDb->iMmap;
   337    337         break;
   338    338       }
   339    339   
   340    340       case LSM_CONFIG_USE_LOG: {
   341    341         int *piVal = va_arg(ap, int *);
   342    342         if( pDb->nTransOpen==0 && (*piVal==0 || *piVal==1) ){
   343    343           pDb->bUseLog = *piVal;