From 900b31ef49b4faed9c3f991ac1062f662f56a76d Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 28 Aug 2007 02:27:51 +0000 Subject: [PATCH] Work toward correct btree locking in a multithreaded environment. (CVS 4307) FossilOrigin-Name: b8cc493b47e618648f645ab73eb0253739e03fcd --- Makefile.in | 6 +- main.mk | 10 ++- manifest | 31 ++++---- manifest.uuid | 2 +- src/btmutex.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++ src/btree.c | 99 +----------------------- src/btree.h | 26 ++++++- src/build.c | 3 +- src/sqliteInt.h | 4 +- src/vdbe.c | 36 +++++---- src/vdbe.h | 3 +- src/vdbeInt.h | 3 +- src/vdbeaux.c | 7 ++ 13 files changed, 294 insertions(+), 135 deletions(-) create mode 100644 src/btmutex.c diff --git a/Makefile.in b/Makefile.in index e5040020d3..558e543228 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 5ff4fb2f76..a68433f1f3 100644 --- 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 diff --git a/manifest b/manifest index c43eabecc5..9c206a56fd 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 4a7d87bc4d..5ea8d95fad 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -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 index 0000000000..4725910f20 --- /dev/null +++ b/src/btmutex.c @@ -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->pBtpBt ); + 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; inMutex; i++){ + assert( pSet->aBtree[i]!=pBtree ); + } + } +#endif + assert( pSet->nMutex>=0 ); + assert( pSet->nMutexaBtree)/sizeof(pSet->aBtree[0])-1 ); + pBt = pBtree->pBt; + for(i=0; inMutex; 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; inMutex; i++){ + Btree *p = pSet->aBtree[i]; + /* Some basic sanity checking */ + assert( i==0 || pSet->aBtree[i-1]->pBtpBt ); + 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; inMutex; i++){ + Btree *p = pSet->aBtree[i]; + /* Some basic sanity checking */ + assert( i==0 || pSet->aBtree[i-1]->pBtpBt ); + 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 */ diff --git a/src/btree.c b/src/btree.c index 3d4e55ac88..cd7b3e1a5c 100644 --- a/src/btree.c +++ b/src/btree.c @@ -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->pBtpBt ); - 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)); } diff --git a/src/btree.h b/src/btree.h index 60e0c887d0..2af90f7713 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.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_ @@ -41,6 +41,18 @@ 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_ */ diff --git a/src/build.c b/src/build.c index a68137ded0..a3cb280671 100644 --- a/src/build.c +++ b/src/build.c @@ -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 @@ -164,6 +164,7 @@ void sqlite3FinishCoding(Parse *pParse){ sqlite3VdbeJumpHere(v, pParse->cookieGoto-1); for(iDb=0, mask=1; iDbnDb; 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]); } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 1ebc238256..bc0e8aab10 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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" diff --git a/src/vdbe.c b/src/vdbe.c index f23c05c764..4519fc032b 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -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 @@ -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 diff --git a/src/vdbe.h b/src/vdbe.h index f6b145bf1c..0f3500a752 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -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*); diff --git a/src/vdbeInt.h b/src/vdbeInt.h index cef5cfc240..78e76c29a1 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -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 diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 8bf6b831ac..80e14b17e7 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -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) /*