]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Work toward correct btree locking in a multithreaded environment. (CVS 4307)
authordrh <drh@noemail.net>
Tue, 28 Aug 2007 02:27:51 +0000 (02:27 +0000)
committerdrh <drh@noemail.net>
Tue, 28 Aug 2007 02:27:51 +0000 (02:27 +0000)
FossilOrigin-Name: b8cc493b47e618648f645ab73eb0253739e03fcd

13 files changed:
Makefile.in
main.mk
manifest
manifest.uuid
src/btmutex.c [new file with mode: 0644]
src/btree.c
src/btree.h
src/build.c
src/sqliteInt.h
src/vdbe.c
src/vdbe.h
src/vdbeInt.h
src/vdbeaux.c

index e5040020d35a0e6aaab5723fb013f0dfe6dd63f6..558e543228f8630e7a1ac7df795b8d7fb1a8704f 100644 (file)
@@ -120,7 +120,7 @@ TCC += -DSQLITE_OMIT_LOAD_EXTENSION=1
 
 # Object files for the SQLite library.
 #
-LIBOBJ = alter.lo analyze.lo attach.lo auth.lo btree.lo build.lo \
+LIBOBJ = alter.lo analyze.lo attach.lo auth.lo btmutex.lo btree.lo build.lo \
          callback.lo complete.lo date.lo \
          delete.lo expr.lo func.lo hash.lo journal.lo insert.lo loadext.lo \
          main.lo malloc.lo mem1.lo mem2.lo mutex.lo \
@@ -138,6 +138,7 @@ SRC = \
   $(TOP)/src/analyze.c \
   $(TOP)/src/attach.c \
   $(TOP)/src/auth.c \
+  $(TOP)/src/btmutex.c \
   $(TOP)/src/btree.c \
   $(TOP)/src/btree.h \
   $(TOP)/src/build.c \
@@ -340,6 +341,9 @@ attach.lo:  $(TOP)/src/attach.c $(HDR)
 auth.lo:       $(TOP)/src/auth.c $(HDR)
        $(LTCOMPILE) -c $(TOP)/src/auth.c
 
+btmutex.lo:    $(TOP)/src/btmutex.c $(HDR)
+       $(LTCOMPILE) -c $(TOP)/src/btmutex.c
+
 btree.lo:      $(TOP)/src/btree.c $(HDR) $(TOP)/src/pager.h
        $(LTCOMPILE) -c $(TOP)/src/btree.c
 
diff --git a/main.mk b/main.mk
index 5ff4fb2f76c0f4b72a674e34b82d2e226f8735cf..a68433f1f3d166719721060a83841fcc6c12bb84 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -55,7 +55,7 @@ TCCX = $(TCC) $(OPTS) $(THREADSAFE) $(USLEEP) -I. -I$(TOP)/src
 
 # Object files for the SQLite library.
 #
-LIBOBJ+= alter.o analyze.o attach.o auth.o btree.o build.o \
+LIBOBJ+= alter.o analyze.o attach.o auth.o btmutex.o btree.o build.o \
          callback.o complete.o date.o delete.o \
          expr.o func.o hash.o insert.o journal.o loadext.o \
          main.o malloc.o mem1.o mem2.o mutex.o \
@@ -91,6 +91,7 @@ SRC = \
   $(TOP)/src/analyze.c \
   $(TOP)/src/attach.c \
   $(TOP)/src/auth.c \
+  $(TOP)/src/btmutex.c \
   $(TOP)/src/btree.c \
   $(TOP)/src/btree.h \
   $(TOP)/src/build.c \
@@ -194,11 +195,15 @@ SRC += \
 # Source code to the test files.
 #
 TESTSRC = \
+  $(TOP)/src/btmutex.c \
   $(TOP)/src/btree.c \
   $(TOP)/src/date.c \
   $(TOP)/src/func.c \
   $(TOP)/src/insert.c \
   $(TOP)/src/malloc.c \
+  $(TOP)/src/mem1.c \
+  $(TOP)/src/mem2.c \
+  $(TOP)/src/mutex.c \
   $(TOP)/src/os.c \
   $(TOP)/src/os_os2.c \
   $(TOP)/src/os_unix.c \
@@ -337,6 +342,9 @@ attach.o:   $(TOP)/src/attach.c $(HDR)
 auth.o:        $(TOP)/src/auth.c $(HDR)
        $(TCCX) -c $(TOP)/src/auth.c
 
