From: danielk1977 Date: Tue, 14 Feb 2006 13:25:43 +0000 (+0000) Subject: Add simple io error tests for test_async.c. (CVS 3094) X-Git-Tag: version-3.6.10~3073 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=be29bfc0b5270758ea17578c40a1215f9eedcd9f;p=thirdparty%2Fsqlite.git Add simple io error tests for test_async.c. (CVS 3094) FossilOrigin-Name: 528dfb71801bb7b8a66944db6f32cc3dc0054118 --- diff --git a/manifest b/manifest index 44bd32e914..fc163d0070 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Changes\sso\sthat\stest_async.c\sworks\swith\smemory\smanagement\sturned\son.\s(CVS\s3093) -D 2006-02-14T10:48:39 +C Add\ssimple\sio\serror\stests\sfor\stest_async.c.\s(CVS\s3094) +D 2006-02-14T13:25:44 F Makefile.in 5d8dff443383918b700e495de42ec65bc1c8865b F Makefile.linux-gcc 74ba0eadf88748a9ce3fd03d2a3ede2e6715baec F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -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 02531d8b049ae0d8bf1a709ac691dac1d9b147bb +F src/test_async.c 326fc8dcced899473fca2d877a05e446c1c92fef F src/test_md5.c 6c42bc0a3c0b54be34623ff77a0eec32b2fa96e3 F src/test_server.c 087b92a39d883e3fa113cae259d64e4c7438bc96 F src/tokenize.c 382b3bb0ca26eb9153b5d20b246ef512a114a24f @@ -106,6 +106,7 @@ F test/alter3.test a6eec8f454be9b6ce73d8d7dc711453675a10ce7 F test/altermalloc.test 6e1f404ec021eb2ba6582e3c77b0a35cf206b7af F test/analyze.test 2f55535aa335785db1a2f97d3f3831c16c09f8b0 F test/async.test ae59f861f17f3e9076cd557cd93677b7c77e57b5 +F test/async2.test 941fe5e7f44c3ab85fd62cfd615a20980d2ab717 F test/attach.test 036315207c477211470168bf121b1c493f781515 F test/attach2.test 0e6a7c54343c85dd877a1e86073a05176043ed40 F test/attach3.test 63013383adc4380af69779f34f4af19bd49f7cbe @@ -352,7 +353,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P 111a426b3e2fae77c9e6c3cd903fd80652b23720 -R 9e1ed54ea3d446f32aefb065fced0a43 +P f4150c29df2774b4422d4296d913cdbcee62c859 +R aae805bd9f65608fc6774fd635877e1c U danielk1977 -Z fdf8411d6a6b39c9d4eae52b0ecfc18b +Z 364385439507478c54b1444a42b19b2e diff --git a/manifest.uuid b/manifest.uuid index 9ef0ef60fa..fbe63b5790 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f4150c29df2774b4422d4296d913cdbcee62c859 \ No newline at end of file +528dfb71801bb7b8a66944db6f32cc3dc0054118 \ No newline at end of file diff --git a/src/test_async.c b/src/test_async.c index 3760583561..422a0007bd 100644 --- a/src/test_async.c +++ b/src/test_async.c @@ -244,6 +244,8 @@ static struct TestAsyncStaticData { 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 */ + int ioError; /* True if an IO error has occured */ + int nFile; /* Number of open files (from sqlite pov) */ } async = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, @@ -332,7 +334,6 @@ struct AsyncWrite { */ 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 */ @@ -366,6 +367,13 @@ static void addAsyncWrite(AsyncWrite *pWrite){ TRACE(("PUSH %p (%s %s)\n", pWrite, azOpcodeName[pWrite->op], pWrite->pFile ? pWrite->pFile->zName : "-")); + if( pWrite->op==ASYNC_CLOSE ){ + async.nFile--; + if( async.nFile==0 ){ + async.ioError = SQLITE_OK; + } + } + /* Drop the queue mutex */ pthread_mutex_unlock(&async.queueMutex); @@ -374,6 +382,19 @@ static void addAsyncWrite(AsyncWrite *pWrite){ pthread_cond_signal(&async.queueSignal); } +/* +** Increment async.nFile in a thread-safe manner. +*/ +static void incrOpenFileCount(){ + /* We must hold the queue mutex in order to modify async.nFile */ + pthread_mutex_lock(&async.queueMutex); + if( async.nFile==0 ){ + async.ioError = SQLITE_OK; + } + async.nFile++; + pthread_mutex_unlock(&async.queueMutex); +} + /* ** This is a utility function to allocate and populate a new AsyncWrite ** structure and insert it (via addAsyncWrite() ) into the global list. @@ -386,8 +407,8 @@ static int addNewAsyncWrite( const char *zByte ){ AsyncWrite *p; - if( pFile && pFile->ioError!=SQLITE_OK ){ - return pFile->ioError; + if( op!=ASYNC_CLOSE && async.ioError ){ + return async.ioError; } p = sqlite3OsMalloc(sizeof(AsyncWrite) + (zByte?nByte:0)); if( !p ){ @@ -484,8 +505,8 @@ static int asyncRead(OsFile *id, void *obuf, int amt){ /* If an I/O error has previously occurred on this file, then all ** subsequent operations fail. */ - if( pFile->ioError!=SQLITE_OK ){ - return pFile->ioError; + if( async.ioError!=SQLITE_OK ){ + return async.ioError; } /* Grab the write queue mutex for the duration of the call */ @@ -708,7 +729,6 @@ static int asyncOpenFile( p->pMethod = &iomethod; p->pBaseRead = pBaseRead; p->pBaseWrite = pBaseWrite; - p->ioError = SQLITE_OK; *pFile = (OsFile *)p; return SQLITE_OK; @@ -738,6 +758,9 @@ static int asyncOpenExclusive(const char *z, OsFile **ppFile, int delFlag){ *ppFile = 0; } } + if( rc==SQLITE_OK ){ + incrOpenFileCount(); + } return rc; } static int asyncOpenReadOnly(const char *z, OsFile **ppFile){ @@ -746,6 +769,9 @@ static int asyncOpenReadOnly(const char *z, OsFile **ppFile){ if( rc==SQLITE_OK ){ rc = asyncOpenFile(z, ppFile, pBase, 0); } + if( rc==SQLITE_OK ){ + incrOpenFileCount(); + } return rc; } static int asyncOpenReadWrite(const char *z, OsFile **ppFile, int *pReadOnly){ @@ -754,6 +780,9 @@ static int asyncOpenReadWrite(const char *z, OsFile **ppFile, int *pReadOnly){ if( rc==SQLITE_OK ){ rc = asyncOpenFile(z, ppFile, pBase, (*pReadOnly ? 0 : 1)); } + if( rc==SQLITE_OK ){ + incrOpenFileCount(); + } return rc; } @@ -869,16 +898,17 @@ static void asyncEnable(int enable){ static void *asyncWriterThread(void *NotUsed){ AsyncWrite *p = 0; int rc = SQLITE_OK; + int holdingMutex = 0; if( pthread_mutex_trylock(&async.writerMutex) ){ return 0; } while( async.writerHaltNow==0 ){ - int holdingMutex; OsFile *pBase = 0; - pthread_mutex_lock(&async.queueMutex); - holdingMutex = 1; + if( !holdingMutex ){ + pthread_mutex_lock(&async.queueMutex); + } while( (p = async.pQueueFirst)==0 ){ pthread_cond_broadcast(&async.emptySignal); if( async.writerHaltWhenIdle ){ @@ -891,6 +921,7 @@ static void *asyncWriterThread(void *NotUsed){ } } if( p==0 ) break; + holdingMutex = 1; /* 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 @@ -911,11 +942,11 @@ static void *asyncWriterThread(void *NotUsed){ ** SQLITE_ASYNC_TWO_FILEHANDLES was set at compile time and two ** file-handles are open for the particular file being "synced". */ + if( async.ioError!=SQLITE_OK && p->op!=ASYNC_CLOSE ){ + p->op = ASYNC_NOOP; + } if( p->pFile ){ pBase = p->pFile->pBaseWrite; - if( p->pFile->ioError!=SQLITE_OK && p->op!=ASYNC_CLOSE ){ - p->op = ASYNC_NOOP; - } if( p->op==ASYNC_CLOSE || p->op==ASYNC_OPENEXCLUSIVE || @@ -1003,19 +1034,6 @@ static void *asyncWriterThread(void *NotUsed){ default: assert(!"Illegal value for AsyncWrite.op"); } - /* If an error happens, store the error code in the pFile.ioError - ** field. This will prevent any future operations on that file, - ** other than closing it. - ** - ** We cannot report the error back to the connection that requested - ** the I/O since the error happened asynchronously. The connection has - ** already moved on. There really is nobody to report the error to. - */ - if( rc!=SQLITE_OK ){ - p->pFile->ioError = rc; - rc = SQLITE_OK; - } - /* If we didn't hang on to the mutex during the IO op, obtain it now ** so that the AsyncWrite structure can be safely removed from the ** global write-op queue. @@ -1032,16 +1050,42 @@ static void *asyncWriterThread(void *NotUsed){ sqlite3OsFree(p); assert( holdingMutex ); + /* An IO error has occured. We cannot report the error back to the + ** connection that requested the I/O since the error happened + ** asynchronously. The connection has already moved on. There + ** really is nobody to report the error to. + ** + ** The file for which the error occured may have been a database or + ** journal file. Regardless, none of the currently queued operations + ** associated with the same database should now be performed. Nor should + ** any subsequently requested IO on either a database or journal file + ** handle for the same database be accepted until the main database + ** file handle has been closed and reopened. + ** + ** Furthermore, no further IO should be queued or performed on any file + ** handle associated with a database that may have been part of a + ** multi-file transaction that included the database associated with + ** the IO error (i.e. a database ATTACHed to the same handle at some + ** point in time). + */ + if( rc!=SQLITE_OK ){ + async.ioError = rc; + } + /* Drop the queue mutex before continuing to the next write operation ** in order to give other threads a chance to work with the write queue. */ - pthread_mutex_unlock(&async.queueMutex); - if( async.ioDelay>0 ){ - sqlite3OsSleep(async.ioDelay); - }else{ - sched_yield(); + if( !async.pQueueFirst || !async.ioError ){ + pthread_mutex_unlock(&async.queueMutex); + holdingMutex = 0; + if( async.ioDelay>0 ){ + sqlite3OsSleep(async.ioDelay); + }else{ + sched_yield(); + } } } + pthread_mutex_unlock(&async.writerMutex); return 0; } diff --git a/test/async2.test b/test/async2.test new file mode 100644 index 0000000000..d08fb66812 --- /dev/null +++ b/test/async2.test @@ -0,0 +1,111 @@ +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# $Id: async2.test,v 1.1 2006/02/14 13:25:45 danielk1977 Exp $ + + +if {[info commands sqlite3async_enable]==""} { + # The async logic is not built into this system + return +} + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Enable asynchronous IO. + +set setup_script { + CREATE TABLE counter(c); + INSERT INTO counter(c) VALUES (1); +} + +set sql_script { + BEGIN; + UPDATE counter SET c = 2; + CREATE TABLE t1(a PRIMARY KEY, b, c); + CREATE TABLE t2(a PRIMARY KEY, b, c); + COMMIT; + + BEGIN; + UPDATE counter SET c = 3; + INSERT INTO t1 VALUES('abcdefghij', 'four', 'score'); + INSERT INTO t2 VALUES('klmnopqrst', 'and', 'seven'); + COMMIT; + + UPDATE counter SET c = 'FIN'; +} + +db close + +set ::go 1 +for {set n 3} {$::go} {incr n} { + set ::sqlite_io_error_pending 0 + file delete -force test.db test.db-journal + sqlite3 db test.db + execsql $::setup_script + db close + + sqlite3async_enable 1 + sqlite3 db test.db + execsql $::sql_script + db close + + set ::sqlite_io_error_pending $n + sqlite3async_halt idle + sqlite3async_start + sqlite3async_wait + + sqlite3async_enable 0 + set ::sqlite_io_error_pending 0 + sqlite3 db test.db + set c [db eval {SELECT c FROM counter LIMIT 1}] + switch -- $c { + 1 { + do_test async-ioerr-1.1.$n { + execsql { + SELECT name FROM sqlite_master; + } + } {counter} + } + 2 { + do_test async-ioerr-1.2.$n.1 { + execsql { + SELECT * FROM t1; + } + } {} + do_test async-ioerr-1.2.$n.2 { + execsql { + SELECT * FROM t2; + } + } {} + } + 3 { + do_test async-ioerr-1.3.$n.1 { + execsql { + SELECT * FROM t1; + } + } {abcdefghij four score} + do_test async-ioerr-1.3.$n.2 { + execsql { + SELECT * FROM t2; + } + } {klmnopqrst and seven} + } + FIN { + set ::go 0 + } + } + + sqlite3async_enable 0 +} + +catch {db close} +sqlite3async_halt idle +sqlite3async_start +sqlite3async_wait + +finish_test