/ Check-in [17ca684c]
Login

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

Overview
Comment:Add internal locking to the test_async.c backend. So that more than one connection may be used from within a single process. (CVS 4396)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 17ca684c124445f17d1e36c37e169056c5fd4569
User & Date: danielk1977 2007-09-04 14:31:47
Context
2007-09-04
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
14:31
Add internal locking to the test_async.c backend. So that more than one connection may be used from within a single process. (CVS 4396) check-in: 17ca684c user: danielk1977 tags: trunk
12:18
Clarify documentation on the return value from sqlite3_column_blob() for a zero-length BLOB. Clarify the documentation on what happens when you have a zeroblob() with a negative length. Additional test cases but no changes to code. Ticket #2623. (CVS 4395) check-in: 63ca02a5 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/hash.c.

     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This is the implementation of generic hash-tables
    13     13   ** used in SQLite.
    14     14   **
    15         -** $Id: hash.c,v 1.23 2007/09/03 15:03:21 danielk1977 Exp $
           15  +** $Id: hash.c,v 1.24 2007/09/04 14:31:47 danielk1977 Exp $
    16     16   */
    17     17   #include "sqliteInt.h"
    18     18   #include <assert.h>
    19     19   
    20     20   /* Turn bulk memory into a hash table object by initializing the
    21     21   ** fields of the Hash structure.
    22     22   **
................................................................................
   306    306       assert( pH->first==0 );
   307    307       assert( pH->count==0 );
   308    308       sqlite3HashClear(pH);
   309    309     }
   310    310   }
   311    311   
   312    312   /* Attempt to locate an element of the hash table pH with a key
   313         -** that matches pKey,nKey.  Return the data for this element if it is
   314         -** found, or NULL if there is no match.
          313  +** that matches pKey,nKey.  Return a pointer to the corresponding 
          314  +** HashElem structure for this element if it is found, or NULL
          315  +** otherwise.
   315    316   */
   316         -void *sqlite3HashFind(const Hash *pH, const void *pKey, int nKey){
          317  +HashElem *sqlite3HashFindElem(const Hash *pH, const void *pKey, int nKey){
   317    318     int h;             /* A hash on key */
   318    319     HashElem *elem;    /* The element that matches key */
   319    320     int (*xHash)(const void*,int);  /* The hash function */
   320    321   
   321    322     if( pH==0 || pH->ht==0 ) return 0;
   322    323     xHash = hashFunction(pH->keyClass);
   323    324     assert( xHash!=0 );
   324    325     h = (*xHash)(pKey,nKey);
   325    326     assert( (pH->htsize & (pH->htsize-1))==0 );
   326    327     elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
          328  +  return elem;
          329  +}
          330  +
          331  +/* Attempt to locate an element of the hash table pH with a key
          332  +** that matches pKey,nKey.  Return the data for this element if it is
          333  +** found, or NULL if there is no match.
          334  +*/
          335  +void *sqlite3HashFind(const Hash *pH, const void *pKey, int nKey){
          336  +  HashElem *elem;    /* The element that matches key */
          337  +  elem = sqlite3HashFindElem(pH, pKey, nKey);
   327    338     return elem ? elem->data : 0;
   328    339   }
   329    340   
   330    341   /* Insert an element into the hash table pH.  The key is pKey,nKey
   331    342   ** and the data is "data".
   332    343   **
   333    344   ** If no element exists with a matching key, then a new

Changes to src/hash.h.

     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This is the header file for the generic hash-table implemenation
    13     13   ** used in SQLite.
    14     14   **
    15         -** $Id: hash.h,v 1.10 2007/08/16 04:30:40 drh Exp $
           15  +** $Id: hash.h,v 1.11 2007/09/04 14:31:47 danielk1977 Exp $
    16     16   */
    17     17   #ifndef _SQLITE_HASH_H_
    18     18   #define _SQLITE_HASH_H_
    19     19   
    20     20   /* Forward declarations of structures. */
    21     21   typedef struct Hash Hash;
    22     22   typedef struct HashElem HashElem;
................................................................................
    77     77   
    78     78   /*
    79     79   ** Access routines.  To delete, insert a NULL pointer.
    80     80   */
    81     81   void sqlite3HashInit(Hash*, int keytype, int copyKey);
    82     82   void *sqlite3HashInsert(Hash*, const void *pKey, int nKey, void *pData);
    83     83   void *sqlite3HashFind(const Hash*, const void *pKey, int nKey);
           84  +HashElem *sqlite3HashFindElem(const Hash*, const void *pKey, int nKey);
    84     85   void sqlite3HashClear(Hash*);
    85     86   
    86     87   /*
    87     88   ** Macros for looping over all elements of a hash table.  The idiom is
    88     89   ** like this:
    89     90   **
    90     91   **   Hash h;

Changes to src/test_async.c.

   147    147   **     Read operations. Both of these read from both the underlying file
   148    148   **     first then adjust their result based on pending writes in the 
   149    149   **     write-op queue.   So async.queueMutex is held for the duration
   150    150   **     of these operations to prevent other threads from changing the
   151    151   **     queue in mid operation.
   152    152   **    
   153    153   **
   154         -**         asyncLock, asyncUnlock, asyncLockState, asyncCheckReservedLock
          154  +**         asyncLock, asyncUnlock, asyncCheckReservedLock
   155    155   **    
   156    156   **     These primitives implement in-process locking using a hash table
   157    157   **     on the file name.  Files are locked correctly for connections coming
   158    158   **     from the same process.  But other processes cannot see these locks
   159    159   **     and will therefore not honor them.
   160    160   **
   161    161   **
................................................................................
   248    248   
   249    249   /* Possible values of AsyncWrite.op */
   250    250   #define ASYNC_NOOP          0
   251    251   #define ASYNC_WRITE         1
   252    252   #define ASYNC_SYNC          2
   253    253   #define ASYNC_TRUNCATE      3
   254    254   #define ASYNC_CLOSE         4
   255         -#define ASYNC_OPENDIRECTORY 5
   256         -#define ASYNC_SETFULLSYNC   6
   257         -#define ASYNC_DELETE        7
   258         -#define ASYNC_OPENEXCLUSIVE 8
   259         -#define ASYNC_SYNCDIRECTORY 9
          255  +#define ASYNC_DELETE        5
          256  +#define ASYNC_OPENEXCLUSIVE 6
   260    257   
   261    258   /* Names of opcodes.  Used for debugging only.
   262    259   ** Make sure these stay in sync with the macros above!
   263    260   */
   264    261   static const char *azOpcodeName[] = {
   265         -  "NOOP", "WRITE", "SYNC", "TRUNCATE", "CLOSE",
   266         -  "OPENDIR", "SETFULLSYNC", "DELETE", "OPENEX", "SYNCDIR",
          262  +  "NOOP", "WRITE", "SYNC", "TRUNCATE", "CLOSE", "DELETE", "OPENEX"
   267    263   };
   268    264   
   269    265   /*
   270    266   ** Entries on the write-op queue are instances of the AsyncWrite
   271    267   ** structure, defined here.
   272    268   **
   273    269   ** The interpretation of the iOffset and nByte variables varies depending 
   274    270   ** on the value of AsyncWrite.op:
   275    271   **
          272  +** ASYNC_NOOP:
          273  +**     No values used.
          274  +**
   276    275   ** ASYNC_WRITE:
   277    276   **     iOffset -> Offset in file to write to.
   278    277   **     nByte   -> Number of bytes of data to write (pointed to by zBuf).
   279    278   **
   280    279   ** ASYNC_SYNC:
   281    280   **     nByte   -> flags to pass to sqlite3OsSync().
   282    281   **
................................................................................
   283    282   ** ASYNC_TRUNCATE:
   284    283   **     iOffset -> Size to truncate file to.
   285    284   **     nByte   -> Unused.
   286    285   **
   287    286   ** ASYNC_CLOSE:
   288    287   **     iOffset -> Unused.
   289    288   **     nByte   -> Unused.
   290         -**
   291         -** ASYNC_OPENDIRECTORY:
   292         -**     iOffset -> Unused.
   293         -**     nByte   -> Number of bytes of zBuf points to (directory name).
   294         -**
   295         -** ASYNC_SETFULLSYNC:
   296         -**     iOffset -> Unused.
   297         -**     nByte   -> New value for the full-sync flag.
   298         -**
   299    289   **
   300    290   ** ASYNC_DELETE:
   301    291   **     iOffset -> Contains the "syncDir" flag.
   302    292   **     nByte   -> Number of bytes of zBuf points to (file name).
   303    293   **
   304    294   ** ASYNC_OPENEXCLUSIVE:
   305    295   **     iOffset -> Value of "delflag".
................................................................................
   315    305     AsyncFileData *pFileData;    /* File to write data to or sync */
   316    306     int op;                      /* One of ASYNC_xxx etc. */
   317    307     i64 iOffset;        /* See above */
   318    308     int nByte;          /* See above */
   319    309     char *zBuf;         /* Data to write to file (or NULL if op!=ASYNC_WRITE) */
   320    310     AsyncWrite *pNext;  /* Next write operation (to any file) */
   321    311   };
          312  +
          313  +/*
          314  +** An instance of the following structure is allocated along with each
          315  +** AsyncFileData structure (see AsyncFileData.lock), but is only used if the
          316  +** file was opened with the SQLITE_OPEN_MAIN_DB.
          317  +**
          318  +** 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.
          323  +*/
          324  +typedef struct AsyncLock AsyncLock;
          325  +struct AsyncLock {
          326  +  int eLock;
          327  +  AsyncLock *pNext;
          328  +};
   322    329   
   323    330   /* 
   324    331   ** The AsyncFile structure is a subclass of sqlite3_file used for 
   325    332   ** asynchronous IO. 
   326    333   **
   327    334   ** All of the actual data for the structure is stored in the structure
   328    335   ** pointed to by AsyncFile.pData, which is allocated as part of the
................................................................................
   336    343     AsyncFileData *pData;
   337    344   };
   338    345   struct AsyncFileData {
   339    346     char *zName;               /* Underlying OS filename - used for debugging */
   340    347     int nName;                 /* Number of characters in zName */
   341    348     sqlite3_file *pBaseRead;   /* Read handle to the underlying Os file */
   342    349     sqlite3_file *pBaseWrite;  /* Write handle to the underlying Os file */
          350  +  AsyncLock lock;
   343    351   };
   344    352   
   345    353   /*
   346    354   ** Add an entry to the end of the global write-op list. pWrite should point 
   347    355   ** to an AsyncWrite structure allocated using sqlite3_malloc().  The writer
   348    356   ** thread will call sqlite3_free() to free the structure after the specified
   349    357   ** operation has been completed.
................................................................................
   432    440   
   433    441   /*
   434    442   ** Close the file. This just adds an entry to the write-op list, the file is
   435    443   ** not actually closed.
   436    444   */
   437    445   static int asyncClose(sqlite3_file *pFile){
   438    446     AsyncFileData *p = ((AsyncFile *)pFile)->pData;
          447  +
          448  +  /* Unlock the file, if it is locked */
          449  +  pthread_mutex_lock(&async.lockMutex);
          450  +  p->lock.eLock = 0;
          451  +  pthread_mutex_unlock(&async.lockMutex);
          452  +
   439    453     return addNewAsyncWrite(p, ASYNC_CLOSE, 0, 0, 0);
   440    454   }
   441    455   
   442    456   /*
   443    457   ** Implementation of sqlite3OsWrite() for asynchronous files. Instead of 
   444    458   ** writing to the underlying file, this function adds an entry to the end of
   445    459   ** the global AsyncWrite list. Either SQLITE_OK or SQLITE_NOMEM may be
................................................................................
   484    498         rc = sqlite3OsRead(pBase, zOut, nRead, iOffset);
   485    499         ASYNC_TRACE(("READ %s %d bytes at %d\n", p->zName, nRead, iOffset));
   486    500       }
   487    501     }
   488    502   
   489    503     if( rc==SQLITE_OK ){
   490    504       AsyncWrite *pWrite;
          505  +    char *zName = p->zName;
   491    506   
   492    507       for(pWrite=async.pQueueFirst; pWrite; pWrite = pWrite->pNext){
   493         -      if( pWrite->pFileData==p && pWrite->op==ASYNC_WRITE ){
          508  +      if( pWrite->op==ASYNC_WRITE && pWrite->pFileData->zName==zName ){
   494    509           int iBeginOut = (pWrite->iOffset-iOffset);
   495    510           int iBeginIn = -iBeginOut;
   496    511           int nCopy;
   497    512   
   498    513           if( iBeginIn<0 ) iBeginIn = 0;
   499    514           if( iBeginOut<0 ) iBeginOut = 0;
   500    515           nCopy = MIN(pWrite->nByte-iBeginIn, iAmt-iBeginOut);
................................................................................
   554    569     if( pBase->pMethods ){
   555    570       rc = sqlite3OsFileSize(pBase, &s);
   556    571     }
   557    572   
   558    573     if( rc==SQLITE_OK ){
   559    574       AsyncWrite *pWrite;
   560    575       for(pWrite=async.pQueueFirst; pWrite; pWrite = pWrite->pNext){
   561         -      if( pWrite->pFileData==p ){
          576  +      if( pWrite->op==ASYNC_DELETE && strcmp(p->zName, pWrite->zBuf)==0 ){
          577  +        s = 0;
          578  +      }else if( pWrite->pFileData && pWrite->pFileData->zName==p->zName){
   562    579           switch( pWrite->op ){
   563    580             case ASYNC_WRITE:
   564    581               s = MAX(pWrite->iOffset + (i64)(pWrite->nByte), s);
   565    582               break;
   566    583             case ASYNC_TRUNCATE:
   567    584               s = MIN(s, pWrite->iOffset);
   568    585               break;
................................................................................
   579    596   ** No disk locking is performed.  We keep track of locks locally in
   580    597   ** the async.aLock hash table.  Locking should appear to work the same
   581    598   ** as with standard (unmodified) SQLite as long as all connections 
   582    599   ** come from this one process.  Connections from external processes
   583    600   ** cannot see our internal hash table (obviously) and will thus not
   584    601   ** honor our locks.
   585    602   */
   586         -static int asyncLock(sqlite3_file *pFile, int lockType){
          603  +static int asyncLock(sqlite3_file *pFile, int eLock){
          604  +  int rc = SQLITE_OK;
          605  +  AsyncFileData *p = ((AsyncFile *)pFile)->pData;
          606  +
          607  +  pthread_mutex_lock(&async.lockMutex);
          608  +  if( p->lock.eLock<eLock ){
          609  +    AsyncLock *pLock;
          610  +    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)
          618  +      )){
          619  +        rc = SQLITE_BUSY;
          620  +      }
          621  +    }
          622  +    if( rc==SQLITE_OK ){
          623  +      p->lock.eLock = eLock;
          624  +    }
          625  +  }
          626  +  pthread_mutex_unlock(&async.lockMutex);
          627  +
          628  +  ASYNC_TRACE(("LOCK %d (%s) rc=%d\n", eLock, p->zName, rc));
          629  +  return rc;
          630  +}
          631  +static int asyncUnlock(sqlite3_file *pFile, int eLock){
   587    632     AsyncFileData *p = ((AsyncFile *)pFile)->pData;
   588         -  ASYNC_TRACE(("LOCK %d (%s)\n", lockType, p->zName));
          633  +  AsyncLock *pLock = &p->lock;
   589    634     pthread_mutex_lock(&async.lockMutex);
   590         -  sqlite3HashInsert(&async.aLock, p->zName, p->nName, (void*)lockType);
          635  +  if( pLock->eLock>eLock ){
          636  +    pLock->eLock = eLock;
          637  +  }
   591    638     pthread_mutex_unlock(&async.lockMutex);
   592    639     return SQLITE_OK;
   593    640   }
   594         -static int asyncUnlock(sqlite3_file *pFile, int lockType){
   595         -  return asyncLock(pFile, lockType);
   596         -}
   597    641   
   598    642   /*
   599    643   ** This function is called when the pager layer first opens a database file
   600    644   ** and is checking for a hot-journal.
   601    645   */
   602    646   static int asyncCheckReservedLock(sqlite3_file *pFile){
          647  +  int ret = 0;
          648  +  AsyncLock *pLock;
   603    649     AsyncFileData *p = ((AsyncFile *)pFile)->pData;
   604         -  int rc;
          650  +
   605    651     pthread_mutex_lock(&async.lockMutex);
   606         -  rc = (int)sqlite3HashFind(&async.aLock, p->zName, p->nName);
          652  +  pLock = (AsyncLock *)sqlite3HashFind(&async.aLock, p->zName, p->nName);
          653  +  for(/*no-op*/; pLock; pLock=pLock->pNext){
          654  +    if( pLock->eLock>=SQLITE_LOCK_RESERVED ){
          655  +      ret = 1;
          656  +    }
          657  +  }
   607    658     pthread_mutex_unlock(&async.lockMutex);
   608         -  ASYNC_TRACE(("CHECK-LOCK %d (%s)\n", rc, p->zName));
   609         -  return rc>SHARED_LOCK;
          659  +
          660  +  ASYNC_TRACE(("CHECK-LOCK %d (%s)\n", ret, p->zName));
          661  +  return ret;
   610    662   }
   611    663   
   612    664   /* 
   613    665   ** This is a no-op, as the asynchronous backend does not support locking.
   614    666   */
   615    667   static int asyncFileControl(sqlite3_file *id, int op, void *pArg){
   616    668     return SQLITE_ERROR;
................................................................................
   652    704       asyncFileControl,                /* xFileControl */
   653    705       asyncSectorSize,                 /* xSectorSize */
   654    706       asyncDeviceCharacteristics       /* xDeviceCharacteristics */
   655    707     };
   656    708   
   657    709     sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
   658    710     AsyncFile *p = (AsyncFile *)pFile;
   659         -  int nName = strlen(zName);;
          711  +  int nName = strlen(zName)+1;
   660    712     int rc;
   661    713     int nByte;
   662    714     AsyncFileData *pData;
   663    715   
   664    716     nByte = (
   665    717       sizeof(AsyncFileData) +        /* AsyncFileData structure */
   666         -    2 * pVfs->szOsFile +           /* AsyncFileData.zName */
   667         -    nName + 1                      /* AsyncFileData.pBaseRead and pBaseWrite */
   668         -  );
          718  +    2 * pVfs->szOsFile +           /* AsyncFileData.pBaseRead and pBaseWrite */
          719  +    nName                          /* AsyncFileData.zName */
          720  +  ); 
   669    721     pData = sqlite3_malloc(nByte);
   670    722     if( !pData ){
   671    723       return SQLITE_NOMEM;
   672    724     }
   673    725     memset(pData, 0, nByte);
   674    726     pData->zName = (char *)&pData[1];
   675    727     pData->nName = nName;
   676         -  pData->pBaseRead = (sqlite3_file *)&pData->zName[nName+1];
   677         -  pData->pBaseWrite = (sqlite3_file *)&pData->zName[nName+1+pVfs->szOsFile];
   678         -  memcpy(pData->zName, zName, nName+1);
          728  +  pData->pBaseRead = (sqlite3_file *)&pData->zName[nName];
          729  +  pData->pBaseWrite = (sqlite3_file *)&pData->zName[nName+pVfs->szOsFile];
          730  +  memcpy(pData->zName, zName, nName);
   679    731   
   680    732     if( flags&SQLITE_OPEN_EXCLUSIVE ){
   681    733       rc = addNewAsyncWrite(pData, ASYNC_OPENEXCLUSIVE, (i64)flags, 0, 0);
   682    734       if( pOutFlags ) *pOutFlags = flags;
   683    735     }else{
   684    736       rc = sqlite3OsOpen(pVfs, zName, pData->pBaseRead, flags, pOutFlags);
   685    737       if( rc==SQLITE_OK && ((*pOutFlags)&SQLITE_OPEN_READWRITE) ){
   686    738         rc = sqlite3OsOpen(pVfs, zName, pData->pBaseWrite, flags, 0);
   687    739       }
   688    740     }
   689    741   
   690    742     if( rc==SQLITE_OK ){
          743  +    HashElem *pElem;
   691    744       p->pMethod = &async_methods;
   692    745       p->pData = pData;
   693    746       incrOpenFileCount();
          747  +
          748  +    /* Link AsyncFileData.lock into the linked list of AsyncLock structures
          749  +    ** for this file. Obtain the async.lockMutex mutex before doing so.
          750  +    */
          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;
          757  +    pElem = sqlite3HashFindElem(&async.aLock, pData->zName, pData->nName);
          758  +    pData->zName = (char *)sqliteHashKey(pElem);
          759  +    pthread_mutex_unlock(&async.lockMutex);
   694    760     }else{
   695    761       sqlite3OsClose(pData->pBaseRead);
   696    762       sqlite3OsClose(pData->pBaseWrite);
   697    763       sqlite3_free(pData);
   698    764     }
   699    765   
   700    766     return rc;
