From: drh Date: Mon, 13 Feb 2006 17:03:47 +0000 (+0000) Subject: Add in-process file locking to test_async.c. The unix implementation of X-Git-Tag: version-3.6.10~3077 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=89ea93121d010660a0d7da134082d5042d83770c;p=thirdparty%2Fsqlite.git Add in-process file locking to test_async.c. The unix implementation of sqlite3OsFullPathname() now attempts to remove /./ and /../ elements from the path. (CVS 3090) FossilOrigin-Name: 42379c623073eb541d053c2dff9f49087fb290f8 --- diff --git a/manifest b/manifest index ce77ca441e..bbb770d6d9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sdeadlock\sproblem\son\sthe\ssqlite3async_wait\stest\sinterface.\s\sImprovements\nto\stracing\sin\stest_async.c.\s(CVS\s3089) -D 2006-02-13T15:29:33 +C Add\sin-process\sfile\slocking\sto\stest_async.c.\s\sThe\sunix\simplementation\sof\nsqlite3OsFullPathname()\snow\sattempts\sto\sremove\s/./\sand\s/../\selements\sfrom\nthe\spath.\s(CVS\s3090) +D 2006-02-13T17:03:48 F Makefile.in 5d8dff443383918b700e495de42ec65bc1c8865b F Makefile.linux-gcc 74ba0eadf88748a9ce3fd03d2a3ede2e6715baec F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -55,7 +55,7 @@ F src/os.h 93035a0e3b9dd05cdd0aaef32ea28ca28e02fe78 F src/os_common.h 108cd719c96a2b714b64e02aeabbd40684274e6a F src/os_test.c 49833426101f99aee4bb5f6a44b7c4b2029fda1c F src/os_test.h 903c93554c23d88f34f667f1979e4a1cee792af3 -F src/os_unix.c 378a89aaf15d83b901c1b73350e91ddecf743986 +F src/os_unix.c 7604ea2bdb447c1f87ca9f5103e452b9cd1cbd1d F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e F src/os_win.c c67a2c46d929cf54c8f80ec5e6079cf684a141a9 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b @@ -80,7 +80,7 @@ F src/test4.c ff4e9406b3d2809966d8f0e82468ac5508be9f56 F src/test5.c 7162f8526affb771c4ed256826eee7bb9eca265f F src/test6.c 60a02961ceb7b3edc25f5dc5c1ac2556622a76de F src/test7.c d28d3e62f9594923648fc6a8fb030eba36564ba1 -F src/test_async.c b721c7cfcf40374a5e88d0ccd99d6354c7e5e258 +F src/test_async.c 2f43ce5293ca20bcd607d7fd261f8f2897cf9821 F src/test_md5.c 6c42bc0a3c0b54be34623ff77a0eec32b2fa96e3 F src/test_server.c 087b92a39d883e3fa113cae259d64e4c7438bc96 F src/tokenize.c 382b3bb0ca26eb9153b5d20b246ef512a114a24f @@ -352,7 +352,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P 4c6dfec54fc128644e066c04902433f8df30672e -R 0f06f2bb71d391db7d343449491e5029 +P 58c6d501385c6f7656290e8451e28be3fc45843f +R b777aa4c4acd9e845f59e4ffc4f5724f U drh -Z 257635654268c39c1b6895d7785df9c1 +Z bfa3cfee6b6173ed141a70e906af7ee7 diff --git a/manifest.uuid b/manifest.uuid index ff9801566c..86cd9f67b8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -58c6d501385c6f7656290e8451e28be3fc45843f \ No newline at end of file +42379c623073eb541d053c2dff9f49087fb290f8 \ No newline at end of file diff --git a/src/os_unix.c b/src/os_unix.c index f7adab2b2c..6621625ac2 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -1539,6 +1539,7 @@ static int unixClose(OsFile **pId){ */ char *sqlite3UnixFullPathname(const char *zRelative){ char *zFull = 0; + int i, j; if( zRelative[0]=='/' ){ sqlite3SetString(&zFull, zRelative, (char*)0); }else{ @@ -1551,6 +1552,28 @@ char *sqlite3UnixFullPathname(const char *zRelative){ (char*)0); sqliteFree(zBuf); } + /* + ** Remove "/./" path elements and convert "/A/./" path elements + ** to just "/". + */ + if( zFull ){ + for(i=j=0; zFull[i]; i++){ + if( zFull[i]=='/' ){ + if( zFull[i+1]=='/' ) continue; + if( zFull[i+1]=='.' && zFull[i+2]=='/' ){ + i += 1; + continue; + } + if( zFull[i+1]=='.' && zFull[i+2]=='.' && zFull[i+3]=='/' ){ + while( j>0 && zFull[j-1]!='/' ){ j--; } + i += 3; + continue; + } + } + zFull[j++] = zFull[i]; + } + zFull[j] = 0; + } return zFull; } diff --git a/src/test_async.c b/src/test_async.c index 989ab27c19..fa8787ed56 100644 --- a/src/test_async.c +++ b/src/test_async.c @@ -161,8 +161,10 @@ static void asyncTrace(const char *zFormat, ...){ ** ** asyncLock, asyncUnlock, asyncLockState, asyncCheckReservedLock ** -** These locking primitives become no-ops. Files are always opened for -** exclusive access when using this IO backend. +** These primitives implement in-process locking using a hash table +** on the file name. Files are locked correctly for connections coming +** from the same process. But other processes cannot see these locks +** and will therefore not honor them. ** ** ** asyncFileHandle. @@ -233,14 +235,17 @@ static void asyncTrace(const char *zFormat, ...){ static struct TestAsyncStaticData { pthread_mutex_t queueMutex; /* Mutex for access to write operation queue */ pthread_mutex_t writerMutex; /* Prevents multiple writer threads */ + pthread_mutex_t lockMutex; /* For access to aLock hash table */ pthread_cond_t queueSignal; /* For waking up sleeping writer thread */ pthread_cond_t emptySignal; /* Notify when the write queue is empty */ AsyncWrite *pQueueFirst; /* Next write operation to be processed */ AsyncWrite *pQueueLast; /* Last write operation on the list */ + Hash aLock; /* Files locked */ volatile int ioDelay; /* Extra delay between write operations */ volatile int writerHaltWhenIdle; /* Writer thread halts when queue empty */ volatile int writerHaltNow; /* Writer thread halts after next op */ } async = { + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, @@ -255,7 +260,6 @@ static struct TestAsyncStaticData { #define ASYNC_CLOSE 4 #define ASYNC_OPENDIRECTORY 5 #define ASYNC_SETFULLSYNC 6 - #define ASYNC_DELETE 7 #define ASYNC_OPENEXCLUSIVE 8 #define ASYNC_SYNCDIRECTORY 9 @@ -265,7 +269,7 @@ static struct TestAsyncStaticData { */ static const char *azOpcodeName[] = { "NOOP", "WRITE", "SYNC", "TRUNCATE", "CLOSE", - "OPENDIR", "SETFULLSYNC", "DELETE", "OPENEX", "SYNCDIR" + "OPENDIR", "SETFULLSYNC", "DELETE", "OPENEX", "SYNCDIR", }; /* @@ -308,6 +312,7 @@ static const char *azOpcodeName[] = { ** iOffset -> Value of "delflag". ** nByte -> Number of bytes of zBuf points to (file name). ** +** ** For an ASYNC_WRITE operation, zBuf points to the data to write to the file. ** This space is sqliteMalloc()d along with the AsyncWrite structure in a ** single blob, so is deleted when sqliteFree() is called on the parent @@ -329,6 +334,8 @@ struct AsyncFile { IoMethod *pMethod; /* Must be first */ int ioError; /* Value of any asychronous error we have seen */ i64 iOffset; /* Current seek() offset in file */ + char *zName; /* Underlying OS filename - used for debugging */ + int nName; /* Number of characters in zName */ OsFile *pBaseRead; /* Read handle to the underlying Os file */ OsFile *pBaseWrite; /* Write handle to the underlying Os file */ }; @@ -356,7 +363,8 @@ static void addAsyncWrite(AsyncWrite *pWrite){ async.pQueueFirst = pWrite; } async.pQueueLast = pWrite; - TRACE(("PUSH %p (%s)\n", pWrite, azOpcodeName[pWrite->op])); + TRACE(("PUSH %p (%s %s)\n", pWrite, azOpcodeName[pWrite->op], + pWrite->pFile ? pWrite->pFile->zName : "-")); /* Drop the queue mutex */ pthread_mutex_unlock(&async.queueMutex); @@ -585,14 +593,23 @@ static int asyncFileHandle(OsFile *id){ } /* -** No file locking occurs with this version of the asynchronous backend. -** So the locking routines are no-ops. +** No disk locking is performed. We keep track of locks locally in +** the async.aLock hash table. Locking should appear to work the same +** as with standard (unmodified) SQLite as long as all connections +** come from this one process. Connections from external processes +** cannot see our internal hash table (obviously) and will thus not +** honor our locks. */ static int asyncLock(OsFile *id, int lockType){ + AsyncFile *pFile = (AsyncFile*)id; + TRACE(("LOCK %d (%s)\n", lockType, pFile->zName)); + pthread_mutex_lock(&async.lockMutex); + sqlite3HashInsert(&async.aLock, pFile->zName, pFile->nName, (void*)lockType); + pthread_mutex_unlock(&async.lockMutex); return SQLITE_OK; } static int asyncUnlock(OsFile *id, int lockType){ - return SQLITE_OK; + return asyncLock(id, lockType); } /* @@ -600,7 +617,13 @@ static int asyncUnlock(OsFile *id, int lockType){ ** and is checking for a hot-journal. */ static int asyncCheckReservedLock(OsFile *id){ - return SQLITE_OK; + AsyncFile *pFile = (AsyncFile*)id; + int rc; + pthread_mutex_lock(&async.lockMutex); + rc = (int)sqlite3HashFind(&async.aLock, pFile->zName, pFile->nName); + pthread_mutex_unlock(&async.lockMutex); + TRACE(("CHECK-LOCK %d (%s)\n", rc, pFile->zName)); + return rc; } /* @@ -632,7 +655,7 @@ static int asyncOpenFile( OsFile *pBaseRead, /* The real OsFile from the real I/O routine */ int openForWriting /* Open a second file handle for writing if true */ ){ - int rc; + int rc, i, n; AsyncFile *p; OsFile *pBaseWrite = 0; @@ -661,13 +684,17 @@ static int asyncOpenFile( } } - p = (AsyncFile *)sqlite3OsMalloc(sizeof(AsyncFile)); + n = strlen(zName); + for(i=n-1; i>=0 && zName[i]!='/'; i--){} + p = (AsyncFile *)sqlite3OsMalloc(sizeof(AsyncFile) + n - i); if( !p ){ rc = SQLITE_NOMEM; goto error_out; } memset(p, 0, sizeof(AsyncFile)); - + p->zName = (char*)&p[1]; + strcpy(p->zName, &zName[i+1]); + p->nName = n - i; p->pMethod = &iomethod; p->pBaseRead = pBaseRead; p->pBaseWrite = pBaseWrite; @@ -759,6 +786,7 @@ static int asyncFileExists(const char *z){ } } + TRACE(("EXISTS: %s = %d\n", z, ret)); pthread_mutex_unlock(&async.queueMutex); return ret; } @@ -772,6 +800,8 @@ static int asyncFileExists(const char *z){ */ static void asyncEnable(int enable){ if( enable && xOrigOpenReadWrite==0 ){ + sqlite3HashInit(&async.aLock, SQLITE_HASH_BINARY, 1); + xOrigOpenReadWrite = sqlite3Os.xOpenReadWrite; xOrigOpenReadOnly = sqlite3Os.xOpenReadOnly; xOrigOpenExclusive = sqlite3Os.xOpenExclusive; @@ -787,6 +817,8 @@ static void asyncEnable(int enable){ sqlite3Os.xSyncDirectory = asyncSyncDirectory; } if( !enable && xOrigOpenReadWrite!=0 ){ + sqlite3HashClear(&async.aLock); + sqlite3Os.xOpenReadWrite = xOrigOpenReadWrite; sqlite3Os.xOpenReadOnly = xOrigOpenReadOnly; sqlite3Os.xOpenExclusive = xOrigOpenExclusive; @@ -847,7 +879,8 @@ static void *asyncWriterThread(void *NotUsed){ } } if( p==0 ) break; - TRACE(("PROCESSING %p (%s)\n", p, azOpcodeName[p->op])); + TRACE(("PROCESSING %p (%s %s)\n", p, azOpcodeName[p->op], + p->pFile ? p->pFile->zName : "-")); /* Right now this thread is holding the mutex on the write-op queue. ** Variable 'p' points to the first entry in the write-op queue. In @@ -1125,16 +1158,25 @@ static int testAsyncWait( int objc, Tcl_Obj *CONST objv[] ){ + int cnt = 10; if( async.writerHaltNow==0 && async.writerHaltWhenIdle==0 ){ Tcl_AppendResult(interp, "would block forever", (char*)0); return TCL_ERROR; } - TRACE(("WAIT\n")); - pthread_mutex_lock(&async.queueMutex); - pthread_cond_broadcast(&async.queueSignal); - pthread_mutex_unlock(&async.queueMutex); - pthread_mutex_lock(&async.writerMutex); - pthread_mutex_unlock(&async.writerMutex); + while( cnt-- && !pthread_mutex_trylock(&async.writerMutex) ){ + pthread_mutex_unlock(&async.writerMutex); + sched_yield(); + } + if( cnt>=0 ){ + TRACE(("WAIT\n")); + pthread_mutex_lock(&async.queueMutex); + pthread_cond_broadcast(&async.queueSignal); + pthread_mutex_unlock(&async.queueMutex); + pthread_mutex_lock(&async.writerMutex); + pthread_mutex_unlock(&async.writerMutex); + }else{ + TRACE(("NOTHING TO WAIT ON\n")); + } return TCL_OK; }