+btmutex.o:     $(TOP)/src/btmutex.c $(HDR) $(TOP)/src/btreeInt.h
+       $(TCCX) -c $(TOP)/src/btmutex.c
+
 btree.o:       $(TOP)/src/btree.c $(HDR) $(TOP)/src/pager.h
        $(TCCX) -c $(TOP)/src/btree.c
 
index c43eabecc5d139d4d8640f537e64456ef3b529fa..9c206a56fdb55fce8ab3b8e991298106f85446af 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,6 +1,6 @@
-C Disable\stests\sthat\sdebug\son\sSQLITE_MEMDEBUG\swhen\sthat\smacro\sis\snot\sdefined.\s(CVS\s4306)
-D 2007-08-27T23:48:24
-F Makefile.in 51bdcd1708f0971bc6b5d8eb47f00d127a899345
+C Work\stoward\scorrect\sbtree\slocking\sin\sa\smultithreaded\senvironment.\s(CVS\s4307)
+D 2007-08-28T02:27:52
+F Makefile.in e8296e112b8942a96c0ed504398bd0d43e3c67ce
 F Makefile.linux-gcc 65241babba6faf1152bf86574477baab19190499
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
 F VERSION 6200589421a0dfe968cd39c431fc62277b963540
@@ -63,7 +63,7 @@ F ext/icu/README.txt 3b130aa66e7a681136f6add198b076a2f90d1e33
 F ext/icu/icu.c 61a345d8126686aa3487aa8d2d0f68abd655f7a4
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
 F ltmain.sh 56abb507100ed2d4261f6dd1653dec3cf4066387
-F main.mk 238b00009433760c469ceb94f37f49e76dceae45
+F main.mk 6b817f36cdfe3c1846cf6bac96afe8f73add5fe5
 F mkdll.sh 37fa8a7412e51b5ab2bc6d4276135f022a0feffb
 F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
 F mkextw.sh 1a866b53637dab137191341cc875575a5ca110fb
@@ -80,10 +80,11 @@ F src/alter.c fd78c6005456c727a6cb7c01c5266f2aacf6d401
 F src/analyze.c a14237d869c6bea0846493b59317e4097e81a0b6
 F src/attach.c a52225c75b107be8c5bc144a2b6d20201be3f8f8
 F src/auth.c 083c1205b45e3f52291ec539d396b4fc557856b3
-F src/btree.c dbb8ec48e619fc4900f42cafffc7de90b4b21250
-F src/btree.h 76c89673981cb77575300c0b78a76eaa00a28743
+F src/btmutex.c e11c59a7a68d9f295a1abfb753ffa4755b5037b1
+F src/btree.c 850cd5de860e01233153ade9b24ffc775a794e8e
+F src/btree.h a8fb26c56b745b57446c2bf29133619261313051
 F src/btreeInt.h c1ba892252bc4dd76ad66da056536c64b23456e3
-F src/build.c bc7406e2ea5bfa8276ee1abeae1db27a98fd0b33
+F src/build.c 08001e8a12b06178193dc4a8f24610f58de80dae
 F src/callback.c a542236a68060caad378efa30006ca46cf77b1b2
 F src/complete.c 4cf68fd75d60257524cbe74f87351b9848399131
 F src/date.c af235f38f50809abd0a96da3bb3e0cc32be6226e
@@ -127,7 +128,7 @@ F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96
 F src/shell.c ac29402b538515fa4697282387be9c1205e6e9eb
 F src/sqlite.h.in 4cf42ce749e4bdf13b9bb4959e91439c3ce8a054
 F src/sqlite3ext.h 9a26028378c288af500d8b94ed079666fed5806b
-F src/sqliteInt.h ec674206f8a6a475ac58552c40e77d88d66862a2
+F src/sqliteInt.h e681df44a19ffb6750d129802f124132ae240c83
 F src/sqliteLimit.h 1bcbbdfa856f8b71b561abb31edb864b0eca1d12
 F src/table.c c725e47f6f3092b9a7b569fc58e408e2173ee008
 F src/tclsqlite.c d76af53f45c9e9f7f7d39531fa4c7bee7d0adad6