................................................................................
   744    810     return ret;
   745    811   }
   746    812   
   747    813   static int asyncGetTempName(sqlite3_vfs *pAsyncVfs, char *zBufOut){
   748    814     sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
   749    815     return pVfs->xGetTempName(pVfs, zBufOut);
   750    816   }
          817  +
          818  +/*
          819  +** Fill in zPathOut with the full path to the file identified by zPath.
          820  +*/
   751    821   static int asyncFullPathname(
   752    822     sqlite3_vfs *pAsyncVfs, 
   753    823     const char *zPath, 
   754    824     char *zPathOut
   755    825   ){
          826  +  int rc;
   756    827     sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
   757         -  return sqlite3OsFullPathname(pVfs, zPath, zPathOut);
          828  +  rc = sqlite3OsFullPathname(pVfs, zPath, zPathOut);
          829  +
          830  +  /* Because of the way intra-process file locking works, this backend
          831  +  ** needs to return a canonical path. The following block assumes the
          832  +  ** file-system uses unix style paths. 
          833  +  */
          834  +  if( rc==SQLITE_OK ){
          835  +    int iIn;
          836  +    int iOut = 0;
          837  +    int nPathOut = strlen(zPathOut);
          838  +
          839  +    for(iIn=0; iIn<nPathOut; iIn++){
          840  +
          841  +      /* Replace any occurences of "//" with "/" */
          842  +      if( iIn<=(nPathOut-2) && zPathOut[iIn]=='/' && zPathOut[iIn+1]=='/'
          843  +      ){
          844  +        continue;
          845  +      }
          846  +
          847  +      /* Replace any occurences of "/./" with "/" */
          848  +      if( iIn<=(nPathOut-3) 
          849  +       && zPathOut[iIn]=='/' && zPathOut[iIn+1]=='.' && zPathOut[iIn+2]=='/'
          850  +      ){
          851  +        iIn++;
          852  +        continue;
          853  +      }
          854  +
          855  +      /* Replace any occurences of "<path-component>/../" with "" */
          856  +      if( iOut>0 && iIn<=(nPathOut-4) 
          857  +       && zPathOut[iIn]=='/' && zPathOut[iIn+1]=='.' 
          858  +       && zPathOut[iIn+2]=='.' && zPathOut[iIn+2]=='/'
          859  +      ){
          860  +        iIn += 3;
          861  +        iOut--;
          862  +        for( ; iOut>0 && zPathOut[iOut]!='/'; iOut--);
          863  +        continue;
          864  +      }
          865  +
          866  +      zPathOut[iOut++] = zPathOut[iIn];
          867  +    }
          868  +    zPathOut[iOut] = '\0';
          869  +  }
          870  +
          871  +  return rc;
   758    872   }
   759    873   static void *asyncDlOpen(sqlite3_vfs *pAsyncVfs, const char *zPath){
   760    874     sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
   761    875     return pVfs->xDlOpen(pVfs, zPath);
   762    876   }
   763    877   static void asyncDlError(sqlite3_vfs *pAsyncVfs, int nByte, char *zErrMsg){
   764    878     sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
................................................................................
   939   1053         case ASYNC_TRUNCATE:
   940   1054           assert( pBase );
   941   1055           ASYNC_TRACE(("TRUNCATE %s to %d bytes\n", 
   942   1056                   p->pFileData->zName, p->iOffset));
   943   1057           rc = sqlite3OsTruncate(pBase, p->iOffset);
   944   1058           break;
   945   1059   
   946         -      case ASYNC_CLOSE:
         1060  +      case ASYNC_CLOSE: {
         1061  +        AsyncLock *pLock;
         1062  +        AsyncFileData *pData = p->pFileData;
   947   1063           ASYNC_TRACE(("CLOSE %s\n", p->pFileData->zName));
   948         -        sqlite3OsClose(p->pFileData->pBaseWrite);
   949         -        sqlite3OsClose(p->pFileData->pBaseRead);
   950         -        sqlite3_free(p->pFileData);
         1064  +        sqlite3OsClose(pData->pBaseWrite);
         1065  +        sqlite3OsClose(pData->pBaseRead);
         1066  +
         1067  +        /* Unlink AsyncFileData.lock from the linked list of AsyncLock 
         1068  +        ** structures for this file. Obtain the async.lockMutex mutex 
         1069  +        ** before doing so.
         1070  +        */
         1071  +        pthread_mutex_lock(&async.lockMutex);
         1072  +        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  +          );
         1077  +        }else{
         1078  +          for( ; pLock && pLock->pNext!=&pData->lock; pLock=pLock->pNext);
         1079  +          if( pLock ){
         1080  +            pLock->pNext = pData->lock.pNext;
         1081  +          }
         1082  +        }
         1083  +        pthread_mutex_unlock(&async.lockMutex);
         1084  +
         1085  +        sqlite3_free(pData);
   951   1086           break;
         1087  +      }
   952   1088   
   953   1089         case ASYNC_DELETE:
   954   1090           ASYNC_TRACE(("DELETE %s\n", p->zBuf));
   955   1091           rc = sqlite3OsDelete(pVfs, p->zBuf, (int)p->iOffset);
   956   1092           break;
   957   1093   
   958   1094         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.7 2006/03/19 13:00:25 drh Exp $
            9  +# $Id: async.test,v 1.8 2007/09/04 14:31:47 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   
................................................................................
    26     26     select2.test
    27     27     select3.test
    28     28     select4.test
    29     29     insert.test
    30     30     insert2.test
    31     31     insert3.test
    32     32     trans.test
           33  +  lock.test
           34  +  lock3.test
    33     35   }
    34         -# set INCLUDE {select4.test}
    35     36   
    36     37   # Enable asynchronous IO.
    37     38   sqlite3async_enable 1
    38     39   
    39     40   rename do_test really_do_test
    40     41   proc do_test {name args} {
    41     42     uplevel really_do_test async_io-$name $args