-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
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
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
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
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,
*/
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 */
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);
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.
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 ){
/* 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 */
p->pMethod = &iomethod;
p->pBaseRead = pBaseRead;
p->pBaseWrite = pBaseWrite;
- p->ioError = SQLITE_OK;
*pFile = (OsFile *)p;
return SQLITE_OK;
*ppFile = 0;
}
}
+ if( rc==SQLITE_OK ){
+ incrOpenFileCount();
+ }
return rc;
}
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){
if( rc==SQLITE_OK ){
rc = asyncOpenFile(z, ppFile, pBase, (*pReadOnly ? 0 : 1));
}
+ if( rc==SQLITE_OK ){
+ incrOpenFileCount();
+ }
return rc;
}
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 ){
}
}
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
** 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 ||
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.
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;
}
--- /dev/null
+#
+# 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