@@ -157,11 +158,11 @@ F src/update.c e89b980b443d44b68bfc0b1746cdb6308e049ac9
 F src/utf.c 4af6259d5906b5a1bf3035cc387c4d7907bdd56e
 F src/util.c 3f9c0387b54f977726790f52ab92cd3d9379b367
 F src/vacuum.c 5ec133b69edf581a232af7e2b01f45c9f2b8be32
-F src/vdbe.c 9d4d00589c174aad9a616f1615464ddddebba0ec
-F src/vdbe.h 001c5b257567c1d3de7feb2203aac71d0d7b16a3
-F src/vdbeInt.h 2bb602c9cb19191d574517bdec0f6c0b600bd3da
+F src/vdbe.c 62d210babaac906a5847d7bd4c71e7b114595e85
+F src/vdbe.h 5b3ee0fd91a08579f514713038fa7dbef9edf407
+F src/vdbeInt.h 0681e0b74a43d3adfec65780d73b2db8f826c7c9
 F src/vdbeapi.c bdd0aea216744482dd1b7fab56de18ba5b6fbdf4
-F src/vdbeaux.c 2889abf9a6fe954de158bea07652d46101577d08
+F src/vdbeaux.c bffdf7b69de21a70520260d359e19df64f31aea4
 F src/vdbeblob.c d12ed95dac0992e1e372d079d76af047cc42f7c7
 F src/vdbefifo.c 334c838c8f42d61a94813d136019ee566b5dc2f6
 F src/vdbemem.c 896fa3f8df9d2661eb15c7ce361857741b447268
@@ -562,7 +563,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
 F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
-P bc6d73d015327a9cf6b687aaf4b3f0d07e0fd484
-R 2349531d73bc37131a9a7284d5308814
+P 741d6fb096dcb232871d3a8468c386022afcf554
+R ff3e167d1bd072e0909067b1f2888076
 U drh
