]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
More locking fixes. Now makes it all the way through quick.test. There
authordrh <drh@noemail.net>
Wed, 29 Aug 2007 00:33:07 +0000 (00:33 +0000)
committerdrh <drh@noemail.net>
Wed, 29 Aug 2007 00:33:07 +0000 (00:33 +0000)
are errors but no assertion faults.  Progress. (CVS 4319)

FossilOrigin-Name: 844d40b8379d3374130e2d94f6e32c2cda34e0ca

manifest
manifest.uuid
src/btmutex.c
src/btree.h
src/btreeInt.h
src/prepare.c

index 514f1bdae4c6ab5dc28fc161cbc049f432f255af..73261cfdf18f322275983c44533ec32360d580cf 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C The\sshared_err\stest\sruns\swith\sno\serrors.\s\sBut\sa\spotential\sdeadlock\shas\sbeen\ndiscovered\sand\sis\sstill\sunfixed.\s(CVS\s4318)
-D 2007-08-28T23:28:08
+C More\slocking\sfixes.\s\sNow\smakes\sit\sall\sthe\sway\sthrough\squick.test.\s\sThere\nare\serrors\sbut\sno\sassertion\sfaults.\s\sProgress.\s(CVS\s4319)
+D 2007-08-29T00:33:07
 F Makefile.in bfcc303429a5d9dcd552d807ee016c77427418c3
 F Makefile.linux-gcc 65241babba6faf1152bf86574477baab19190499
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -80,10 +80,10 @@ F src/alter.c fd78c6005456c727a6cb7c01c5266f2aacf6d401
 F src/analyze.c a14237d869c6bea0846493b59317e4097e81a0b6
 F src/attach.c a52225c75b107be8c5bc144a2b6d20201be3f8f8
 F src/auth.c 083c1205b45e3f52291ec539d396b4fc557856b3
-F src/btmutex.c fc9d8316fccf491f1011ddc4087338f0ce3b5263
+F src/btmutex.c 0abcf98bd6eff466c2c471c96259635169d53981
 F src/btree.c 0241f79766d718d524a28857c3899aee7a728194
-F src/btree.h 03895d1da697e616d918856a26ec63ef8f5aa497
-F src/btreeInt.h 5b1bc919cb80f0dd6baec1cdbae737f9806a7e3b
+F src/btree.h 21cea6453a7ed73c90535329b44910610f3c635e
+F src/btreeInt.h 1fa6510aa8601dc0358a5240d191335236d3cf76
 F src/build.c 99b0b0c44ce7673c00d8c0c97ce536e3b796328b
 F src/callback.c a542236a68060caad378efa30006ca46cf77b1b2
 F src/complete.c 4cf68fd75d60257524cbe74f87351b9848399131
@@ -124,7 +124,7 @@ F src/pager.c 97fd0bb853e01e6890ad9aae97e7f9155330ebdc
 F src/pager.h a9872db184613ae90ae80921f5fd956aa8f3522e
 F src/parse.y 2d2ce439dc6184621fb0b86f4fc5aca7f391a590
 F src/pragma.c 9b989506a1b7c8aecd6befb8235e2f57a4aba7e5
-F src/prepare.c 29ea14cf6b0558f2f80aa53e112bff55f1119e36
+F src/prepare.c 67df42078f348876edf6124aca2c615de5743d75
 F src/printf.c 33d23a68e498006136ca9770579cf2d14a7ec68e
 F src/random.c 4a22746501bf36b0a088c66e38dde5daba6a35da
 F src/select.c 98c367bce3f38c5adfcc97de9ab5c79b0e5dc2b2
@@ -567,7 +567,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
 F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
-P f84550be0a0c9e5859b852863b9a8f8ed3fd6919
-R 9c42e7c4d50fc0b5627cd0c4b7aa2569
+P f093a0d7b29a819605e0527bf23a047e16c32688
+R 61c7afc6574f5208f9d0bca844fc0424
 U drh
-Z 82189ccd3fcca220119ac94e3b76d37a
+Z b78638b8a0836a1559717f1c34999b51
index cb74d82e0f2de98cbec9bd54a57e7cdb5cccb3f1..cfa5f5bc2cbe213f3278d0cc168f4f13eea37d2c 100644 (file)
@@ -1 +1 @@
-f093a0d7b29a819605e0527bf23a047e16c32688
\ No newline at end of file
+844d40b8379d3374130e2d94f6e32c2cda34e0ca
\ No newline at end of file
index f0f64c5704163e4fcbde717458472a3f2b496959..b3dbc364195d2fd639755e041a100e7ee552797d 100644 (file)
@@ -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
@@ -109,6 +109,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
@@ -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 );
index c21b01074b4d0e362fd602ed560774fa613d324b..a7f71fe35cc672d9608329c4cc6095ed3b502181 100644 (file)
@@ -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)
index 47f78ca028dd13f0618096614efe21907047f738..6ce4052fab092947beb2c909e6d37088a8c13537 100644 (file)
@@ -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 */
index 68cd5a7fd04a97c131b9fb11471b90bbbaeb0d64..cf6be6a3acf81bddcd0fa28b4f0931caacafcfa5 100644 (file)
@@ -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 ){