/ Check-in [3794dcd3]
Login

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

Overview
Comment:Add external locking to test_async.c. There are still some tests to come. (CVS 4398)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 3794dcd31a74e90b181b336bf6a4c917bda526b8
User & Date: danielk1977 2007-09-04 18:28:44
Context
2007-09-04
22:31
Do not use the TryEnterCriticalSection API on windows since it is unavailable on some platforms. (CVS 4399) check-in: bf3d67d1 user: drh tags: trunk
18:28
Add external locking to test_async.c. There are still some tests to come. (CVS 4398) check-in: 3794dcd3 user: danielk1977 tags: trunk
15:38
Fix a problem whereby the *ppVtab output buffer passed to sqlite3_module.xConstruct() could be invalidated (freed) if a malloc() failure occured within a call to sqlite3_declare_vtab(). (CVS 4397) check-in: efd61df1 user: danielk1977 tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/test_async.c.

    66     66   ** stream that exceeds the I/O capability of the background writer thread,
    67     67   ** the queue of pending write operations will grow without bound until we
    68     68   ** run out of memory.  Users of this technique may want to keep track of
    69     69   ** the quantity of pending writes and stop accepting new write requests
    70     70   ** when the buffer gets to be too big.
    71     71   */
    72     72   
           73  +/* 
           74  +** If this symbol is defined, then file-system locks are obtained as
           75  +** required. This slows things down, but allows multiple processes
           76  +** to access the database concurrently.
           77  +*/
           78  +#define ENABLE_FILE_LOCKING
           79  +
    73     80   #include "sqliteInt.h"
    74     81   #include <tcl.h>
    75     82   
    76     83   /*
    77     84   ** This test uses pthreads and hence only works on unix and with
    78     85   ** a threadsafe build of SQLite.
    79     86   */
    80     87   #if OS_UNIX && SQLITE_THREADSAFE
           88  +
    81     89   
    82     90   /*
    83     91   ** This demo uses pthreads.  If you do not have a pthreads implementation
    84     92   ** for your operating system, you will need to recode the threading 
    85     93   ** logic.
    86     94   */
    87     95   #include <pthread.h>