-Z 56024ff7bdc953200a8a79d2b652027f
+Z 6d8ca04435be2f950a1ddef0045b914b
index 4a7d87bc4df901675f986ff5799eedeb2216098a..5ea8d95fadc2fa23f273feb5f16debdce418abc8 100644 (file)
@@ -1 +1 @@
-741d6fb096dcb232871d3a8468c386022afcf554
\ No newline at end of file
+b8cc493b47e618648f645ab73eb0253739e03fcd
\ No newline at end of file
diff --git a/src/btmutex.c b/src/btmutex.c
new file mode 100644 (file)
index 0000000..4725910
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+** 2007 August 27
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    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: btmutex.c,v 1.1 2007/08/28 02:27:52 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
+** big and we want to break it down some.  This packaged seemed like
+** a good breakout.
+*/
+#include "btreeInt.h"
+#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
+
+
+/*
+** Enter a mutex on the given BTree object.
+**
+** If the object is not sharable, then no mutex is ever required
+** and this routine is a no-op.  The underlying mutex is non-recursive.
+** But we keep a reference count in Btree.wantToLock so the behavior
+** of this interface is recursive.
+**
+** To avoid deadlocks, multiple Btrees are locked in the same order
+** by all database connections.  The p->pNext is a list of other
+** Btrees belonging to the same database connection as the p Btree
+** which need to be locked after p.  If we cannot get a lock on
+** p, then first unlock all of the others on p->pNext, then wait
+** for the lock to become available on p, then relock all of the
+** subsequent Btrees that desire a lock.
+*/
+void sqlite3BtreeEnter(Btree *p){
+  Btree *pLater;
+
+  /* Some basic sanity checking on the Btree.  The list of Btrees
+  ** connected by pNext and pPrev should be in sorted order by
+  ** Btree.pBt value. All elements of the list should belong to
+  ** the same connection. Only shared Btrees are on the list. */
+  assert( p->pNext==0 || p->pNext->pBt>p->pBt );
+  assert( p->pPrev==0 || p->pPrev->pBt<p->pBt );
+  assert( p->pNext==0 || p->pNext->pSqlite==p->pSqlite );
+  assert( p->pPrev==0 || p->pPrev->pSqlite==p->pSqlite );
+  assert( p->sharable || (p->pNext==0 && p->pPrev==0) );
+
+  /* Check for locking consistency */
+  assert( !p->locked || p->wantToLock>0 );
+  assert( p->sharable || p->wantToLock==0 );
+
+  /* We should already hold a lock on the database connection */
+  assert( sqlite3BtreeMutexHeld(p->pSqlite->mutex) );
+
+  if( !p->sharable ) return;
+  p->wantToLock++;
+  if( p->locked ) return;
+
+  /* In most cases, we should be able to acquire the lock we
+  ** want without having to go throught the ascending lock
+  ** procedure that follows.  Just be sure not to block.
+  */
+  if( sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){
+    p->locked = 1;
+    return;
+  }
+
+  /* To avoid deadlock, first release all locks with a larger
+  ** BtShared address.  Then acquire our lock.  Then reacquire
+  ** the other BtShared locks that we used to hold in ascending
+  ** order.
+  */
+  for(pLater=p->pNext; pLater; pLater=pLater->pNext){
+    assert( pLater->sharable );
+    assert( pLater->pNext==0 || pLater->pNext->pBt>pLater->pBt );
+    assert( !pLater->locked || pLater->wantToLock>0 );
+    if( pLater->locked ){
+      sqlite3_mutex_leave(pLater->pBt->mutex);
+      pLater->locked = 0;
+    }
+  }
+  sqlite3_mutex_enter(p->pBt->mutex);
+  for(pLater=p->pNext; pLater; pLater=pLater->pNext){
+    if( pLater->wantToLock ){
+      sqlite3_mutex_enter(pLater->pBt->mutex);
+      pLater->locked = 1;
+    }
+  }
+}
+
+/*
+** Exit the recursive mutex on a Btree.
+*/
+void sqlite3BtreeLeave(Btree *p){
+  if( p->sharable ){
+    assert( p->wantToLock>0 );
+    p->wantToLock--;
+    if( p->wantToLock==0 ){
+      assert( p->locked );
+      sqlite3_mutex_leave(p->pBt->mutex);
+      p->locked = 0;
+    }
+  }
+}
+
+/*
+** Potentially dd a new Btree pointer to a BtreeMutexSet.
+** Really only add the Btree if it can possibly be shared with
+** another database connection.
+**
+** The Btrees are kept in sorted order by pBtree->pBt.  That
+** way when we go to enter all the mutexes, we can enter them
+** in order without every having to backup and retry and without
+** worrying about deadlock.
+**
+** The number of shared btrees will always be small (usually 0 or 1)
+** so an insertion sort is an adequate algorithm here.
+*/
+void sqlite3BtreeMutexSetInsert(BtreeMutexSet *pSet, 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 );
+    }
+  }
+#endif
+  assert( pSet->nMutex>=0 );
+  assert( pSet->nMutex<sizeof(pSet->aBtree)/sizeof(pSet->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];
+      }
+      pSet->aBtree[i] = pBtree;
+      return;
+    }
+  }
+  pSet->aBtree[pSet->nMutex++] = pBtree;
+}
+
+/*
+** Enter the mutex of every btree in the set.  This routine is
+** called at the beginning of sqlite3VdbeExec().  The mutexes are
+** exited at the end of the same function.
+*/
+void sqlite3BtreeMutexSetEnter(BtreeMutexSet *pSet){
+  int i;
+  for(i=0; i<pSet->nMutex; i++){
+    Btree *p = pSet->aBtree[i];
+    /* Some basic sanity checking */
+    assert( i==0 || pSet->aBtree[i-1]->pBt<p->pBt );
+    assert( !p->locked || p->wantToLock>0 );
+    assert( p->sharable );
+
+    /* We should already hold a lock on the database connection */
+    assert( sqlite3BtreeMutexHeld(p->pSqlite->mutex) );
+
+    p->wantToLock++;
+    if( !p->locked ){
+      sqlite3_mutex_enter(p->pBt->mutex);
+    }
+  }
+}
+
+/*
+** Leave the mutex of every btree in the set.
+*/
+void sqlite3BtreeMutexSetLeave(BtreeMutexSet *pSet){
+  int i;
+  for(i=0; i<pSet->nMutex; i++){
+    Btree *p = pSet->aBtree[i];
+    /* Some basic sanity checking */
+    assert( i==0 || pSet->aBtree[i-1]->pBt<p->pBt );
+    assert( p->locked );
+    assert( p->sharable );
+    assert( p->wantToLock>0 );
+
+    /* We should already hold a lock on the database connection */
+    assert( sqlite3BtreeMutexHeld(p->pSqlite->mutex) );
+
+    p->wantToLock--;
+    if( p->wantToLock==0 ){
+      sqlite3_mutex_leave(p->pBt->mutex);
+    }
+  }
+}
+
+
+#endif  /* SQLITE_THREADSAFE && !SQLITE_OMIT_SHARED_CACHE */
index 3d4e55ac880e50c314cee8b5f3178d2681e698a7..cd7b3e1a5ce5567b0d9e315059ec14c34f305395 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.412 2007/08/24 11:52:29 danielk1977 Exp $
+** $Id: btree.c,v 1.413 2007/08/28 02:27:52 drh Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** See the header comment on "btreeInt.h" for additional information.
@@ -1386,98 +1386,6 @@ int sqlite3BtreeClose(Btree *p){
   return SQLITE_OK;
 }
 
