aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/test_async.c68
-rw-r--r--src/test_journal.c21
2 files changed, 74 insertions, 15 deletions
diff --git a/src/test_async.c b/src/test_async.c
index 9cd5957cc..d2fd95aab 100644
--- a/src/test_async.c
+++ b/src/test_async.c
@@ -10,7 +10,7 @@
**
*************************************************************************
**
-** $Id: test_async.c,v 1.54 2009/03/28 15:04:24 drh Exp $
+** $Id: test_async.c,v 1.55 2009/03/28 17:21:52 danielk1977 Exp $
**
** This file contains an example implementation of an asynchronous IO
** backend for SQLite.
@@ -483,7 +483,6 @@ static int async_mutex_lock(pthread_mutex_t *pMutex){
assert(&(aHolder[2])==&asyncdebug.writerMutexHolder);
assert( pthread_self()!=0 );
-
for(iIdx=0; iIdx<3; iIdx++){
if( pMutex==&aMutex[iIdx] ) break;
@@ -610,7 +609,9 @@ static void assert_mutex_is_held(pthread_mutex_t *pMutex){
*/
static void addAsyncWrite(AsyncWrite *pWrite){
/* We must hold the queue mutex in order to modify the queue pointers */
- pthread_mutex_lock(&async.queueMutex);
+ if( pWrite->op!=ASYNC_UNLOCK ){
+ pthread_mutex_lock(&async.queueMutex);
+ }
/* Add the record to the end of the write-op queue */
assert( !pWrite->pNext );
@@ -629,7 +630,9 @@ static void addAsyncWrite(AsyncWrite *pWrite){
}
/* Drop the queue mutex */
- pthread_mutex_unlock(&async.queueMutex);
+ if( pWrite->op!=ASYNC_UNLOCK ){
+ pthread_mutex_unlock(&async.queueMutex);
+ }
/* The writer thread might have been idle because there was nothing
** on the write-op queue for it to do. So wake it up. */
@@ -956,10 +959,12 @@ static int asyncUnlock(sqlite3_file *pFile, int eLock){
AsyncFileData *p = ((AsyncFile *)pFile)->pData;
if( p->zName ){
AsyncFileLock *pLock = &p->lock;
+ pthread_mutex_lock(&async.queueMutex);
pthread_mutex_lock(&async.lockMutex);
pLock->eLock = MIN(pLock->eLock, eLock);
- pthread_mutex_unlock(&async.lockMutex);
rc = addNewAsyncWrite(p, ASYNC_UNLOCK, 0, eLock, 0);
+ pthread_mutex_unlock(&async.lockMutex);
+ pthread_mutex_unlock(&async.queueMutex);
}
return rc;
}
@@ -1526,15 +1531,54 @@ static void *asyncWriterThread(void *pIsStarted){
}
case ASYNC_UNLOCK: {
+ AsyncWrite *pIter;
AsyncFileData *pData = p->pFileData;
int eLock = p->nByte;
- pthread_mutex_lock(&async.lockMutex);
- pData->lock.eAsyncLock = MIN(
- pData->lock.eAsyncLock, MAX(pData->lock.eLock, eLock)
- );
- assert(pData->lock.eAsyncLock>=pData->lock.eLock);
- rc = getFileLock(pData->pLock);
- pthread_mutex_unlock(&async.lockMutex);
+
+ /* When a file is locked by SQLite using the async backend, it is
+ ** locked within the 'real' file-system synchronously. When it is
+ ** unlocked, an ASYNC_UNLOCK event is added to the write-queue to
+ ** unlock the file asynchronously. The design of the async backend
+ ** requires that the 'real' file-system file be locked from the
+ ** time that SQLite first locks it (and probably reads from it)
+ ** until all asynchronous write events that were scheduled before
+ ** SQLite unlocked the file have been processed.
+ **
+ ** This is more complex if SQLite locks and unlocks the file multiple
+ ** times in quick succession. For example, if SQLite does:
+ **
+ ** lock, write, unlock, lock, write, unlock
+ **
+ ** Each "lock" operation locks the file immediately. Each "write"
+ ** and "unlock" operation adds an event to the event queue. If the
+ ** second "lock" operation is performed before the first "unlock"
+ ** operation has been processed asynchronously, then the first
+ ** "unlock" cannot be safely processed as is, since this would mean
+ ** the file was unlocked when the second "write" operation is
+ ** processed. To work around this, when processing an ASYNC_UNLOCK
+ ** operation, SQLite:
+ **
+ ** 1) Unlocks the file to the minimum of the argument passed to
+ ** the xUnlock() call and the current lock from SQLite's point
+ ** of view, and
+ **
+ ** 2) Only unlocks the file at all if this event is the last
+ ** ASYNC_UNLOCK event on this file in the write-queue.
+ */
+ assert( holdingMutex==1 );
+ assert( async.pQueueFirst==p );
+ for(pIter=async.pQueueFirst->pNext; pIter; pIter=pIter->pNext){
+ if( pIter->pFileData==pData && pIter->op==ASYNC_UNLOCK ) break;
+ }
+ if( !pIter ){
+ pthread_mutex_lock(&async.lockMutex);
+ pData->lock.eAsyncLock = MIN(
+ pData->lock.eAsyncLock, MAX(pData->lock.eLock, eLock)
+ );
+ assert(pData->lock.eAsyncLock>=pData->lock.eLock);
+ rc = getFileLock(pData->pLock);
+ pthread_mutex_unlock(&async.lockMutex);
+ }
break;
}
diff --git a/src/test_journal.c b/src/test_journal.c
index fab8d7f9d..f2acfe4da 100644
--- a/src/test_journal.c
+++ b/src/test_journal.c
@@ -15,7 +15,7 @@
** correctly populates and syncs a journal file before writing to a
** corresponding database file.
**
-** $Id: test_journal.c,v 1.13 2009/03/26 11:49:11 danielk1977 Exp $
+** $Id: test_journal.c,v 1.14 2009/03/28 17:21:52 danielk1977 Exp $
*/
#if SQLITE_TEST /* This file is used for testing only */
@@ -206,6 +206,17 @@ struct JtGlobal {
};
static struct JtGlobal g = {0, 0};
+/*
+** Functions to obtain and relinquish a mutex to protect g.pList. The
+** STATIC_PRNG mutex is reused, purely for the sake of convenience.
+*/
+static void enterJtMutex(void){
+ sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PRNG));
+}
+static void leaveJtMutex(void){
+ sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PRNG));
+}
+
extern int sqlite3_io_error_pending;
static void stop_ioerr_simulation(int *piSave){
*piSave = sqlite3_io_error_pending;
@@ -236,11 +247,12 @@ static int jtClose(sqlite3_file *pFile){
jt_file *p = (jt_file *)pFile;
closeTransaction(p);
+ enterJtMutex();
if( p->zName ){
for(pp=&g.pList; *pp!=p; pp=&(*pp)->pNext);
*pp = p->pNext;
}
-
+ leaveJtMutex();
return sqlite3OsClose(p->pReal);
}
@@ -257,7 +269,6 @@ static int jtRead(
return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
}
-
/*
** Parameter zJournal is the name of a journal file that is currently
** open. This function locates and returns the handle opened on the
@@ -274,6 +285,7 @@ static int jtRead(
**/
static jt_file *locateDatabaseHandle(const char *zJournal){
jt_file *pMain = 0;
+ enterJtMutex();
for(pMain=g.pList; pMain; pMain=pMain->pNext){
int nName = strlen(zJournal) - strlen("-journal");
if( (pMain->flags&SQLITE_OPEN_MAIN_DB)
@@ -284,6 +296,7 @@ static jt_file *locateDatabaseHandle(const char *zJournal){
break;
}
}
+ leaveJtMutex();
return pMain;
}
@@ -669,10 +682,12 @@ static int jtOpen(
p->pNext = 0;
p->pWritable = 0;
p->aCksum = 0;
+ enterJtMutex();
if( zName ){
p->pNext = g.pList;
g.pList = p;
}
+ leaveJtMutex();
}
return rc;
}