................................................................................
    91     99   #define MIN(x,y) ((x)<(y)?(x):(y))
    92    100   #define MAX(x,y) ((x)>(y)?(x):(y))
    93    101   
    94    102   /* Forward references */
    95    103   typedef struct AsyncWrite AsyncWrite;
    96    104   typedef struct AsyncFile AsyncFile;
    97    105   typedef struct AsyncFileData AsyncFileData;
          106  +typedef struct AsyncFileLock AsyncFileLock;
          107  +typedef struct AsyncLock AsyncLock;
    98    108   
    99    109   /* Enable for debugging */
   100    110   static int sqlite3async_trace = 0;
   101    111   # define ASYNC_TRACE(X) if( sqlite3async_trace ) asyncTrace X
   102    112   static void asyncTrace(const char *zFormat, ...){
   103    113     char *z;
   104    114     va_list ap;
................................................................................
   128    138   **     xOpenXXX (three versions)
   129    139   **     xDelete
   130    140   **     xFileExists
   131    141   **     xSyncDirectory
   132    142   **
   133    143   ** File handle operations (invoked by SQLite thread):
   134    144   **
   135         -**         asyncWrite, asyncClose, asyncTruncate, asyncSync, 
   136         -**         asyncSetFullSync, asyncOpenDirectory.
          145  +**         asyncWrite, asyncClose, asyncTruncate, asyncSync 
   137    146   **    
   138    147   **     The operations above add an entry to the global write-op list. They
   139    148   **     prepare the entry, acquire the async.queueMutex momentarily while
   140    149   **     list pointers are  manipulated to insert the new entry, then release
   141    150   **     the mutex and signal the writer thread to wake up in case it happens
   142    151   **     to be asleep.
   143    152   **
................................................................................
   155    164   **    
   156    165   **     These primitives implement in-process locking using a hash table
   157    166   **     on the file name.  Files are locked correctly for connections coming
   158    167   **     from the same process.  But other processes cannot see these locks
   159    168   **     and will therefore not honor them.
   160    169   **
   161    170   **
   162         -**         asyncFileHandle.
   163         -**    
   164         -**     The sqlite3OsFileHandle() function is currently only used when 
   165         -**     debugging the pager module. Unless sqlite3OsClose() is called on the
   166         -**     file (shouldn't be possible for other reasons), the underlying 
   167         -**     implementations are safe to call without grabbing any mutex. So we just
   168         -**     go ahead and call it no matter what any other threads are doing.
   169         -**
   170         -**    
   171         -**         asyncSeek.
   172         -**
   173         -**     Calling this method just manipulates the AsyncFile.iOffset variable. 
   174         -**     Since this variable is never accessed by writer thread, this
   175         -**     function does not require the mutex.  Actual calls to OsSeek() take 
   176         -**     place just before OsWrite() or OsRead(), which are always protected by 
   177         -**     the mutex.
   178         -**
   179    171   ** The writer thread:
   180    172   **
   181    173   **     The async.writerMutex is used to make sure only there is only
   182    174   **     a single writer thread running at a time.
   183    175   **
   184    176   **     Inside the writer thread is a loop that works like this:
   185    177   **
................................................................................
   218    210   #ifndef SQLITE_ASYNC_TWO_FILEHANDLES
   219    211   /* #define SQLITE_ASYNC_TWO_FILEHANDLES 0 */
   220    212   #define SQLITE_ASYNC_TWO_FILEHANDLES 1
   221    213   #endif
   222    214   
   223    215   /*
   224    216   ** State information is held in the static variable "async" defined
   225         -** as follows:
          217  +** as the following structure.
          218  +**
          219  +** Both async.ioError and async.nFile are protected by async.queueMutex.
   226    220   */
   227    221   static struct TestAsyncStaticData {
   228    222     pthread_mutex_t queueMutex;  /* Mutex for access to write operation queue */
   229    223     pthread_mutex_t writerMutex; /* Prevents multiple writer threads */
   230    224     pthread_mutex_t lockMutex;   /* For access to aLock hash table */
   231    225     pthread_cond_t queueSignal;  /* For waking up sleeping writer thread */
   232    226     pthread_cond_t emptySignal;  /* Notify when the write queue is empty */
................................................................................
   250    244   #define ASYNC_NOOP          0
   251    245   #define ASYNC_WRITE         1
   252    246   #define ASYNC_SYNC          2
   253    247   #define ASYNC_TRUNCATE      3
   254    248   #define ASYNC_CLOSE         4
   255    249   #define ASYNC_DELETE        5
   256    250   #define ASYNC_OPENEXCLUSIVE 6
          251  +#define ASYNC_UNLOCK        7
   257    252   
   258    253   /* Names of opcodes.  Used for debugging only.
   259    254   ** Make sure these stay in sync with the macros above!
   260    255   */
   261    256   static const char *azOpcodeName[] = {
   262         -  "NOOP", "WRITE", "SYNC", "TRUNCATE", "CLOSE", "DELETE", "OPENEX"
          257  +  "NOOP", "WRITE", "SYNC", "TRUNCATE", "CLOSE", "DELETE", "OPENEX", "UNLOCK"
   263    258   };
   264    259   
   265    260   /*
   266    261   ** Entries on the write-op queue are instances of the AsyncWrite
   267    262   ** structure, defined here.
   268    263   **
   269    264   ** The interpretation of the iOffset and nByte variables varies depending 
................................................................................
   291    286   **     iOffset -> Contains the "syncDir" flag.
   292    287   **     nByte   -> Number of bytes of zBuf points to (file name).
   293    288   **
   294    289   ** ASYNC_OPENEXCLUSIVE:
   295    290   **     iOffset -> Value of "delflag".
   296    291   **     nByte   -> Number of bytes of zBuf points to (file name).
   297    292   **
          293  +** ASYNC_UNLOCK:
          294  +**     nByte   -> Argument to sqlite3OsUnlock().
          295  +**
   298    296   **
   299    297   ** For an ASYNC_WRITE operation, zBuf points to the data to write to the file. 
   300    298   ** This space is sqlite3_malloc()d along with the AsyncWrite structure in a
   301    299   ** single blob, so is deleted when sqlite3_free() is called on the parent 
   302    300   ** structure.
   303    301   */
   304    302   struct AsyncWrite {
................................................................................
   312    310   
   313    311   /*
   314    312   ** An instance of the following structure is allocated along with each
   315    313   ** AsyncFileData structure (see AsyncFileData.lock), but is only used if the
   316    314   ** file was opened with the SQLITE_OPEN_MAIN_DB.
   317    315   **
   318    316   ** The global async.aLock[] hash table maps from database file-name to a
   319         -** linked-list of AsyncLock structures corresponding to handles opened on the
   320         -** file. The AsyncLock structures are linked into the list when the file is
   321         -** opened and removed when it is closed. Mutex async.lockMutex must be held
   322         -** before accessing any AsyncLock structure or the async.aLock[] table.
          317  +** linked-list of AsyncFileLock structures corresponding to handles opened on
          318  +** the file. The AsyncFileLock structures are linked into the list when the
          319  +** file is opened and removed when it is closed. Mutex async.lockMutex must be
          320  +** held before accessing any AsyncFileLock structure or the async.aLock[]
          321  +** table.
   323    322   */
   324         -typedef struct AsyncLock AsyncLock;
          323  +struct AsyncFileLock {
          324  +  int eLock;                /* Internally visible lock state (sqlite pov) */
          325  +  int eAsyncLock;           /* Lock-state with write-queue unlock */
          326  +  AsyncFileLock *pNext;
          327  +};
          328  +
   325    329   struct AsyncLock {
          330  +  sqlite3_file *pFile;
   326    331     int eLock;
   327         -  AsyncLock *pNext;
          332  +  AsyncFileLock *pList;
   328    333   };
   329    334   
   330    335   /* 
   331    336   ** The AsyncFile structure is a subclass of sqlite3_file used for 
   332    337   ** asynchronous IO. 
   333    338   **
   334    339   ** All of the actual data for the structure is stored in the structure
................................................................................
   343    348     AsyncFileData *pData;
   344    349   };
   345    350   struct AsyncFileData {
   346    351     char *zName;               /* Underlying OS filename - used for debugging */
   347    352     int nName;                 /* Number of characters in zName */
   348    353     sqlite3_file *pBaseRead;   /* Read handle to the underlying Os file */
   349    354     sqlite3_file *pBaseWrite;  /* Write handle to the underlying Os file */
   350         -  AsyncLock lock;
          355  +  AsyncFileLock lock;
   351    356   };
   352    357   
   353    358   /*
   354    359   ** Add an entry to the end of the global write-op list. pWrite should point 
   355    360   ** to an AsyncWrite structure allocated using sqlite3_malloc().  The writer
   356    361   ** thread will call sqlite3_free() to free the structure after the specified
   357    362   ** operation has been completed.
................................................................................
   473    478   */
   474    479   static int asyncRead(sqlite3_file *pFile, void *zOut, int iAmt, i64 iOffset){
   475    480     AsyncFileData *p = ((AsyncFile *)pFile)->pData;
   476    481     int rc = SQLITE_OK;
   477    482     i64 filesize;
   478    483     int nRead;
   479    484     sqlite3_file *pBase = p->pBaseRead;
          485  +
          486  +  /* Grab the write queue mutex for the duration of the call */
          487  +  pthread_mutex_lock(&async.queueMutex);
   480    488   
   481    489     /* If an I/O error has previously occurred in this virtual file 
   482    490     ** system, then all subsequent operations fail.
   483    491     */
   484    492     if( async.ioError!=SQLITE_OK ){
   485         -    return async.ioError;
          493  +    rc = async.ioError;
          494  +    goto asyncread_out;
   486    495     }
   487    496   
   488         -  /* Grab the write queue mutex for the duration of the call */
   489         -  pthread_mutex_lock(&async.queueMutex);
   490         -
   491    497     if( pBase->pMethods ){
   492    498       rc = sqlite3OsFileSize(pBase, &filesize);
   493    499       if( rc!=SQLITE_OK ){
   494    500         goto asyncread_out;
   495    501       }
   496    502       nRead = MIN(filesize - iOffset, iAmt);
   497    503       if( nRead>0 ){
................................................................................
   587    593         }
   588    594       }
   589    595       *piSize = s;
   590    596     }
   591    597     pthread_mutex_unlock(&async.queueMutex);
   592    598     return rc;
   593    599   }
          600  +
          601  +/*
          602  +** Lock or unlock the actual file-system entry.
          603  +*/
          604  +static int getFileLock(AsyncLock *pLock){
          605  +  int rc = SQLITE_OK;
          606  +  AsyncFileLock *pIter;
          607  +  int eRequired = 0;
          608  +
          609  +  if( pLock->pFile ){
          610  +    for(pIter=pLock->pList; pIter; pIter=pIter->pNext){
          611  +      assert(pIter->eAsyncLock>=pIter->eLock);
          612  +      if( pIter->eAsyncLock>eRequired ){
          613  +        eRequired = pIter->eAsyncLock;
          614  +      }
          615  +    }
          616  +    if( eRequired>pLock->eLock ){
          617  +      rc = sqlite3OsLock(pLock->pFile, eRequired);
          618  +    }else if(eRequired<pLock->eLock){
          619  +      rc = sqlite3OsUnlock(pLock->pFile, eRequired);
          620  +    }
          621  +    if( rc==SQLITE_OK ){
          622  +      pLock->eLock = eRequired;
          623  +    }
          624  +  }
          625  +
          626  +  return rc;
          627  +}
   594    628   
   595    629   /*
   596    630   ** No disk locking is performed.  We keep track of locks locally in
   597    631   ** the async.aLock hash table.  Locking should appear to work the same
   598    632   ** as with standard (unmodified) SQLite as long as all connections 
   599    633   ** come from this one process.  Connections from external processes
   600    634   ** cannot see our internal hash table (obviously) and will thus not
................................................................................
   603    637   static int asyncLock(sqlite3_file *pFile, int eLock){
   604    638     int rc = SQLITE_OK;
   605    639     AsyncFileData *p = ((AsyncFile *)pFile)->pData;
   606    640   
   607    641     pthread_mutex_lock(&async.lockMutex);
   608    642     if( p->lock.eLock<eLock ){
   609    643       AsyncLock *pLock;
          644  +    AsyncFileLock *pIter;
   610    645       pLock = (AsyncLock *)sqlite3HashFind(&async.aLock, p->zName, p->nName);
   611         -    assert(pLock);
   612         -    for(/*no-op*/; pLock; pLock=pLock->pNext){
   613         -      if( pLock!=&p->lock && (
   614         -        (eLock==SQLITE_LOCK_EXCLUSIVE && pLock->eLock>=SQLITE_LOCK_SHARED) ||
   615         -        (eLock==SQLITE_LOCK_PENDING && pLock->eLock>=SQLITE_LOCK_RESERVED) ||
   616         -        (eLock==SQLITE_LOCK_RESERVED && pLock->eLock>=SQLITE_LOCK_RESERVED) ||
   617         -        (eLock==SQLITE_LOCK_SHARED && pLock->eLock>=SQLITE_LOCK_PENDING)
          646  +    assert(pLock && pLock->pList);
          647  +    for(pIter=pLock->pList; pIter; pIter=pIter->pNext){
          648  +      if( pIter!=&p->lock && (
          649  +        (eLock==SQLITE_LOCK_EXCLUSIVE && pIter->eLock>=SQLITE_LOCK_SHARED) ||
          650  +        (eLock==SQLITE_LOCK_PENDING && pIter->eLock>=SQLITE_LOCK_RESERVED) ||
          651  +        (eLock==SQLITE_LOCK_RESERVED && pIter->eLock>=SQLITE_LOCK_RESERVED) ||
          652  +        (eLock==SQLITE_LOCK_SHARED && pIter->eLock>=SQLITE_LOCK_PENDING)
   618    653         )){
   619    654           rc = SQLITE_BUSY;
   620    655         }
   621    656       }
   622    657       if( rc==SQLITE_OK ){
   623    658         p->lock.eLock = eLock;
          659  +      if( eLock>p->lock.eAsyncLock ){
          660  +        p->lock.eAsyncLock = eLock;
          661  +      }
          662  +    }
          663  +    assert(p->lock.eAsyncLock>=p->lock.eLock);
          664  +    if( rc==SQLITE_OK ){
          665  +      rc = getFileLock(pLock);
   624    666       }
   625    667     }
   626    668     pthread_mutex_unlock(&async.lockMutex);
   627    669   
   628    670     ASYNC_TRACE(("LOCK %d (%s) rc=%d\n", eLock, p->zName, rc));
   629    671     return rc;
   630    672   }
   631    673   static int asyncUnlock(sqlite3_file *pFile, int eLock){
   632    674     AsyncFileData *p = ((AsyncFile *)pFile)->pData;
   633         -  AsyncLock *pLock = &p->lock;
          675  +  AsyncFileLock *pLock = &p->lock;
   634    676     pthread_mutex_lock(&async.lockMutex);
   635    677     if( pLock->eLock>eLock ){
   636    678       pLock->eLock = eLock;
   637    679     }
   638    680     pthread_mutex_unlock(&async.lockMutex);
   639         -  return SQLITE_OK;
          681  +  return addNewAsyncWrite(p, ASYNC_UNLOCK, 0, eLock, 0);
   640    682   }
   641    683   
   642    684   /*
   643    685   ** This function is called when the pager layer first opens a database file
   644    686   ** and is checking for a hot-journal.
   645    687   */
   646    688   static int asyncCheckReservedLock(sqlite3_file *pFile){
   647    689     int ret = 0;
          690  +  AsyncFileLock *pIter;
   648    691     AsyncLock *pLock;
   649    692     AsyncFileData *p = ((AsyncFile *)pFile)->pData;
   650    693   
   651    694     pthread_mutex_lock(&async.lockMutex);
   652    695     pLock = (AsyncLock *)sqlite3HashFind(&async.aLock, p->zName, p->nName);
   653         -  for(/*no-op*/; pLock; pLock=pLock->pNext){
   654         -    if( pLock->eLock>=SQLITE_LOCK_RESERVED ){
          696  +  for(pIter=pLock->pList; pIter; pIter=pIter->pNext){
          697  +    if( pIter->eLock>=SQLITE_LOCK_RESERVED ){
   655    698         ret = 1;
   656    699       }
   657    700     }
   658    701     pthread_mutex_unlock(&async.lockMutex);
   659    702   
   660    703     ASYNC_TRACE(("CHECK-LOCK %d (%s)\n", ret, p->zName));
   661    704     return ret;
   662    705   }
   663    706   
   664    707   /* 
   665    708   ** This is a no-op, as the asynchronous backend does not support locking.
   666    709   */
   667    710   static int asyncFileControl(sqlite3_file *id, int op, void *pArg){
          711  +  switch( op ){
          712  +    case SQLITE_FCNTL_LOCKSTATE: {
          713  +      pthread_mutex_lock(&async.lockMutex);
          714  +      *(int*)pArg = ((AsyncFile*)id)->pData->lock.eLock;
          715  +      pthread_mutex_unlock(&async.lockMutex);
          716  +      return SQLITE_OK;
          717  +    }
          718  +  }
   668    719     return SQLITE_ERROR;
   669    720   }
   670    721   
   671    722   /* 
   672    723   ** Return the device characteristics and sector-size of the device. It
   673    724   ** is not tricky to implement these correctly, as this backend might 
   674    725   ** not have an open file handle at this point.
................................................................................
   709    760     sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
   710    761     AsyncFile *p = (AsyncFile *)pFile;
   711    762     int nName = strlen(zName)+1;
   712    763     int rc;
   713    764     int nByte;
   714    765     AsyncFileData *pData;
   715    766   
          767  +  AsyncLock *pLock = 0;
          768  +
   716    769     nByte = (
   717    770       sizeof(AsyncFileData) +        /* AsyncFileData structure */
   718    771       2 * pVfs->szOsFile +           /* AsyncFileData.pBaseRead and pBaseWrite */
   719    772       nName                          /* AsyncFileData.zName */
   720    773     ); 
   721    774     pData = sqlite3_malloc(nByte);
   722    775     if( !pData ){
................................................................................
   734    787       if( pOutFlags ) *pOutFlags = flags;
   735    788     }else{
   736    789       rc = sqlite3OsOpen(pVfs, zName, pData->pBaseRead, flags, pOutFlags);
   737    790       if( rc==SQLITE_OK && ((*pOutFlags)&SQLITE_OPEN_READWRITE) ){
   738    791         rc = sqlite3OsOpen(pVfs, zName, pData->pBaseWrite, flags, 0);
   739    792       }
   740    793     }
          794  +
          795  +  pthread_mutex_lock(&async.lockMutex);
          796  +
          797  +  if( rc==SQLITE_OK ){
          798  +    pLock = sqlite3HashFind(&async.aLock, pData->zName, pData->nName);
          799  +    if( !pLock ){
          800  +      pLock = sqlite3MallocZero(pVfs->szOsFile + sizeof(AsyncLock));
          801  +      if( pLock ){
          802  +#ifdef ENABLE_FILE_LOCKING
          803  +        if( flags&SQLITE_OPEN_MAIN_DB ){
          804  +          pLock->pFile = (sqlite3_file *)&pLock[1];
          805  +          rc = sqlite3OsOpen(pVfs, zName, pLock->pFile, flags, 0);
          806  +          if( rc!=SQLITE_OK ){
          807  +            sqlite3_free(pLock);
          808  +            pLock = 0;
          809  +          }
          810  +        }
          811  +#endif
          812  +        sqlite3HashInsert(
          813  +          &async.aLock, pData->zName, pData->nName, (void *)pLock
          814  +        );
          815  +      }else{
          816  +        rc = SQLITE_NOMEM;
          817  +      }
          818  +    }
          819  +  }
   741    820   
   742    821     if( rc==SQLITE_OK ){
   743    822       HashElem *pElem;
   744    823       p->pMethod = &async_methods;
   745    824       p->pData = pData;
   746    825       incrOpenFileCount();
   747    826   
   748         -    /* Link AsyncFileData.lock into the linked list of AsyncLock structures
   749         -    ** for this file. Obtain the async.lockMutex mutex before doing so.
          827  +    /* Link AsyncFileData.lock into the linked list of 
          828  +    ** AsyncFileLock structures for this file.
   750    829       */
   751         -    AsyncLock *pNext;
   752         -    pthread_mutex_lock(&async.lockMutex);
   753         -    pNext = sqlite3HashInsert(
   754         -        &async.aLock, pData->zName, pData->nName, (void *)&pData->lock
   755         -    );
   756         -    pData->lock.pNext = pNext;
          830  +    pData->lock.pNext = pLock->pList;
          831  +    pLock->pList = &pData->lock;
          832  +
   757    833       pElem = sqlite3HashFindElem(&async.aLock, pData->zName, pData->nName);
   758    834       pData->zName = (char *)sqliteHashKey(pElem);
   759         -    pthread_mutex_unlock(&async.lockMutex);
   760    835     }else{
   761    836       sqlite3OsClose(pData->pBaseRead);
   762    837       sqlite3OsClose(pData->pBaseWrite);
   763    838       sqlite3_free(pData);
   764    839     }
   765    840   
          841  +  pthread_mutex_unlock(&async.lockMutex);
   766    842     return rc;
   767    843   }
   768    844   
   769    845   /*
   770    846   ** Implementation of sqlite3OsDelete. Add an entry to the end of the 
   771    847   ** write-op queue to perform the delete.
   772    848   */
................................................................................
  1055   1131           ASYNC_TRACE(("TRUNCATE %s to %d bytes\n", 
  1056   1132                   p->pFileData->zName, p->iOffset));
  1057   1133           rc = sqlite3OsTruncate(pBase, p->iOffset);
  1058   1134           break;
  1059   1135   
  1060   1136         case ASYNC_CLOSE: {
  1061   1137           AsyncLock *pLock;
         1138  +        AsyncFileLock **ppIter;
  1062   1139           AsyncFileData *pData = p->pFileData;
  1063   1140           ASYNC_TRACE(("CLOSE %s\n", p->pFileData->zName));
  1064   1141           sqlite3OsClose(pData->pBaseWrite);
  1065   1142           sqlite3OsClose(pData->pBaseRead);
  1066   1143   
  1067         -        /* Unlink AsyncFileData.lock from the linked list of AsyncLock 
         1144  +        /* Unlink AsyncFileData.lock from the linked list of AsyncFileLock 
  1068   1145           ** structures for this file. Obtain the async.lockMutex mutex 
  1069   1146           ** before doing so.
  1070   1147           */
  1071   1148           pthread_mutex_lock(&async.lockMutex);
  1072   1149           pLock = sqlite3HashFind(&async.aLock, pData->zName, pData->nName);
  1073         -        if( pLock==&pData->lock ){
  1074         -          sqlite3HashInsert(
  1075         -              &async.aLock, pData->zName, pData->nName, (void *)pLock->pNext 
  1076         -          );
         1150  +        for(ppIter=&pLock->pList; *ppIter; ppIter=&((*ppIter)->pNext)){
         1151  +          if( (*ppIter)==&pData->lock ){
         1152  +            *ppIter = (*ppIter)->pNext;
         1153  +            break;
         1154  +          }
         1155  +        }
         1156  +        if( !pLock->pList ){
         1157  +          if( pLock->pFile ) sqlite3OsClose(pLock->pFile);
         1158  +          sqlite3_free(pLock);
         1159  +          sqlite3HashInsert(&async.aLock, pData->zName, pData->nName, 0);
  1077   1160           }else{
  1078         -          for( ; pLock && pLock->pNext!=&pData->lock; pLock=pLock->pNext);
  1079         -          if( pLock ){
  1080         -            pLock->pNext = pData->lock.pNext;
  1081         -          }
         1161  +          rc = getFileLock(pLock);
  1082   1162           }
  1083   1163           pthread_mutex_unlock(&async.lockMutex);
  1084   1164   
  1085   1165           sqlite3_free(pData);
  1086   1166           break;
  1087   1167         }
         1168  +
         1169  +      case ASYNC_UNLOCK: {
         1170  +        AsyncLock *pLock;
         1171  +        AsyncFileData *pData = p->pFileData;
         1172  +        int eLock = p->nByte;
         1173  +        pthread_mutex_lock(&async.lockMutex);
         1174  +        if( pData->lock.eAsyncLock>eLock ){
         1175  +          if( pData->lock.eLock>eLock ){
         1176  +            pData->lock.eAsyncLock = pData->lock.eLock;
         1177  +          }else{
         1178  +            pData->lock.eAsyncLock = eLock;
         1179  +          }
         1180  +        }
         1181  +        assert(pData->lock.eAsyncLock>=pData->lock.eLock);
         1182  +        pLock = sqlite3HashFind(&async.aLock, pData->zName, pData->nName);
         1183  +        rc = getFileLock(pLock);
         1184  +        pthread_mutex_unlock(&async.lockMutex);
         1185  +        break;
         1186  +      }
  1088   1187   
  1089   1188         case ASYNC_DELETE:
  1090   1189           ASYNC_TRACE(("DELETE %s\n", p->zBuf));
  1091   1190           rc = sqlite3OsDelete(pVfs, p->zBuf, (int)p->iOffset);
  1092   1191           break;
  1093   1192   
  1094   1193         case ASYNC_OPENEXCLUSIVE: {

Changes to test/async.test.

     2      2   #    May you do good and not evil.
     3      3   #    May you find forgiveness for yourself and forgive others.
     4      4   #    May you share freely, never taking more than you give.
     5      5   #
     6      6   #***********************************************************************
     7      7   # This file runs all tests.
     8      8   #
     9         -# $Id: async.test,v 1.8 2007/09/04 14:31:47 danielk1977 Exp $
            9  +# $Id: async.test,v 1.9 2007/09/04 18:28:44 danielk1977 Exp $
    10     10   
    11     11   
    12     12   if {[catch {sqlite3async_enable}]} {
    13     13     # The async logic is not built into this system
    14     14     return
    15     15   }
    16     16   
................................................................................
    28     28     select4.test
    29     29     insert.test
    30     30     insert2.test
    31     31     insert3.test
    32     32     trans.test
    33     33     lock.test
    34     34     lock3.test
           35  +  lock2.test
    35     36   }
           37  +# set INCLUDE lock4.test
    36     38   
    37     39   # Enable asynchronous IO.
    38     40   sqlite3async_enable 1
    39     41   
    40     42   rename do_test really_do_test
    41     43   proc do_test {name args} {
    42     44     uplevel really_do_test async_io-$name $args