-#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
-/*
-** Enter a mutex on the given BTree object.
-**
-** If the object is not sharable, then no mutex is ever required
-** and this routine is a no-op.  The underlying mutex is non-recursive.
-** But we keep a reference count in Btree.wantToLock so the behavior
-** of this interface is recursive.
-**
-** To avoid deadlocks, multiple Btrees are locked in the same order
-** by all database connections.  The p->pNext is a list of other
-** Btrees belonging to the same database connection as the p Btree
-** which need to be locked after p.  If we cannot get a lock on
-** p, then first unlock all of the others on p->pNext, then wait
-** for the lock to become available on p, then relock all of the
-** subsequent Btrees that desire a lock.
-*/
-void sqlite3BtreeEnter(Btree *p){
-  Btree *pLater;
-
-  /* Some basic sanity checking on the Btree.  The list of Btrees
-  ** connected by pNext and pPrev should be in sorted order by
-  ** Btree.pBt value. All elements of the list should belong to
-  ** the same connection. Only shared Btrees are on the list. */
-  assert( p->pNext==0 || p->pNext->pBt>p->pBt );
-  assert( p->pPrev==0 || p->pPrev->pBt<p->pBt );
-  assert( p->pNext==0 || p->pNext->pSqlite==p->pSqlite );
-  assert( p->pPrev==0 || p->pPrev->pSqlite==p->pSqlite );
-  assert( p->sharable || (p->pNext==0 && p->pPrev==0) );
-
-  /* Check for locking consistency */
-  assert( !p->locked || p->wantToLock>0 );
-  assert( p->sharable || p->wantToLock==0 );
-
-  /* We should already hold a lock on the database connection */
-  assert( sqlite3BtreeMutexHeld(p->pSqlite->mutex) );
-
-  if( !p->sharable ) return;
-  p->wantToLock++;
-  if( p->locked ) return;
-
-  /* In most cases, we should be able to acquire the lock we
-  ** want without having to go throught the ascending lock
-  ** procedure that follows.  Just be sure not to block.
-  */
-  if( sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){
-    p->locked = 1;
-    return;
-  }
-
-  /* To avoid deadlock, first release all locks with a larger
-  ** BtShared address.  Then acquire our lock.  Then reacquire
-  ** the other BtShared locks that we used to hold in ascending
-  ** order.
-  */
-  for(pLater=p->pNext; pLater; pLater=pLater->pNext){
-    assert( pLater->sharable );
-    assert( pLater->pNext==0 || pLater->pNext->pBt>pLater->pBt );
-    assert( !pLater->locked || pLater->wantToLock>0 );
-    if( pLater->locked ){
-      sqlite3_mutex_leave(pLater->pBt->mutex);
-      pLater->locked = 0;
-    }
-  }
-  sqlite3_mutex_enter(p->pBt->mutex);
-  for(pLater=p->pNext; pLater; pLater=pLater->pNext){
-    if( pLater->wantToLock ){
-      sqlite3_mutex_enter(pLater->pBt->mutex);
-      pLater->locked = 1;
-    }
-  }
-}
-#endif /* !SQLITE_OMIT_SHARED_CACHE */
-
-/*
-** Exit the recursive mutex on a Btree.
-*/
-#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
-void sqlite3BtreeLeave(Btree *p){
-  if( p->sharable ){
-    assert( p->wantToLock>0 );
-    p->wantToLock--;
-    if( p->wantToLock==0 ){
-      assert( p->locked );
-      sqlite3_mutex_leave(p->pBt->mutex);
-      p->locked = 0;
-    }
-  }
-}
-#endif
-
-
 #if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
 /*
 ** Short-cuts for entering and leaving mutexes on a cursor.
@@ -6680,7 +6588,6 @@ char *sqlite3BtreeIntegrityCheck(
 */
 const char *sqlite3BtreeGetFilename(Btree *p){
   assert( p->pBt->pPager!=0 );
-  assert( sqlite3BtreeMutexHeld(p->pBt->mutex) );
   return sqlite3PagerFilename(p->pBt->pPager);
 }
 
