modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/lsm1/lsm-test/lsmtest_tdb3.c (about)

     1  
     2  #include "lsmtest_tdb.h"
     3  #include "lsm.h"
     4  #include "lsmtest.h"
     5  
     6  #include <stdlib.h>
     7  #include <string.h>
     8  #include <assert.h>
     9  #ifndef _WIN32
    10  # include <unistd.h>
    11  #endif
    12  #include <stdio.h>
    13  
    14  #ifndef _WIN32
    15  # include <sys/time.h>
    16  #endif
    17  
    18  typedef struct LsmDb LsmDb;
    19  typedef struct LsmWorker LsmWorker;
    20  typedef struct LsmFile LsmFile;
    21  
    22  #define LSMTEST_DFLT_MT_MAX_CKPT (8*1024)
    23  #define LSMTEST_DFLT_MT_MIN_CKPT (2*1024)
    24  
    25  #ifdef LSM_MUTEX_PTHREADS
    26  #include <pthread.h>
    27  
    28  #define LSMTEST_THREAD_CKPT      1
    29  #define LSMTEST_THREAD_WORKER    2
    30  #define LSMTEST_THREAD_WORKER_AC 3
    31  
    32  /*
    33  ** There are several different types of worker threads that run in different
    34  ** test configurations, depending on the value of LsmWorker.eType.
    35  **
    36  **   1. Checkpointer.
    37  **   2. Worker with auto-checkpoint.
    38  **   3. Worker without auto-checkpoint.
    39  */
    40  struct LsmWorker {
    41    LsmDb *pDb;                     /* Main database structure */
    42    lsm_db *pWorker;                /* Worker database handle */
    43    pthread_t worker_thread;        /* Worker thread */
    44    pthread_cond_t worker_cond;     /* Condition var the worker waits on */
    45    pthread_mutex_t worker_mutex;   /* Mutex used with worker_cond */
    46    int bDoWork;                    /* Set to true by client when there is work */
    47    int worker_rc;                  /* Store error code here */
    48    int eType;                      /* LSMTEST_THREAD_XXX constant */
    49    int bBlock;
    50  };
    51  #else
    52  struct LsmWorker { int worker_rc; int bBlock; };
    53  #endif
    54  
    55  static void mt_shutdown(LsmDb *);
    56  
    57  lsm_env *tdb_lsm_env(void){
    58    static int bInit = 0;
    59    static lsm_env env;
    60    if( bInit==0 ){
    61      memcpy(&env, lsm_default_env(), sizeof(env));
    62      bInit = 1;
    63    }
    64    return &env;
    65  }
    66  
    67  typedef struct FileSector FileSector;
    68  typedef struct FileData FileData;
    69  
    70  struct FileSector {
    71    u8 *aOld;                       /* Old data for this sector */
    72  };
    73  
    74  struct FileData {
    75    int nSector;                    /* Allocated size of apSector[] array */
    76    FileSector *aSector;            /* Array of file sectors */
    77  };
    78  
    79  /*
    80  ** bPrepareCrash:
    81  **   If non-zero, the file wrappers maintain enough in-memory data to
    82  **   simulate the effect of a power-failure on the file-system (i.e. that
    83  **   unsynced sectors may be written, not written, or overwritten with
    84  **   arbitrary data when the crash occurs).
    85  **
    86  ** bCrashed:
    87  **   Set to true after a crash is simulated. Once this variable is true, all
    88  **   VFS methods other than xClose() return LSM_IOERR as soon as they are
    89  **   called (without affecting the contents of the file-system).
    90  **
    91  ** env:
    92  **   The environment object used by all lsm_db* handles opened by this
    93  **   object (i.e. LsmDb.db plus any worker connections). Variable env.pVfsCtx
    94  **   always points to the containing LsmDb structure.
    95  */
    96  struct LsmDb {
    97    TestDb base;                    /* Base class - methods table */
    98    lsm_env env;                    /* Environment used by connection db */
    99    char *zName;                    /* Database file name */
   100    lsm_db *db;                     /* LSM database handle */
   101  
   102    lsm_cursor *pCsr;               /* Cursor held open during read transaction */
   103    void *pBuf;                     /* Buffer for tdb_fetch() output */
   104    int nBuf;                       /* Allocated (not used) size of pBuf */
   105  
   106    /* Crash testing related state */
   107    int bCrashed;                   /* True once a crash has occurred */
   108    int nAutoCrash;                 /* Number of syncs until a crash */
   109    int bPrepareCrash;              /* True to store writes in memory */
   110  
   111    /* Unsynced data (while crash testing) */
   112    int szSector;                   /* Assumed size of disk sectors (512B) */
   113    FileData aFile[2];              /* Database and log file data */
   114  
   115    /* Other test instrumentation */
   116    int bNoRecovery;                /* If true, assume DMS2 is locked */
   117  
   118    /* Work hook redirection */
   119    void (*xWork)(lsm_db *, void *);
   120    void *pWorkCtx;
   121  
   122    /* IO logging hook */
   123    void (*xWriteHook)(void *, int, lsm_i64, int, int);
   124    void *pWriteCtx;
   125    
   126    /* Worker threads (for lsm_mt) */
   127    int nMtMinCkpt;
   128    int nMtMaxCkpt;
   129    int eMode;
   130    int nWorker;
   131    LsmWorker *aWorker;
   132  };
   133  
   134  #define LSMTEST_MODE_SINGLETHREAD    1
   135  #define LSMTEST_MODE_BACKGROUND_CKPT 2
   136  #define LSMTEST_MODE_BACKGROUND_WORK 3
   137  #define LSMTEST_MODE_BACKGROUND_BOTH 4
   138  
   139  /*************************************************************************
   140  **************************************************************************
   141  ** Begin test VFS code.
   142  */
   143  
   144  struct LsmFile {
   145    lsm_file *pReal;                /* Real underlying file */
   146    int bLog;                       /* True for log file. False for db file */
   147    LsmDb *pDb;                     /* Database handle that uses this file */
   148  };
   149  
   150  static int testEnvFullpath(
   151    lsm_env *pEnv,                  /* Environment for current LsmDb */
   152    const char *zFile,              /* Relative path name */
   153    char *zOut,                     /* Output buffer */
   154    int *pnOut                      /* IN/OUT: Size of output buffer */
   155  ){
   156    lsm_env *pRealEnv = tdb_lsm_env();
   157    return pRealEnv->xFullpath(pRealEnv, zFile, zOut, pnOut);
   158  }
   159  
   160  static int testEnvOpen(
   161    lsm_env *pEnv,                  /* Environment for current LsmDb */
   162    const char *zFile,              /* Name of file to open */
   163    int flags,
   164    lsm_file **ppFile               /* OUT: New file handle object */
   165  ){
   166    lsm_env *pRealEnv = tdb_lsm_env();
   167    LsmDb *pDb = (LsmDb *)pEnv->pVfsCtx;
   168    int rc;                         /* Return Code */
   169    LsmFile *pRet;                  /* The new file handle */
   170    int nFile;                      /* Length of string zFile in bytes */
   171  
   172    nFile = strlen(zFile);
   173    pRet = (LsmFile *)testMalloc(sizeof(LsmFile));
   174    pRet->pDb = pDb;
   175    pRet->bLog = (nFile > 4 && 0==memcmp("-log", &zFile[nFile-4], 4));
   176  
   177    rc = pRealEnv->xOpen(pRealEnv, zFile, flags, &pRet->pReal);
   178    if( rc!=LSM_OK ){
   179      testFree(pRet);
   180      pRet = 0;
   181    }
   182  
   183    *ppFile = (lsm_file *)pRet;
   184    return rc;
   185  }
   186  
   187  static int testEnvRead(lsm_file *pFile, lsm_i64 iOff, void *pData, int nData){
   188    lsm_env *pRealEnv = tdb_lsm_env();
   189    LsmFile *p = (LsmFile *)pFile;
   190    if( p->pDb->bCrashed ) return LSM_IOERR;
   191    return pRealEnv->xRead(p->pReal, iOff, pData, nData);
   192  }
   193  
   194  static int testEnvWrite(lsm_file *pFile, lsm_i64 iOff, void *pData, int nData){
   195    lsm_env *pRealEnv = tdb_lsm_env();
   196    LsmFile *p = (LsmFile *)pFile;
   197    LsmDb *pDb = p->pDb;
   198  
   199    if( pDb->bCrashed ) return LSM_IOERR;
   200  
   201    if( pDb->bPrepareCrash ){
   202      FileData *pData2 = &pDb->aFile[p->bLog];
   203      int iFirst;                 
   204      int iLast;
   205      int iSector;
   206  
   207      iFirst = (int)(iOff / pDb->szSector);
   208      iLast =  (int)((iOff + nData - 1) / pDb->szSector);
   209  
   210      if( pData2->nSector<(iLast+1) ){
   211        int nNew = ( ((iLast + 1) + 63) / 64 ) * 64;
   212        assert( nNew>iLast );
   213        pData2->aSector = (FileSector *)testRealloc(
   214            pData2->aSector, nNew*sizeof(FileSector)
   215        );
   216        memset(&pData2->aSector[pData2->nSector], 
   217            0, (nNew - pData2->nSector) * sizeof(FileSector)
   218        );
   219        pData2->nSector = nNew;
   220      }
   221  
   222      for(iSector=iFirst; iSector<=iLast; iSector++){
   223        if( pData2->aSector[iSector].aOld==0 ){
   224          u8 *aOld = (u8 *)testMalloc(pDb->szSector);
   225          pRealEnv->xRead(
   226              p->pReal, (lsm_i64)iSector*pDb->szSector, aOld, pDb->szSector
   227          );
   228          pData2->aSector[iSector].aOld = aOld;
   229        }
   230      }
   231    }
   232  
   233    if( pDb->xWriteHook ){
   234      int rc;
   235      int nUs;
   236      struct timeval t1;
   237      struct timeval t2;
   238  
   239      gettimeofday(&t1, 0);
   240      assert( nData>0 );
   241      rc = pRealEnv->xWrite(p->pReal, iOff, pData, nData);
   242      gettimeofday(&t2, 0);
   243  
   244      nUs = (t2.tv_sec - t1.tv_sec) * 1000000 + (t2.tv_usec - t1.tv_usec);
   245      pDb->xWriteHook(pDb->pWriteCtx, p->bLog, iOff, nData, nUs);
   246      return rc;
   247    }
   248  
   249    return pRealEnv->xWrite(p->pReal, iOff, pData, nData);
   250  }
   251  
   252  static void doSystemCrash(LsmDb *pDb);
   253  
   254  static int testEnvSync(lsm_file *pFile){
   255    lsm_env *pRealEnv = tdb_lsm_env();
   256    LsmFile *p = (LsmFile *)pFile;
   257    LsmDb *pDb = p->pDb;
   258    FileData *pData = &pDb->aFile[p->bLog];
   259    int i;
   260  
   261    if( pDb->bCrashed ) return LSM_IOERR;
   262  
   263    if( pDb->nAutoCrash ){
   264      pDb->nAutoCrash--;
   265      if( pDb->nAutoCrash==0 ){
   266        doSystemCrash(pDb);
   267        pDb->bCrashed = 1;
   268        return LSM_IOERR;
   269      }
   270    }
   271  
   272    if( pDb->bPrepareCrash ){
   273      for(i=0; i<pData->nSector; i++){
   274        testFree(pData->aSector[i].aOld);
   275        pData->aSector[i].aOld = 0;
   276      }
   277    }
   278  
   279    if( pDb->xWriteHook ){
   280      int rc;
   281      int nUs;
   282      struct timeval t1;
   283      struct timeval t2;
   284  
   285      gettimeofday(&t1, 0);
   286      rc = pRealEnv->xSync(p->pReal);
   287      gettimeofday(&t2, 0);
   288  
   289      nUs = (t2.tv_sec - t1.tv_sec) * 1000000 + (t2.tv_usec - t1.tv_usec);
   290      pDb->xWriteHook(pDb->pWriteCtx, p->bLog, 0, 0, nUs);
   291      return rc;
   292    }
   293  
   294    return pRealEnv->xSync(p->pReal);
   295  }
   296  
   297  static int testEnvTruncate(lsm_file *pFile, lsm_i64 iOff){
   298    lsm_env *pRealEnv = tdb_lsm_env();
   299    LsmFile *p = (LsmFile *)pFile;
   300    if( p->pDb->bCrashed ) return LSM_IOERR;
   301    return pRealEnv->xTruncate(p->pReal, iOff);
   302  }
   303  
   304  static int testEnvSectorSize(lsm_file *pFile){
   305    lsm_env *pRealEnv = tdb_lsm_env();
   306    LsmFile *p = (LsmFile *)pFile;
   307    return pRealEnv->xSectorSize(p->pReal);
   308  }
   309  
   310  static int testEnvRemap(
   311    lsm_file *pFile, 
   312    lsm_i64 iMin, 
   313    void **ppOut,
   314    lsm_i64 *pnOut
   315  ){
   316    lsm_env *pRealEnv = tdb_lsm_env();
   317    LsmFile *p = (LsmFile *)pFile;
   318    return pRealEnv->xRemap(p->pReal, iMin, ppOut, pnOut);
   319  }
   320  
   321  static int testEnvFileid(
   322    lsm_file *pFile, 
   323    void *ppOut,
   324    int *pnOut
   325  ){
   326    lsm_env *pRealEnv = tdb_lsm_env();
   327    LsmFile *p = (LsmFile *)pFile;
   328    return pRealEnv->xFileid(p->pReal, ppOut, pnOut);
   329  }
   330  
   331  static int testEnvClose(lsm_file *pFile){
   332    lsm_env *pRealEnv = tdb_lsm_env();
   333    LsmFile *p = (LsmFile *)pFile;
   334  
   335    pRealEnv->xClose(p->pReal);
   336    testFree(p);
   337    return LSM_OK;
   338  }
   339  
   340  static int testEnvUnlink(lsm_env *pEnv, const char *zFile){
   341    lsm_env *pRealEnv = tdb_lsm_env();
   342    unused_parameter(pEnv);
   343    return pRealEnv->xUnlink(pRealEnv, zFile);
   344  }
   345  
   346  static int testEnvLock(lsm_file *pFile, int iLock, int eType){
   347    LsmFile *p = (LsmFile *)pFile;
   348    lsm_env *pRealEnv = tdb_lsm_env();
   349  
   350    if( iLock==2 && eType==LSM_LOCK_EXCL && p->pDb->bNoRecovery ){
   351      return LSM_BUSY;
   352    }
   353    return pRealEnv->xLock(p->pReal, iLock, eType);
   354  }
   355  
   356  static int testEnvTestLock(lsm_file *pFile, int iLock, int nLock, int eType){
   357    LsmFile *p = (LsmFile *)pFile;
   358    lsm_env *pRealEnv = tdb_lsm_env();
   359  
   360    if( iLock==2 && eType==LSM_LOCK_EXCL && p->pDb->bNoRecovery ){
   361      return LSM_BUSY;
   362    }
   363    return pRealEnv->xTestLock(p->pReal, iLock, nLock, eType);
   364  }
   365  
   366  static int testEnvShmMap(lsm_file *pFile, int iRegion, int sz, void **pp){
   367    LsmFile *p = (LsmFile *)pFile;
   368    lsm_env *pRealEnv = tdb_lsm_env();
   369    return pRealEnv->xShmMap(p->pReal, iRegion, sz, pp);
   370  }
   371  
   372  static void testEnvShmBarrier(void){
   373  }
   374  
   375  static int testEnvShmUnmap(lsm_file *pFile, int bDel){
   376    LsmFile *p = (LsmFile *)pFile;
   377    lsm_env *pRealEnv = tdb_lsm_env();
   378    return pRealEnv->xShmUnmap(p->pReal, bDel);
   379  }
   380  
   381  static int testEnvSleep(lsm_env *pEnv, int us){
   382    lsm_env *pRealEnv = tdb_lsm_env();
   383    return pRealEnv->xSleep(pRealEnv, us);
   384  }
   385  
   386  static void doSystemCrash(LsmDb *pDb){
   387    lsm_env *pEnv = tdb_lsm_env();
   388    int iFile;
   389    int iSeed = pDb->aFile[0].nSector + pDb->aFile[1].nSector;
   390  
   391    char *zFile = pDb->zName;
   392    char *zFree = 0;
   393  
   394    for(iFile=0; iFile<2; iFile++){
   395      lsm_file *pFile = 0;
   396      int i;
   397  
   398      pEnv->xOpen(pEnv, zFile, 0, &pFile);
   399      for(i=0; i<pDb->aFile[iFile].nSector; i++){
   400        u8 *aOld = pDb->aFile[iFile].aSector[i].aOld;
   401        if( aOld ){
   402          int iOpt = testPrngValue(iSeed++) % 3;
   403          switch( iOpt ){
   404            case 0:
   405              break;
   406  
   407            case 1:
   408              testPrngArray(iSeed++, (u32 *)aOld, pDb->szSector/4);
   409              /* Fall-through */
   410  
   411            case 2:
   412              pEnv->xWrite(
   413                  pFile, (lsm_i64)i * pDb->szSector, aOld, pDb->szSector
   414              );
   415              break;
   416          }
   417          testFree(aOld);
   418          pDb->aFile[iFile].aSector[i].aOld = 0;
   419        }
   420      }
   421      pEnv->xClose(pFile);
   422      zFree = zFile = sqlite3_mprintf("%s-log", pDb->zName);
   423    }
   424  
   425    sqlite3_free(zFree);
   426  }
   427  /*
   428  ** End test VFS code.
   429  **************************************************************************
   430  *************************************************************************/
   431  
   432  /*************************************************************************
   433  **************************************************************************
   434  ** Begin test compression hooks.
   435  */
   436  
   437  #ifdef HAVE_ZLIB
   438  #include <zlib.h>
   439  
   440  static int testZipBound(void *pCtx, int nSrc){
   441    return compressBound(nSrc);
   442  }
   443  
   444  static int testZipCompress(
   445    void *pCtx,                     /* Context pointer */
   446    char *aOut, int *pnOut,         /* OUT: Buffer containing compressed data */
   447    const char *aIn, int nIn        /* Buffer containing input data */
   448  ){
   449    uLongf n = *pnOut;              /* In/out buffer size for compress() */
   450    int rc;                         /* compress() return code */
   451   
   452    rc = compress((Bytef*)aOut, &n, (Bytef*)aIn, nIn);
   453    *pnOut = n;
   454    return (rc==Z_OK ? 0 : LSM_ERROR);
   455  }
   456  
   457  static int testZipUncompress(
   458    void *pCtx,                     /* Context pointer */
   459    char *aOut, int *pnOut,         /* OUT: Buffer containing uncompressed data */
   460    const char *aIn, int nIn        /* Buffer containing input data */
   461  ){
   462    uLongf n = *pnOut;              /* In/out buffer size for uncompress() */
   463    int rc;                         /* uncompress() return code */
   464  
   465    rc = uncompress((Bytef*)aOut, &n, (Bytef*)aIn, nIn);
   466    *pnOut = n;
   467    return (rc==Z_OK ? 0 : LSM_ERROR);
   468  }
   469  
   470  static int testConfigureCompression(lsm_db *pDb){
   471    static lsm_compress zip = {
   472      0,                            /* Context pointer (unused) */
   473      1,                            /* Id value */
   474      testZipBound,                 /* xBound method */
   475      testZipCompress,              /* xCompress method */
   476      testZipUncompress             /* xUncompress method */
   477    };
   478    return lsm_config(pDb, LSM_CONFIG_SET_COMPRESSION, &zip);
   479  }
   480  #endif /* ifdef HAVE_ZLIB */
   481  
   482  /*
   483  ** End test compression hooks.
   484  **************************************************************************
   485  *************************************************************************/
   486  
   487  static int test_lsm_close(TestDb *pTestDb){
   488    int i;
   489    int rc = LSM_OK;
   490    LsmDb *pDb = (LsmDb *)pTestDb;
   491  
   492    lsm_csr_close(pDb->pCsr);
   493    lsm_close(pDb->db);
   494  
   495    /* If this is a multi-threaded database, wait on the worker threads. */
   496    mt_shutdown(pDb);
   497    for(i=0; i<pDb->nWorker && rc==LSM_OK; i++){
   498      rc = pDb->aWorker[i].worker_rc;
   499    }
   500  
   501    for(i=0; i<pDb->aFile[0].nSector; i++){
   502      testFree(pDb->aFile[0].aSector[i].aOld);
   503    }
   504    testFree(pDb->aFile[0].aSector);
   505    for(i=0; i<pDb->aFile[1].nSector; i++){
   506      testFree(pDb->aFile[1].aSector[i].aOld);
   507    }
   508    testFree(pDb->aFile[1].aSector);
   509  
   510    memset(pDb, sizeof(LsmDb), 0x11);
   511    testFree((char *)pDb->pBuf);
   512    testFree((char *)pDb);
   513    return rc;
   514  }
   515  
   516  static void mt_signal_worker(LsmDb*, int);
   517  
   518  static int waitOnCheckpointer(LsmDb *pDb, lsm_db *db){
   519    int nSleep = 0;
   520    int nKB;
   521    int rc;
   522  
   523    do {
   524      nKB = 0;
   525      rc = lsm_info(db, LSM_INFO_CHECKPOINT_SIZE, &nKB);
   526      if( rc!=LSM_OK || nKB<pDb->nMtMaxCkpt ) break;
   527  #ifdef LSM_MUTEX_PTHREADS
   528      mt_signal_worker(pDb, 
   529          (pDb->eMode==LSMTEST_MODE_BACKGROUND_CKPT ? 0 : 1)
   530      );
   531  #endif
   532      usleep(5000);
   533      nSleep += 5;
   534    }while( 1 );
   535  
   536  #if 0
   537      if( nSleep ) printf("# waitOnCheckpointer(): nSleep=%d\n", nSleep);
   538  #endif
   539  
   540    return rc;
   541  }
   542  
   543  static int waitOnWorker(LsmDb *pDb){
   544    int rc;
   545    int nLimit = -1;
   546    int nSleep = 0;
   547  
   548    rc = lsm_config(pDb->db, LSM_CONFIG_AUTOFLUSH, &nLimit);
   549    do {
   550      int nOld, nNew, rc2;
   551      rc2 = lsm_info(pDb->db, LSM_INFO_TREE_SIZE, &nOld, &nNew);
   552      if( rc2!=LSM_OK ) return rc2;
   553      if( nOld==0 || nNew<(nLimit/2) ) break;
   554  #ifdef LSM_MUTEX_PTHREADS
   555      mt_signal_worker(pDb, 0);
   556  #endif
   557      usleep(5000);
   558      nSleep += 5;
   559    }while( 1 );
   560  
   561  #if 0
   562    if( nSleep ) printf("# waitOnWorker(): nSleep=%d\n", nSleep);
   563  #endif
   564  
   565    return rc;
   566  }
   567  
   568  static int test_lsm_write(
   569    TestDb *pTestDb, 
   570    void *pKey, 
   571    int nKey, 
   572    void *pVal,
   573    int nVal
   574  ){
   575    LsmDb *pDb = (LsmDb *)pTestDb;
   576    int rc = LSM_OK;
   577  
   578    if( pDb->eMode==LSMTEST_MODE_BACKGROUND_CKPT ){
   579      rc = waitOnCheckpointer(pDb, pDb->db);
   580    }else if( 
   581        pDb->eMode==LSMTEST_MODE_BACKGROUND_WORK
   582     || pDb->eMode==LSMTEST_MODE_BACKGROUND_BOTH 
   583    ){
   584      rc = waitOnWorker(pDb);
   585    }
   586  
   587    if( rc==LSM_OK ){
   588      rc = lsm_insert(pDb->db, pKey, nKey, pVal, nVal);
   589    }
   590    return rc;
   591  }
   592  
   593  static int test_lsm_delete(TestDb *pTestDb, void *pKey, int nKey){
   594    LsmDb *pDb = (LsmDb *)pTestDb;
   595    return lsm_delete(pDb->db, pKey, nKey);
   596  }
   597  
   598  static int test_lsm_delete_range(
   599    TestDb *pTestDb, 
   600    void *pKey1, int nKey1,
   601    void *pKey2, int nKey2
   602  ){
   603    LsmDb *pDb = (LsmDb *)pTestDb;
   604    return lsm_delete_range(pDb->db, pKey1, nKey1, pKey2, nKey2);
   605  }
   606  
   607  static int test_lsm_fetch(
   608    TestDb *pTestDb, 
   609    void *pKey, 
   610    int nKey, 
   611    void **ppVal, 
   612    int *pnVal
   613  ){
   614    int rc;
   615    LsmDb *pDb = (LsmDb *)pTestDb;
   616    lsm_cursor *csr;
   617  
   618    if( pKey==0 ) return LSM_OK;
   619  
   620    rc = lsm_csr_open(pDb->db, &csr);
   621    if( rc!=LSM_OK ) return rc;
   622  
   623    rc = lsm_csr_seek(csr, pKey, nKey, LSM_SEEK_EQ);
   624    if( rc==LSM_OK ){
   625      if( lsm_csr_valid(csr) ){
   626        const void *pVal; int nVal;
   627        rc = lsm_csr_value(csr, &pVal, &nVal);
   628        if( nVal>pDb->nBuf ){
   629          testFree(pDb->pBuf);
   630          pDb->pBuf = testMalloc(nVal*2);
   631          pDb->nBuf = nVal*2;
   632        }
   633        memcpy(pDb->pBuf, pVal, nVal);
   634        *ppVal = pDb->pBuf;
   635        *pnVal = nVal;
   636      }else{
   637        *ppVal = 0;
   638        *pnVal = -1;
   639      }
   640    }
   641    lsm_csr_close(csr);
   642    return rc;
   643  }
   644  
   645  static int test_lsm_scan(
   646    TestDb *pTestDb,
   647    void *pCtx,
   648    int bReverse,
   649    void *pFirst, int nFirst,
   650    void *pLast, int nLast,
   651    void (*xCallback)(void *, void *, int , void *, int)
   652  ){
   653    LsmDb *pDb = (LsmDb *)pTestDb;
   654    lsm_cursor *csr;
   655    int rc;
   656  
   657    rc = lsm_csr_open(pDb->db, &csr);
   658    if( rc!=LSM_OK ) return rc;
   659  
   660    if( bReverse ){
   661      if( pLast ){
   662        rc = lsm_csr_seek(csr, pLast, nLast, LSM_SEEK_LE);
   663      }else{
   664        rc = lsm_csr_last(csr);
   665      }
   666    }else{
   667      if( pFirst ){
   668        rc = lsm_csr_seek(csr, pFirst, nFirst, LSM_SEEK_GE);
   669      }else{
   670        rc = lsm_csr_first(csr);
   671      }
   672    }
   673  
   674    while( rc==LSM_OK && lsm_csr_valid(csr) ){
   675      const void *pKey; int nKey;
   676      const void *pVal; int nVal;
   677      int cmp;
   678  
   679      lsm_csr_key(csr, &pKey, &nKey);
   680      lsm_csr_value(csr, &pVal, &nVal);
   681  
   682      if( bReverse && pFirst ){
   683        cmp = memcmp(pFirst, pKey, MIN(nKey, nFirst));
   684        if( cmp>0 || (cmp==0 && nFirst>nKey) ) break;
   685      }else if( bReverse==0 && pLast ){
   686        cmp = memcmp(pLast, pKey, MIN(nKey, nLast));
   687        if( cmp<0 || (cmp==0 && nLast<nKey) ) break;
   688      }
   689  
   690      xCallback(pCtx, (void *)pKey, nKey, (void *)pVal, nVal);
   691  
   692      if( bReverse ){
   693        rc = lsm_csr_prev(csr);
   694      }else{
   695        rc = lsm_csr_next(csr);
   696      }
   697    }
   698  
   699    lsm_csr_close(csr);
   700    return rc;
   701  }
   702  
   703  static int test_lsm_begin(TestDb *pTestDb, int iLevel){
   704    int rc = LSM_OK;
   705    LsmDb *pDb = (LsmDb *)pTestDb;
   706  
   707    /* iLevel==0 is a no-op. */
   708    if( iLevel==0 ) return 0;
   709  
   710    if( pDb->pCsr==0 ) rc = lsm_csr_open(pDb->db, &pDb->pCsr);
   711    if( rc==LSM_OK && iLevel>1 ){
   712      rc = lsm_begin(pDb->db, iLevel-1);
   713    }
   714  
   715    return rc;
   716  }
   717  static int test_lsm_commit(TestDb *pTestDb, int iLevel){
   718    LsmDb *pDb = (LsmDb *)pTestDb;
   719  
   720    /* If iLevel==0, close any open read transaction */
   721    if( iLevel==0 && pDb->pCsr ){
   722      lsm_csr_close(pDb->pCsr);
   723      pDb->pCsr = 0;
   724    }
   725  
   726    /* If iLevel==0, close any open read transaction */
   727    return lsm_commit(pDb->db, MAX(0, iLevel-1));
   728  }
   729  static int test_lsm_rollback(TestDb *pTestDb, int iLevel){
   730    LsmDb *pDb = (LsmDb *)pTestDb;
   731  
   732    /* If iLevel==0, close any open read transaction */
   733    if( iLevel==0 && pDb->pCsr ){
   734      lsm_csr_close(pDb->pCsr);
   735      pDb->pCsr = 0;
   736    }
   737  
   738    return lsm_rollback(pDb->db, MAX(0, iLevel-1));
   739  }
   740  
   741  /*
   742  ** A log message callback registered with lsm connections. Prints all 
   743  ** messages to stderr.
   744  */
   745  static void xLog(void *pCtx, int rc, const char *z){
   746    unused_parameter(rc);
   747    /* fprintf(stderr, "lsm: rc=%d \"%s\"\n", rc, z); */
   748    if( pCtx ) fprintf(stderr, "%s: ", (char *)pCtx);
   749    fprintf(stderr, "%s\n", z);
   750    fflush(stderr);
   751  }
   752  
   753  static void xWorkHook(lsm_db *db, void *pArg){
   754    LsmDb *p = (LsmDb *)pArg;
   755    if( p->xWork ) p->xWork(db, p->pWorkCtx);
   756  }
   757  
   758  #define TEST_NO_RECOVERY -1
   759  #define TEST_COMPRESSION -3
   760  
   761  #define TEST_MT_MODE     -2
   762  #define TEST_MT_MIN_CKPT -4
   763  #define TEST_MT_MAX_CKPT -5
   764  
   765  int test_lsm_config_str(
   766    LsmDb *pLsm,
   767    lsm_db *db, 
   768    int bWorker,
   769    const char *zStr,
   770    int *pnThread
   771  ){
   772    struct CfgParam {
   773      const char *zParam;
   774      int bWorker;
   775      int eParam;
   776    } aParam[] = {
   777      { "autoflush",        0, LSM_CONFIG_AUTOFLUSH },
   778      { "page_size",        0, LSM_CONFIG_PAGE_SIZE },
   779      { "block_size",       0, LSM_CONFIG_BLOCK_SIZE },
   780      { "safety",           0, LSM_CONFIG_SAFETY },
   781      { "autowork",         0, LSM_CONFIG_AUTOWORK },
   782      { "autocheckpoint",   0, LSM_CONFIG_AUTOCHECKPOINT },
   783      { "mmap",             0, LSM_CONFIG_MMAP },
   784      { "use_log",          0, LSM_CONFIG_USE_LOG },
   785      { "automerge",        0, LSM_CONFIG_AUTOMERGE },
   786      { "max_freelist",     0, LSM_CONFIG_MAX_FREELIST },
   787      { "multi_proc",       0, LSM_CONFIG_MULTIPLE_PROCESSES },
   788      { "worker_automerge", 1, LSM_CONFIG_AUTOMERGE },
   789      { "test_no_recovery", 0, TEST_NO_RECOVERY },
   790      { "bg_min_ckpt",      0, TEST_NO_RECOVERY },
   791  
   792      { "mt_mode",          0, TEST_MT_MODE },
   793      { "mt_min_ckpt",      0, TEST_MT_MIN_CKPT },
   794      { "mt_max_ckpt",      0, TEST_MT_MAX_CKPT },
   795  
   796  #ifdef HAVE_ZLIB
   797      { "compression",      0, TEST_COMPRESSION },
   798  #endif
   799      { 0, 0 }
   800    };
   801    const char *z = zStr;
   802    int nThread = 1;
   803  
   804    if( zStr==0 ) return 0;
   805  
   806    assert( db );
   807    while( z[0] ){
   808      const char *zStart;
   809  
   810      /* Skip whitespace */
   811      while( *z==' ' ) z++;
   812      zStart = z;
   813  
   814      while( *z && *z!='=' ) z++;
   815      if( *z ){
   816        int eParam;
   817        int i;
   818        int iVal;
   819        int iMul = 1;
   820        int rc;
   821        char zParam[32];
   822        int nParam = z-zStart;
   823        if( nParam==0 || nParam>sizeof(zParam)-1 ) goto syntax_error;
   824  
   825        memcpy(zParam, zStart, nParam);
   826        zParam[nParam] = '\0';
   827        rc = testArgSelect(aParam, "param", zParam, &i);
   828        if( rc!=0 ) return rc;
   829        eParam = aParam[i].eParam;
   830  
   831        z++;
   832        zStart = z;
   833        while( *z>='0' && *z<='9' ) z++;
   834        if( *z=='k' || *z=='K' ){
   835          iMul = 1;
   836          z++;
   837        }else if( *z=='M' || *z=='M' ){
   838          iMul = 1024;
   839          z++;
   840        }
   841        nParam = z-zStart;
   842        if( nParam==0 || nParam>sizeof(zParam)-1 ) goto syntax_error;
   843        memcpy(zParam, zStart, nParam);
   844        zParam[nParam] = '\0';
   845        iVal = atoi(zParam) * iMul;
   846  
   847        if( eParam>0 ){
   848          if( bWorker || aParam[i].bWorker==0 ){
   849            lsm_config(db, eParam, &iVal);
   850          }
   851        }else{
   852          switch( eParam ){
   853            case TEST_NO_RECOVERY:
   854              if( pLsm ) pLsm->bNoRecovery = iVal;
   855              break;
   856            case TEST_MT_MODE:
   857              if( pLsm ) nThread = iVal;
   858              break;
   859            case TEST_MT_MIN_CKPT:
   860              if( pLsm && iVal>0 ) pLsm->nMtMinCkpt = iVal*1024;
   861              break;
   862            case TEST_MT_MAX_CKPT:
   863              if( pLsm && iVal>0 ) pLsm->nMtMaxCkpt = iVal*1024;
   864              break;
   865  #ifdef HAVE_ZLIB
   866            case TEST_COMPRESSION:
   867              testConfigureCompression(db);
   868              break;
   869  #endif
   870          }
   871        }
   872      }else if( z!=zStart ){
   873        goto syntax_error;
   874      }
   875    }
   876  
   877    if( pnThread ) *pnThread = nThread;
   878    if( pLsm && pLsm->nMtMaxCkpt < pLsm->nMtMinCkpt ){
   879      pLsm->nMtMinCkpt = pLsm->nMtMaxCkpt;
   880    }
   881  
   882    return 0;
   883   syntax_error:
   884    testPrintError("syntax error at: \"%s\"\n", z);
   885    return 1;
   886  }
   887  
   888  int tdb_lsm_config_str(TestDb *pDb, const char *zStr){
   889    int rc = 0;
   890    if( tdb_lsm(pDb) ){
   891  #ifdef LSM_MUTEX_PTHREADS
   892      int i;
   893  #endif
   894      LsmDb *pLsm = (LsmDb *)pDb;
   895  
   896      rc = test_lsm_config_str(pLsm, pLsm->db, 0, zStr, 0);
   897  #ifdef LSM_MUTEX_PTHREADS
   898      for(i=0; rc==0 && i<pLsm->nWorker; i++){
   899        rc = test_lsm_config_str(0, pLsm->aWorker[i].pWorker, 1, zStr, 0);
   900      }
   901  #endif
   902    }
   903    return rc;
   904  }
   905  
   906  int tdb_lsm_configure(lsm_db *db, const char *zConfig){
   907    return test_lsm_config_str(0, db, 0, zConfig, 0);
   908  }
   909  
   910  static int testLsmStartWorkers(LsmDb *, int, const char *, const char *);
   911  
   912  static int testLsmOpen(
   913    const char *zCfg,
   914    const char *zFilename, 
   915    int bClear, 
   916    TestDb **ppDb
   917  ){
   918    static const DatabaseMethods LsmMethods = {
   919      test_lsm_close,
   920      test_lsm_write,
   921      test_lsm_delete,
   922      test_lsm_delete_range,
   923      test_lsm_fetch,
   924      test_lsm_scan,
   925      test_lsm_begin,
   926      test_lsm_commit,
   927      test_lsm_rollback
   928    };
   929  
   930    int rc;
   931    int nFilename;
   932    LsmDb *pDb;
   933  
   934    /* If the bClear flag is set, delete any existing database. */
   935    assert( zFilename);
   936    if( bClear ) testDeleteLsmdb(zFilename);
   937    nFilename = strlen(zFilename);
   938  
   939    pDb = (LsmDb *)testMalloc(sizeof(LsmDb) + nFilename + 1);
   940    memset(pDb, 0, sizeof(LsmDb));
   941    pDb->base.pMethods = &LsmMethods;
   942    pDb->zName = (char *)&pDb[1];
   943    memcpy(pDb->zName, zFilename, nFilename + 1);
   944  
   945    /* Default the sector size used for crash simulation to 512 bytes. 
   946    ** Todo: There should be an OS method to obtain this value - just as
   947    ** there is in SQLite. For now, LSM assumes that it is smaller than
   948    ** the page size (default 4KB).
   949    */
   950    pDb->szSector = 256;
   951  
   952    /* Default values for the mt_min_ckpt and mt_max_ckpt parameters. */
   953    pDb->nMtMinCkpt = LSMTEST_DFLT_MT_MIN_CKPT;
   954    pDb->nMtMaxCkpt = LSMTEST_DFLT_MT_MAX_CKPT;
   955  
   956    memcpy(&pDb->env, tdb_lsm_env(), sizeof(lsm_env));
   957    pDb->env.pVfsCtx = (void *)pDb;
   958    pDb->env.xFullpath = testEnvFullpath;
   959    pDb->env.xOpen = testEnvOpen;
   960    pDb->env.xRead = testEnvRead;
   961    pDb->env.xWrite = testEnvWrite;
   962    pDb->env.xTruncate = testEnvTruncate;
   963    pDb->env.xSync = testEnvSync;
   964    pDb->env.xSectorSize = testEnvSectorSize;
   965    pDb->env.xRemap = testEnvRemap;
   966    pDb->env.xFileid = testEnvFileid;
   967    pDb->env.xClose = testEnvClose;
   968    pDb->env.xUnlink = testEnvUnlink;
   969    pDb->env.xLock = testEnvLock;
   970    pDb->env.xTestLock = testEnvTestLock;
   971    pDb->env.xShmBarrier = testEnvShmBarrier;
   972    pDb->env.xShmMap = testEnvShmMap;
   973    pDb->env.xShmUnmap = testEnvShmUnmap;
   974    pDb->env.xSleep = testEnvSleep;
   975  
   976    rc = lsm_new(&pDb->env, &pDb->db);
   977    if( rc==LSM_OK ){
   978      int nThread = 1;
   979      lsm_config_log(pDb->db, xLog, 0);
   980      lsm_config_work_hook(pDb->db, xWorkHook, (void *)pDb);
   981  
   982      rc = test_lsm_config_str(pDb, pDb->db, 0, zCfg, &nThread);
   983      if( rc==LSM_OK ) rc = lsm_open(pDb->db, zFilename);
   984  
   985      pDb->eMode = nThread;
   986  #ifdef LSM_MUTEX_PTHREADS
   987      if( rc==LSM_OK && nThread>1 ){
   988        testLsmStartWorkers(pDb, nThread, zFilename, zCfg);
   989      }
   990  #endif
   991  
   992      if( rc!=LSM_OK ){
   993        test_lsm_close((TestDb *)pDb);
   994        pDb = 0;
   995      }
   996    }
   997  
   998    *ppDb = (TestDb *)pDb;
   999    return rc;
  1000  }
  1001  
  1002  int test_lsm_open(
  1003    const char *zSpec, 
  1004    const char *zFilename, 
  1005    int bClear, 
  1006    TestDb **ppDb
  1007  ){
  1008    return testLsmOpen(zSpec, zFilename, bClear, ppDb);
  1009  }
  1010  
  1011  int test_lsm_small_open(
  1012    const char *zSpec, 
  1013    const char *zFile, 
  1014    int bClear, 
  1015    TestDb **ppDb
  1016  ){
  1017    const char *zCfg = "page_size=256 block_size=64 mmap=1024";
  1018    return testLsmOpen(zCfg, zFile, bClear, ppDb);
  1019  }
  1020  
  1021  int test_lsm_lomem_open(
  1022    const char *zSpec, 
  1023    const char *zFilename, 
  1024    int bClear, 
  1025    TestDb **ppDb
  1026  ){
  1027      /* "max_freelist=4 autocheckpoint=32" */
  1028    const char *zCfg = 
  1029      "page_size=256 block_size=64 autoflush=16 "
  1030      "autocheckpoint=32"
  1031      "mmap=0 "
  1032    ;
  1033    return testLsmOpen(zCfg, zFilename, bClear, ppDb);
  1034  }
  1035  
  1036  int test_lsm_zip_open(
  1037    const char *zSpec, 
  1038    const char *zFilename, 
  1039    int bClear, 
  1040    TestDb **ppDb
  1041  ){
  1042    const char *zCfg = 
  1043      "page_size=256 block_size=64 autoflush=16 "
  1044      "autocheckpoint=32 compression=1 mmap=0 "
  1045    ;
  1046    return testLsmOpen(zCfg, zFilename, bClear, ppDb);
  1047  }
  1048  
  1049  lsm_db *tdb_lsm(TestDb *pDb){
  1050    if( pDb->pMethods->xClose==test_lsm_close ){
  1051      return ((LsmDb *)pDb)->db;
  1052    }
  1053    return 0;
  1054  }
  1055  
  1056  int tdb_lsm_multithread(TestDb *pDb){
  1057    int ret = 0;
  1058    if( tdb_lsm(pDb) ){
  1059      ret = ((LsmDb*)pDb)->eMode!=LSMTEST_MODE_SINGLETHREAD;
  1060    }
  1061    return ret;
  1062  }
  1063  
  1064  void tdb_lsm_enable_log(TestDb *pDb, int bEnable){
  1065    lsm_db *db = tdb_lsm(pDb);
  1066    if( db ){
  1067      lsm_config_log(db, (bEnable ? xLog : 0), (void *)"client");
  1068    }
  1069  }
  1070  
  1071  void tdb_lsm_application_crash(TestDb *pDb){
  1072    if( tdb_lsm(pDb) ){
  1073      LsmDb *p = (LsmDb *)pDb;
  1074      p->bCrashed = 1;
  1075    }
  1076  }
  1077  
  1078  void tdb_lsm_prepare_system_crash(TestDb *pDb){
  1079    if( tdb_lsm(pDb) ){
  1080      LsmDb *p = (LsmDb *)pDb;
  1081      p->bPrepareCrash = 1;
  1082    }
  1083  }
  1084  
  1085  void tdb_lsm_system_crash(TestDb *pDb){
  1086    if( tdb_lsm(pDb) ){
  1087      LsmDb *p = (LsmDb *)pDb;
  1088      p->bCrashed = 1;
  1089      doSystemCrash(p);
  1090    }
  1091  }
  1092  
  1093  void tdb_lsm_safety(TestDb *pDb, int eMode){
  1094    assert( eMode==LSM_SAFETY_OFF 
  1095         || eMode==LSM_SAFETY_NORMAL 
  1096         || eMode==LSM_SAFETY_FULL 
  1097    );
  1098    if( tdb_lsm(pDb) ){
  1099      int iParam = eMode;
  1100      LsmDb *p = (LsmDb *)pDb;
  1101      lsm_config(p->db, LSM_CONFIG_SAFETY, &iParam);
  1102    }
  1103  }
  1104  
  1105  void tdb_lsm_prepare_sync_crash(TestDb *pDb, int iSync){
  1106    assert( iSync>0 );
  1107    if( tdb_lsm(pDb) ){
  1108      LsmDb *p = (LsmDb *)pDb;
  1109      p->nAutoCrash = iSync;
  1110      p->bPrepareCrash = 1;
  1111    }
  1112  }
  1113  
  1114  void tdb_lsm_config_work_hook(
  1115    TestDb *pDb, 
  1116    void (*xWork)(lsm_db *, void *), 
  1117    void *pWorkCtx
  1118  ){
  1119    if( tdb_lsm(pDb) ){
  1120      LsmDb *p = (LsmDb *)pDb;
  1121      p->xWork = xWork;
  1122      p->pWorkCtx = pWorkCtx;
  1123    }
  1124  }
  1125  
  1126  void tdb_lsm_write_hook(
  1127    TestDb *pDb, 
  1128    void (*xWrite)(void *, int, lsm_i64, int, int),
  1129    void *pWriteCtx
  1130  ){
  1131    if( tdb_lsm(pDb) ){
  1132      LsmDb *p = (LsmDb *)pDb;
  1133      p->xWriteHook = xWrite;
  1134      p->pWriteCtx = pWriteCtx;
  1135    }
  1136  }
  1137  
  1138  int tdb_lsm_open(const char *zCfg, const char *zDb, int bClear, TestDb **ppDb){
  1139    return testLsmOpen(zCfg, zDb, bClear, ppDb);
  1140  }
  1141  
  1142  #ifdef LSM_MUTEX_PTHREADS
  1143  
  1144  /*
  1145  ** Signal worker thread iWorker that there may be work to do.
  1146  */
  1147  static void mt_signal_worker(LsmDb *pDb, int iWorker){
  1148    LsmWorker *p = &pDb->aWorker[iWorker];
  1149    pthread_mutex_lock(&p->worker_mutex);
  1150    p->bDoWork = 1;
  1151    pthread_cond_signal(&p->worker_cond);
  1152    pthread_mutex_unlock(&p->worker_mutex);
  1153  }
  1154  
  1155  /*
  1156  ** This routine is used as the main() for all worker threads.
  1157  */
  1158  static void *worker_main(void *pArg){
  1159    LsmWorker *p = (LsmWorker *)pArg;
  1160    lsm_db *pWorker;                /* Connection to access db through */
  1161  
  1162    pthread_mutex_lock(&p->worker_mutex);
  1163    while( (pWorker = p->pWorker) ){
  1164      int rc = LSM_OK;
  1165  
  1166      /* Do some work. If an error occurs, exit. */
  1167  
  1168      pthread_mutex_unlock(&p->worker_mutex);
  1169      if( p->eType==LSMTEST_THREAD_CKPT ){
  1170        int nKB = 0;
  1171        rc = lsm_info(pWorker, LSM_INFO_CHECKPOINT_SIZE, &nKB);
  1172        if( rc==LSM_OK && nKB>=p->pDb->nMtMinCkpt ){
  1173          rc = lsm_checkpoint(pWorker, 0);
  1174        }
  1175      }else{
  1176        int nWrite;
  1177        do {
  1178  
  1179          if( p->eType==LSMTEST_THREAD_WORKER ){
  1180            waitOnCheckpointer(p->pDb, pWorker);
  1181          }
  1182  
  1183          nWrite = 0;
  1184          rc = lsm_work(pWorker, 0, 256, &nWrite);
  1185  
  1186          if( p->eType==LSMTEST_THREAD_WORKER && nWrite ){
  1187            mt_signal_worker(p->pDb, 1);
  1188          }
  1189        }while( nWrite && p->pWorker );
  1190      }
  1191      pthread_mutex_lock(&p->worker_mutex);
  1192  
  1193      if( rc!=LSM_OK && rc!=LSM_BUSY ){
  1194        p->worker_rc = rc;
  1195        break;
  1196      }
  1197  
  1198      /* The thread will wake up when it is signaled either because another
  1199      ** thread has created some work for this one or because the connection
  1200      ** is being closed.  */
  1201      if( p->pWorker && p->bDoWork==0 ){
  1202        pthread_cond_wait(&p->worker_cond, &p->worker_mutex);
  1203      }
  1204      p->bDoWork = 0;
  1205    }
  1206    pthread_mutex_unlock(&p->worker_mutex);
  1207    
  1208    return 0;
  1209  }
  1210  
  1211  
  1212  static void mt_stop_worker(LsmDb *pDb, int iWorker){
  1213    LsmWorker *p = &pDb->aWorker[iWorker];
  1214    if( p->pWorker ){
  1215      void *pDummy;
  1216      lsm_db *pWorker;
  1217  
  1218      /* Signal the worker to stop */
  1219      pthread_mutex_lock(&p->worker_mutex);
  1220      pWorker = p->pWorker;
  1221      p->pWorker = 0;
  1222      pthread_cond_signal(&p->worker_cond);
  1223      pthread_mutex_unlock(&p->worker_mutex);
  1224  
  1225      /* Join the worker thread. */
  1226      pthread_join(p->worker_thread, &pDummy);
  1227  
  1228      /* Free resources allocated in mt_start_worker() */
  1229      pthread_cond_destroy(&p->worker_cond);
  1230      pthread_mutex_destroy(&p->worker_mutex);
  1231      lsm_close(pWorker);
  1232    }
  1233  }
  1234  
  1235  static void mt_shutdown(LsmDb *pDb){
  1236    int i;
  1237    for(i=0; i<pDb->nWorker; i++){
  1238      mt_stop_worker(pDb, i);
  1239    }
  1240  }
  1241  
  1242  /*
  1243  ** This callback is invoked by LSM when the client database writes to
  1244  ** the database file (i.e. to flush the contents of the in-memory tree).
  1245  ** This implies there may be work to do on the database, so signal
  1246  ** the worker threads.
  1247  */
  1248  static void mt_client_work_hook(lsm_db *db, void *pArg){
  1249    LsmDb *pDb = (LsmDb *)pArg;     /* LsmDb database handle */
  1250  
  1251    /* Invoke the user level work-hook, if any. */
  1252    if( pDb->xWork ) pDb->xWork(db, pDb->pWorkCtx);
  1253  
  1254    /* Wake up worker thread 0. */
  1255    mt_signal_worker(pDb, 0);
  1256  }
  1257  
  1258  static void mt_worker_work_hook(lsm_db *db, void *pArg){
  1259    LsmDb *pDb = (LsmDb *)pArg;     /* LsmDb database handle */
  1260  
  1261    /* Invoke the user level work-hook, if any. */
  1262    if( pDb->xWork ) pDb->xWork(db, pDb->pWorkCtx);
  1263  }
  1264  
  1265  /*
  1266  ** Launch worker thread iWorker for database connection pDb.
  1267  */
  1268  static int mt_start_worker(
  1269    LsmDb *pDb,                     /* Main database structure */
  1270    int iWorker,                    /* Worker number to start */
  1271    const char *zFilename,          /* File name of database to open */
  1272    const char *zCfg,               /* Connection configuration string */
  1273    int eType                       /* Type of worker thread */
  1274  ){
  1275    int rc = 0;                     /* Return code */
  1276    LsmWorker *p;                   /* Object to initialize */
  1277  
  1278    assert( iWorker<pDb->nWorker );
  1279    assert( eType==LSMTEST_THREAD_CKPT 
  1280         || eType==LSMTEST_THREAD_WORKER 
  1281         || eType==LSMTEST_THREAD_WORKER_AC 
  1282    );
  1283  
  1284    p = &pDb->aWorker[iWorker];
  1285    p->eType = eType;
  1286    p->pDb = pDb;
  1287  
  1288    /* Open the worker connection */
  1289    if( rc==0 ) rc = lsm_new(&pDb->env, &p->pWorker);
  1290    if( zCfg ){
  1291      test_lsm_config_str(pDb, p->pWorker, 1, zCfg, 0);
  1292    }
  1293    if( rc==0 ) rc = lsm_open(p->pWorker, zFilename);
  1294    lsm_config_log(p->pWorker, xLog, (void *)"worker");
  1295  
  1296    /* Configure the work-hook */
  1297    if( rc==0 ){
  1298      lsm_config_work_hook(p->pWorker, mt_worker_work_hook, (void *)pDb);
  1299    }
  1300  
  1301    if( eType==LSMTEST_THREAD_WORKER ){
  1302      test_lsm_config_str(0, p->pWorker, 1, "autocheckpoint=0", 0);
  1303    }
  1304  
  1305    /* Kick off the worker thread. */
  1306    if( rc==0 ) rc = pthread_cond_init(&p->worker_cond, 0);
  1307    if( rc==0 ) rc = pthread_mutex_init(&p->worker_mutex, 0);
  1308    if( rc==0 ) rc = pthread_create(&p->worker_thread, 0, worker_main, (void *)p);
  1309  
  1310    return rc;
  1311  }
  1312  
  1313  
  1314  static int testLsmStartWorkers(
  1315    LsmDb *pDb, int eModel, const char *zFilename, const char *zCfg
  1316  ){
  1317    int rc;
  1318  
  1319    if( eModel<1 || eModel>4 ) return 1;
  1320    if( eModel==1 ) return 0;
  1321  
  1322    /* Configure a work-hook for the client connection. Worker 0 is signalled
  1323    ** every time the users connection writes to the database.  */
  1324    lsm_config_work_hook(pDb->db, mt_client_work_hook, (void *)pDb);
  1325  
  1326    /* Allocate space for two worker connections. They may not both be
  1327    ** used, but both are allocated.  */
  1328    pDb->aWorker = (LsmWorker *)testMalloc(sizeof(LsmWorker) * 2);
  1329    memset(pDb->aWorker, 0, sizeof(LsmWorker) * 2);
  1330  
  1331    switch( eModel ){
  1332      case LSMTEST_MODE_BACKGROUND_CKPT:
  1333        pDb->nWorker = 1;
  1334        test_lsm_config_str(0, pDb->db, 0, "autocheckpoint=0", 0);
  1335        rc = mt_start_worker(pDb, 0, zFilename, zCfg, LSMTEST_THREAD_CKPT);
  1336        break;
  1337  
  1338      case LSMTEST_MODE_BACKGROUND_WORK:
  1339        pDb->nWorker = 1;
  1340        test_lsm_config_str(0, pDb->db, 0, "autowork=0", 0);
  1341        rc = mt_start_worker(pDb, 0, zFilename, zCfg, LSMTEST_THREAD_WORKER_AC);
  1342        break;
  1343  
  1344      case LSMTEST_MODE_BACKGROUND_BOTH:
  1345        pDb->nWorker = 2;
  1346        test_lsm_config_str(0, pDb->db, 0, "autowork=0", 0);
  1347        rc = mt_start_worker(pDb, 0, zFilename, zCfg, LSMTEST_THREAD_WORKER);
  1348        if( rc==0 ){
  1349          rc = mt_start_worker(pDb, 1, zFilename, zCfg, LSMTEST_THREAD_CKPT);
  1350        }
  1351        break;
  1352    }
  1353  
  1354    return rc;
  1355  }
  1356  
  1357  
  1358  int test_lsm_mt2(
  1359    const char *zSpec, 
  1360    const char *zFilename, 
  1361    int bClear, 
  1362    TestDb **ppDb
  1363  ){
  1364    const char *zCfg = "mt_mode=2";
  1365    return testLsmOpen(zCfg, zFilename, bClear, ppDb);
  1366  }
  1367  
  1368  int test_lsm_mt3(
  1369    const char *zSpec, 
  1370    const char *zFilename, 
  1371    int bClear, 
  1372    TestDb **ppDb
  1373  ){
  1374    const char *zCfg = "mt_mode=4";
  1375    return testLsmOpen(zCfg, zFilename, bClear, ppDb);
  1376  }
  1377  
  1378  #else
  1379  static void mt_shutdown(LsmDb *pDb) { 
  1380    unused_parameter(pDb); 
  1381  }
  1382  int test_lsm_mt(const char *zFilename, int bClear, TestDb **ppDb){
  1383    unused_parameter(zFilename);
  1384    unused_parameter(bClear);
  1385    unused_parameter(ppDb);
  1386    testPrintError("threads unavailable - recompile with LSM_MUTEX_PTHREADS\n");
  1387    return 1;
  1388  }
  1389  #endif