diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/btmutex.c | 98 | ||||
-rw-r--r-- | src/btree.h | 32 | ||||
-rw-r--r-- | src/btreeInt.h | 9 | ||||
-rw-r--r-- | src/prepare.c | 35 |
4 files changed, 126 insertions, 48 deletions
diff --git a/src/btmutex.c b/src/btmutex.c index f0f64c570..b3dbc3641 100644 --- a/src/btmutex.c +++ b/src/btmutex.c @@ -10,7 +10,7 @@ ** ************************************************************************* ** -** $Id: btmutex.c,v 1.3 2007/08/28 23:28:08 drh Exp $ +** $Id: btmutex.c,v 1.4 2007/08/29 00:33:07 drh Exp $ ** ** This file contains code used to implement mutexes on Btree objects. ** This code really belongs in btree.c. But btree.c is getting too @@ -110,6 +110,61 @@ void sqlite3BtreeLeave(Btree *p){ } /* +** Enter the mutex on every Btree associated with a database +** connection. This is needed (for example) prior to parsing +** a statement since we will be comparing table and column names +** against all schemas and we do not want those schemas being +** reset out from under us. +** +** There is a corresponding leave-all procedures. +** +** Enter the mutexes in accending order by BtShared pointer address +** to avoid the possibility of deadlock when two threads with +** two or more btrees in common both try to lock all their btrees +** at the same instant. +*/ +void sqlite3BtreeEnterAll(sqlite3 *db){ + int i; + Btree *p; + assert( sqlite3_mutex_held(db->mutex) ); + for(i=0; i<db->nDb && ((p = db->aDb[i].pBt)==0 || p->sharable==0); i++){} + if( i<db->nDb ){ + while( p->pNext ) p = p->pNext; + while( 1 ){ + if( p->locked ){ + sqlite3_mutex_leave(p->pBt->mutex); + p->locked = 0; + } + if( p->pPrev==0 ) break; + p = p->pPrev; + } + while( p ){ + p->wantToLock++; + sqlite3_mutex_enter(p->pBt->mutex); + p->locked = 1; + p = p->pNext; + } + } +} +void sqlite3BtreeLeaveAll(sqlite3 *db){ + int i; + Btree *p; + assert( sqlite3_mutex_held(db->mutex) ); + for(i=0; i<db->nDb && ((p = db->aDb[i].pBt)==0 || p->sharable==0); i++){} + if( i<db->nDb ){ + while( p->pPrev ) p = p->pPrev; + while( p ){ + p->wantToLock--; + if( p->wantToLock==0 ){ + sqlite3_mutex_leave(p->pBt->mutex); + p->locked = 0; + } + p = p->pNext; + } + } +} + +/* ** Potentially dd a new Btree pointer to a BtreeMutexArray. ** Really only add the Btree if it can possibly be shared with ** another database connection. @@ -122,31 +177,32 @@ void sqlite3BtreeLeave(Btree *p){ ** The number of shared btrees will always be small (usually 0 or 1) ** so an insertion sort is an adequate algorithm here. */ -void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pSet, Btree *pBtree){ +void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pArray, Btree *pBtree){ int i, j; BtShared *pBt; if( !pBtree->sharable ) return; #ifndef NDEBUG { - for(i=0; i<pSet->nMutex; i++){ - assert( pSet->aBtree[i]!=pBtree ); + for(i=0; i<pArray->nMutex; i++){ + assert( pArray->aBtree[i]!=pBtree ); } } #endif - assert( pSet->nMutex>=0 ); - assert( pSet->nMutex<sizeof(pSet->aBtree)/sizeof(pSet->aBtree[0])-1 ); + assert( pArray->nMutex>=0 ); + assert( pArray->nMutex<sizeof(pArray->aBtree)/sizeof(pArray->aBtree[0])-1 ); pBt = pBtree->pBt; - for(i=0; i<pSet->nMutex; i++){ - assert( pSet->aBtree[i]!=pBtree ); - if( pSet->aBtree[i]->pBt>pBt ){ - for(j=pSet->nMutex; j>i; j--){ - pSet->aBtree[j] = pSet->aBtree[j-1]; + for(i=0; i<pArray->nMutex; i++){ + assert( pArray->aBtree[i]!=pBtree ); + if( pArray->aBtree[i]->pBt>pBt ){ + for(j=pArray->nMutex; j>i; j--){ + pArray->aBtree[j] = pArray->aBtree[j-1]; } - pSet->aBtree[i] = pBtree; + pArray->aBtree[i] = pBtree; + pArray->nMutex++; return; } } - pSet->aBtree[pSet->nMutex++] = pBtree; + pArray->aBtree[pArray->nMutex++] = pBtree; } /* @@ -154,12 +210,12 @@ void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pSet, Btree *pBtree){ ** called at the beginning of sqlite3VdbeExec(). The mutexes are ** exited at the end of the same function. */ -void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pSet){ +void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pArray){ int i; - for(i=0; i<pSet->nMutex; i++){ - Btree *p = pSet->aBtree[i]; + for(i=0; i<pArray->nMutex; i++){ + Btree *p = pArray->aBtree[i]; /* Some basic sanity checking */ - assert( i==0 || pSet->aBtree[i-1]->pBt<p->pBt ); + assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt ); assert( !p->locked || p->wantToLock>0 ); assert( p->sharable ); @@ -177,12 +233,12 @@ void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pSet){ /* ** Leave the mutex of every btree in the group. */ -void sqlite3BtreeMutexArrayLeave(BtreeMutexArray *pSet){ +void sqlite3BtreeMutexArrayLeave(BtreeMutexArray *pArray){ int i; - for(i=0; i<pSet->nMutex; i++){ - Btree *p = pSet->aBtree[i]; + for(i=0; i<pArray->nMutex; i++){ + Btree *p = pArray->aBtree[i]; /* Some basic sanity checking */ - assert( i==0 || pSet->aBtree[i-1]->pBt<p->pBt ); + assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt ); assert( p->locked ); assert( p->sharable ); assert( p->wantToLock>0 ); diff --git a/src/btree.h b/src/btree.h index c21b01074..a7f71fe35 100644 --- a/src/btree.h +++ b/src/btree.h @@ -13,7 +13,7 @@ ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. ** -** @(#) $Id: btree.h,v 1.88 2007/08/28 23:28:08 drh Exp $ +** @(#) $Id: btree.h,v 1.89 2007/08/29 00:33:07 drh Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -107,21 +107,6 @@ void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); int sqlite3BtreeSchemaLocked(Btree *); int sqlite3BtreeLockTable(Btree *, int, u8); -/* -** If we are not using shared cache, then there is no need to -** use mutexes to access the BtShared structures. So make the -** Enter and Leave procedures no-ops. -*/ -#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE) - void sqlite3BtreeEnter(Btree*); - void sqlite3BtreeLeave(Btree*); -# define sqlite3BtreeMutexHeld(X) sqlite3_mutex_held(X) -#else -# define sqlite3BtreeEnter(X) -# define sqlite3BtreeLeave(X) -# define sqlite3BtreeMutexHeld(X) 1 -#endif - const char *sqlite3BtreeGetFilename(Btree *); const char *sqlite3BtreeGetDirname(Btree *); const char *sqlite3BtreeGetJournalname(Btree *); @@ -182,11 +167,26 @@ void sqlite3BtreeCursorList(Btree*); int sqlite3BtreePageDump(Btree*, int, int recursive); #endif +/* +** If we are not using shared cache, then there is no need to +** use mutexes to access the BtShared structures. So make the +** Enter and Leave procedures no-ops. +*/ #if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE + void sqlite3BtreeEnter(Btree*); + void sqlite3BtreeLeave(Btree*); +# define sqlite3BtreeMutexHeld(X) sqlite3_mutex_held(X) + void sqlite3BtreeEnterAll(sqlite3*); + void sqlite3BtreeLeaveAll(sqlite3*); void sqlite3BtreeMutexArrayEnter(BtreeMutexArray*); void sqlite3BtreeMutexArrayLeave(BtreeMutexArray*); void sqlite3BtreeMutexArrayInsert(BtreeMutexArray*, Btree*); #else +# define sqlite3BtreeEnter(X) +# define sqlite3BtreeLeave(X) +# define sqlite3BtreeMutexHeld(X) 1 +# define sqlite3BtreeEnterAll(X) +# define sqlite3BtreeLeaveAll(X) # define sqlite3BtreeMutexArrayEnter(X) # define sqlite3BtreeMutexArrayLeave(X) # define sqlite3BtreeMutexArrayInsert(X,Y) diff --git a/src/btreeInt.h b/src/btreeInt.h index 47f78ca02..6ce4052fa 100644 --- a/src/btreeInt.h +++ b/src/btreeInt.h @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btreeInt.h,v 1.11 2007/08/28 22:24:35 drh Exp $ +** $Id: btreeInt.h,v 1.12 2007/08/29 00:33:07 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -331,7 +331,7 @@ struct Btree { u8 sharable; /* True if we can share pBt with other pSqlite */ u8 locked; /* True if pSqlite currently has pBt locked */ int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */ - Btree *pNext; /* List of Btrees with the same pSqlite value */ + Btree *pNext; /* List of other sharable Btrees from the same pSqlite */ Btree *pPrev; /* Back pointer of the same list */ }; @@ -358,7 +358,10 @@ struct Btree { ** ** Fields in this structure are accessed under the BtShared.mutex ** mutex, except for nRef and pNext which are accessed under the -** global SQLITE_MUTEX_STATIC_MASTER mutex. +** global SQLITE_MUTEX_STATIC_MASTER mutex. The pPager field +** may not be modified once it is initially set as long as nRef>0. +** The pSchema field may be set once under BtShared.mutex and +** thereafter is unchanged as long as nRef>0. */ struct BtShared { Pager *pPager; /* The page cache */ diff --git a/src/prepare.c b/src/prepare.c index 68cd5a7fd..cf6be6a3a 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -13,7 +13,7 @@ ** interface, and routines that contribute to loading the database schema ** from disk. ** -** $Id: prepare.c,v 1.58 2007/08/22 02:56:44 drh Exp $ +** $Id: prepare.c,v 1.59 2007/08/29 00:33:07 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -47,6 +47,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **azColName){ sqlite3 *db = pData->db; int iDb = pData->iDb; + assert( sqlite3_mutex_held(db->mutex) ); pData->rc = SQLITE_OK; DbClearProperty(db, iDb, DB_Empty); if( db->mallocFailed ){ @@ -156,6 +157,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ assert( iDb>=0 && iDb<db->nDb ); assert( db->aDb[iDb].pSchema ); + assert( sqlite3_mutex_held(db->mutex) ); /* zMasterSchema and zInitScript are set to point at the master schema ** and initialisation script appropriate for the database being @@ -197,9 +199,11 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ } return SQLITE_OK; } + sqlite3BtreeEnter(pDb->pBt); rc = sqlite3BtreeCursor(pDb->pBt, MASTER_ROOT, 0, 0, 0, &curMain); if( rc!=SQLITE_OK && rc!=SQLITE_EMPTY ){ sqlite3SetString(pzErrMsg, sqlite3ErrStr(rc), (char*)0); + sqlite3BtreeLeave(pDb->pBt); return rc; } @@ -228,6 +232,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ if( rc ){ sqlite3SetString(pzErrMsg, sqlite3ErrStr(rc), (char*)0); sqlite3BtreeCloseCursor(curMain); + sqlite3BtreeLeave(pDb->pBt); return rc; } }else{ @@ -251,6 +256,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ sqlite3BtreeCloseCursor(curMain); sqlite3SetString(pzErrMsg, "attached databases must use the same" " text encoding as main database", (char*)0); + sqlite3BtreeLeave(pDb->pBt); return SQLITE_ERROR; } } @@ -277,6 +283,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ if( pDb->pSchema->file_format>SQLITE_MAX_FILE_FORMAT ){ sqlite3BtreeCloseCursor(curMain); sqlite3SetString(pzErrMsg, "unsupported file format", (char*)0); + sqlite3BtreeLeave(pDb->pBt); return SQLITE_ERROR; } @@ -321,6 +328,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ DbSetProperty(db, iDb, DB_SchemaLoaded); rc = SQLITE_OK; } + sqlite3BtreeLeave(pDb->pBt); return rc; } @@ -338,6 +346,7 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){ int i, rc; int commit_internal = !(db->flags&SQLITE_InternChanges); + assert( sqlite3_mutex_held(db->mutex) ); if( db->init.busy ) return SQLITE_OK; rc = SQLITE_OK; db->init.busy = 1; @@ -377,6 +386,7 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){ int sqlite3ReadSchema(Parse *pParse){ int rc = SQLITE_OK; sqlite3 *db = pParse->db; + assert( sqlite3_mutex_held(db->mutex) ); if( !db->init.busy ){ rc = sqlite3Init(db, &pParse->zErrMsg); } @@ -399,6 +409,7 @@ static int schemaIsValid(sqlite3 *db){ int cookie; int allOk = 1; + assert( sqlite3_mutex_held(db->mutex) ); for(iDb=0; allOk && iDb<db->nDb; iDb++){ Btree *pBt; pBt = db->aDb[iDb].pBt; @@ -435,6 +446,7 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){ ** more likely to cause a segfault than -1 (of course there are assert() ** statements too, but it never hurts to play the odds). */ + assert( sqlite3_mutex_held(db->mutex) ); if( pSchema ){ for(i=0; i<db->nDb; i++){ if( db->aDb[i].pSchema==pSchema ){ @@ -475,11 +487,15 @@ int sqlite3Prepare( */ for(i=0; i<db->nDb; i++) { Btree *pBt = db->aDb[i].pBt; - if( pBt && sqlite3BtreeSchemaLocked(pBt) ){ - const char *zDb = db->aDb[i].zName; - sqlite3Error(db, SQLITE_LOCKED, "database schema is locked: %s", zDb); - sqlite3SafetyOff(db); - return SQLITE_LOCKED; + if( pBt ){ + int rc; + rc = sqlite3BtreeSchemaLocked(pBt); + if( rc ){ + const char *zDb = db->aDb[i].zName; + sqlite3Error(db, SQLITE_LOCKED, "database schema is locked: %s", zDb); + sqlite3SafetyOff(db); + return SQLITE_LOCKED; + } } } @@ -575,7 +591,9 @@ static int sqlite3LockAndPrepare( return SQLITE_MISUSE; } sqlite3_mutex_enter(db->mutex); + sqlite3BtreeEnterAll(db); rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, ppStmt, pzTail); + sqlite3BtreeLeaveAll(db); sqlite3_mutex_leave(db->mutex); return rc; } @@ -591,13 +609,14 @@ int sqlite3Reprepare(Vdbe *p){ const char *zSql; sqlite3 *db; + assert( sqlite3_mutex_held(sqlite3VdbeDb(p)->mutex) ); zSql = sqlite3VdbeGetSql(p); if( zSql==0 ){ return 0; } db = sqlite3VdbeDb(p); assert( sqlite3_mutex_held(db->mutex) ); - rc = sqlite3Prepare(db, zSql, -1, 0, &pNew, 0); + rc = sqlite3LockAndPrepare(db, zSql, -1, 0, &pNew, 0); if( rc ){ assert( pNew==0 ); return 0; @@ -666,7 +685,7 @@ static int sqlite3Prepare16( sqlite3_mutex_enter(db->mutex); zSql8 = sqlite3Utf16to8(db, zSql, nBytes); if( zSql8 ){ - rc = sqlite3Prepare(db, zSql8, -1, saveSqlFlag, ppStmt, &zTail8); + rc = sqlite3LockAndPrepare(db, zSql8, -1, saveSqlFlag, ppStmt, &zTail8); } if( zTail8 && pzTail ){ |