@@ -6689,7 +6596,6 @@ const char *sqlite3BtreeGetFilename(Btree *p){
 */
 const char *sqlite3BtreeGetDirname(Btree *p){
   assert( p->pBt->pPager!=0 );
-  assert( sqlite3BtreeMutexHeld(p->pBt->mutex) );
   return sqlite3PagerDirname(p->pBt->pPager);
 }
 
@@ -6700,7 +6606,6 @@ const char *sqlite3BtreeGetDirname(Btree *p){
 */
 const char *sqlite3BtreeGetJournalname(Btree *p){
   assert( p->pBt->pPager!=0 );
-  assert( sqlite3BtreeMutexHeld(p->pBt->mutex) );
   return sqlite3PagerJournalname(p->pBt->pPager);
 }
 
@@ -6780,7 +6685,6 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
 ** Return non-zero if a transaction is active.
 */
 int sqlite3BtreeIsInTrans(Btree *p){
-  assert( sqlite3BtreeMutexHeld(p->pBt->mutex) );
   assert( sqlite3BtreeMutexHeld(p->pSqlite->mutex) );
   return (p && (p->inTrans==TRANS_WRITE));
 }
@@ -6798,7 +6702,6 @@ int sqlite3BtreeIsInStmt(Btree *p){
 ** Return non-zero if a read (or write) transaction is active.
 */
 int sqlite3BtreeIsInReadTrans(Btree *p){
-  assert( sqlite3BtreeMutexHeld(p->pBt->mutex) );
   assert( sqlite3BtreeMutexHeld(p->pSqlite->mutex) );
   return (p && (p->inTrans!=TRANS_NONE));
 }
index 60e0c887d03f13d068b0aa414acc4cdc9d34dc76..2af90f7713ddb0244a2794cc49d71541a89407c3 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.86 2007/08/22 02:56:43 drh Exp $
+** @(#) $Id: btree.h,v 1.87 2007/08/28 02:27:52 drh Exp $
 */
 #ifndef _BTREE_H_
 #define _BTREE_H_
 typedef struct Btree Btree;
 typedef struct BtCursor BtCursor;
 typedef struct BtShared BtShared;
+typedef struct BtreeMutexSet BtreeMutexSet;
+
+/*
+** This structure records all of the Btrees that need to hold
+** a mutex before we enter sqlite3VdbeExec().  The Btrees are
+** are placed in aBtree[] in order of aBtree[]->pBt.  That way,
+** we can always lock and unlock them all quickly.
+*/
+struct BtreeMutexSet {
+  int nMutex;
+  Btree *aBtree[SQLITE_MAX_ATTACHED+1];
+};
 
 
 int sqlite3BtreeOpen(
@@ -170,4 +182,16 @@ void sqlite3BtreeCursorList(Btree*);
 int sqlite3BtreePageDump(Btree*, int, int recursive);
 #endif
 
+#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE
+  void sqlite3BtreeMutexSetEnter(BtreeMutexSet*);
+  void sqlite3BtreeMutexSetLeave(BtreeMutexSet*);
+  void sqlite3BtreeMutexSetInsert(BtreeMutexSet*, Btree*);
+#else
+# define sqlite3BtreeMutexSetEnter(X)
+# define sqlite3BtreeMutexSetLeave(X)
+# define sqlite3BtreeMutexSetInsert(X,Y)
+#endif
+
+
+
 #endif /* _BTREE_H_ */
index a68137ded099014aa344dcd9e8cafe6ed2240dc7..a3cb2806714df243e6e78ea1e0a6048e3aaa72ca 100644 (file)
@@ -22,7 +22,7 @@
 **     COMMIT
 **     ROLLBACK
 **
-** $Id: build.c,v 1.438 2007/08/22 20:18:22 drh Exp $
+** $Id: build.c,v 1.439 2007/08/28 02:27:52 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -164,6 +164,7 @@ void sqlite3FinishCoding(Parse *pParse){
       sqlite3VdbeJumpHere(v, pParse->cookieGoto-1);
       for(iDb=0, mask=1; iDb<db->nDb; mask<<=1, iDb++){
         if( (mask & pParse->cookieMask)==0 ) continue;
+        sqlite3VdbeAddMutexBtree(v, db->aDb[iDb].pBt);
         sqlite3VdbeAddOp(v, OP_Transaction, iDb, (mask & pParse->writeMask)!=0);
         sqlite3VdbeAddOp(v, OP_VerifyCookie, iDb, pParse->cookieValue[iDb]);
       }
index 1ebc238256c717e3d9cc46e282c1d4c9ef2b30eb..bc0e8aab108ba804eca0da844bfd90168449bc33 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.602 2007/08/27 23:26:59 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.603 2007/08/28 02:27:52 drh Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -233,8 +233,8 @@ struct BusyHandler {
 ** Defer sourcing vdbe.h and btree.h until after the "u8" and 
 ** "BusyHandler typedefs.
 */
-#include "vdbe.h"
 #include "btree.h"
+#include "vdbe.h"
 #include "pager.h"
 
 
index f23c05c7646193322bcecb2f90376c4617722130..4519fc032b2556e3ad668391055554287993d368 100644 (file)
@@ -43,7 +43,7 @@
 ** in this file for details.  If in doubt, do not deviate from existing
 ** commenting and indentation practices when changing or adding code.
 **
-** $Id: vdbe.c,v 1.644 2007/08/21 19:33:57 drh Exp $
+** $Id: vdbe.c,v 1.645 2007/08/28 02:27:52 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -465,6 +465,7 @@ int sqlite3VdbeExec(
   if( p->magic!=VDBE_MAGIC_RUN ) return SQLITE_MISUSE;
   assert( db->magic==SQLITE_MAGIC_BUSY );
   pTos = p->pTos;
+  sqlite3BtreeMutexSetEnter(&p->mtxSet);
   if( p->rc==SQLITE_NOMEM ){
     /* This happens if a malloc() inside a call to sqlite3_column_text() or
     ** sqlite3_column_text16() failed.  */
@@ -685,10 +686,11 @@ case OP_Halt: {            /* no-push */
   rc = sqlite3VdbeHalt(p);
   assert( rc==SQLITE_BUSY || rc==SQLITE_OK );
   if( rc==SQLITE_BUSY ){
-    p->rc = SQLITE_BUSY;
-    return SQLITE_BUSY;
+    p->rc = rc = SQLITE_BUSY;
+  }else{
+    rc = p->rc ? SQLITE_ERROR : SQLITE_DONE;
   }
-  return p->rc ? SQLITE_ERROR : SQLITE_DONE;
+  goto vdbe_return;
 }
 
 /* Opcode: Integer P1 * *
@@ -1003,7 +1005,8 @@ case OP_Callback: {            /* no-push */
   p->popStack = pOp->p1;
   p->pc = pc + 1;
   p->pTos = pTos;
-  return SQLITE_ROW;
+  rc = SQLITE_ROW;
+  goto vdbe_return;
 }
 
 /* Opcode: Concat P1 P2 *
@@ -2462,15 +2465,16 @@ case OP_AutoCommit: {       /* no-push */
         p->pTos = pTos;
         p->pc = pc;
         db->autoCommit = 1-i;
-        p->rc = SQLITE_BUSY;
-        return SQLITE_BUSY;
+        p->rc = rc = SQLITE_BUSY;
+        goto vdbe_return;
       }
     }
     if( p->rc==SQLITE_OK ){
-      return SQLITE_DONE;
+      rc = SQLITE_DONE;
     }else{
-      return SQLITE_ERROR;
+      rc = SQLITE_ERROR;
     }
+    goto vdbe_return;
   }else{
     sqlite3SetString(&p->zErrMsg,
         (!i)?"cannot start a transaction within a transaction":(
@@ -2513,9 +2517,9 @@ case OP_Transaction: {       /* no-push */
     rc = sqlite3BtreeBeginTrans(pBt, pOp->p2);
     if( rc==SQLITE_BUSY ){
       p->pc = pc;
-      p->rc = SQLITE_BUSY;
+      p->rc = rc = SQLITE_BUSY;
       p->pTos = pTos;
-      return SQLITE_BUSY;
+      goto vdbe_return;
     }
     if( rc!=SQLITE_OK && rc!=SQLITE_READONLY /* && rc!=SQLITE_BUSY */ ){
       goto abort_due_to_error;
@@ -2755,9 +2759,9 @@ case OP_OpenWrite: {       /* no-push */
   switch( rc ){
     case SQLITE_BUSY: {
       p->pc = pc;
-      p->rc = SQLITE_BUSY;
+      p->rc = rc = SQLITE_BUSY;
       p->pTos = &pTos[1 + (pOp->p2<=0)]; /* Operands must remain on stack */
-      return SQLITE_BUSY;
+      goto vdbe_return;
     }
     case SQLITE_OK: {
       int flags = sqlite3BtreeFlags(pCur->pCursor);
@@ -5193,6 +5197,12 @@ vdbe_halt:
   }
   sqlite3VdbeHalt(p);
   p->pTos = pTos;
+
+  /* This is the only way out of this procedure.  We have to
+  ** release the mutexes on btrees that were acquired at the
+  ** top. */
+vdbe_return:
+  sqlite3BtreeMutexSetLeave(&p->mtxSet);
   return rc;
 
   /* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH
index f6b145bf1c35740170119715159f54cf29aeb3a6..0f3500a75224436bbf68018c39bf9fa9d5baeed1 100644 (file)
@@ -15,7 +15,7 @@
 ** or VDBE.  The VDBE implements an abstract machine that runs a
 ** simple program to access and modify the underlying database.
 **
-** $Id: vdbe.h,v 1.110 2007/05/08 21:45:28 drh Exp $
+** $Id: vdbe.h,v 1.111 2007/08/28 02:27:52 drh Exp $
 */
 #ifndef _SQLITE_VDBE_H_
 #define _SQLITE_VDBE_H_
@@ -120,6 +120,7 @@ void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2);
 void sqlite3VdbeJumpHere(Vdbe*, int addr);
 void sqlite3VdbeChangeToNoop(Vdbe*, int addr, int N);
 void sqlite3VdbeChangeP3(Vdbe*, int addr, const char *zP1, int N);
+void sqlite3VdbeAddMutexBtree(Vdbe*, Btree*);
 VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
 int sqlite3VdbeMakeLabel(Vdbe*);
 void sqlite3VdbeDelete(Vdbe*);
index cef5cfc240ccb5b39b1324897f63d16fb3a289e6..78e76c29a1e6804161cf1f94f5de73cb881b0049 100644 (file)
@@ -317,7 +317,7 @@ struct Vdbe {
   unsigned uniqueCnt;     /* Used by OP_MakeRecord when P2!=0 */
   int errorAction;        /* Recovery action to do in case of an error */
   int inTempTrans;        /* True if temp database is transactioned */
-  int returnStack[100];   /* Return address stack for OP_Gosub & OP_Return */
+  int returnStack[25];    /* Return address stack for OP_Gosub & OP_Return */
   int returnDepth;        /* Next unused element in returnStack[] */
   int nResColumn;         /* Number of columns in one row of the result set */
   char **azResColumn;     /* Values for one row of result */ 
@@ -332,6 +332,7 @@ struct Vdbe {
   u8 inVtabMethod;        /* See comments above */
   int nChange;            /* Number of db changes made since last reset */
   i64 startTime;          /* Time when query started - used for profiling */
+  BtreeMutexSet mtxSet;   /* Set of Btree mutexes */
   int nSql;             /* Number of bytes in zSql */
   char *zSql;           /* Text of the SQL statement that generated this */
 #ifdef SQLITE_DEBUG
index 8bf6b831ac493cebee21f2bc50a09f132ce0b46e..80e14b17e7b1e324a025bdb67bab12a63e3db322 100644 (file)
@@ -658,6 +658,13 @@ static char *displayP3(Op *pOp, char *zTemp, int nTemp){
 }
 #endif
 
+/*
+** Add a btree to the set of btrees that might need a mutex.
+*/
+void sqlite3VdbeAddMutexBtree(Vdbe *p, Btree *pBtree){
+  sqlite3BtreeMutexSetInsert(&p->mtxSet, pBtree);
+}
+
 
 #if defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
 /*