]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Work toward multithreading support. Currently crashes quickly on a test. (CVS 4253)
authordrh <drh@noemail.net>
Mon, 20 Aug 2007 22:48:41 +0000 (22:48 +0000)
committerdrh <drh@noemail.net>
Mon, 20 Aug 2007 22:48:41 +0000 (22:48 +0000)
FossilOrigin-Name: 1315bd8e125602275fb718780f9b2730bd37f6ab

23 files changed:
main.mk
manifest
manifest.uuid
src/btree.c
src/btree.h
src/btreeInt.h
src/main.c
src/mem2.c
src/mutex.c
src/os.c
src/os_os2.h
src/os_unix.c
src/random.c
src/sqlite.h.in
src/sqliteInt.h
src/test1.c
src/test2.c
src/test4.c
src/test6.c
src/test7.c
src/test_async.c
src/test_config.c
src/test_server.c

diff --git a/main.mk b/main.mk
index 004be61ae8a55866a16381f33a8411ba2c8ce874..43ffe004f3ffb4e16ebfe62bb2e179fe7fac15dd 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -281,8 +281,8 @@ last_change:        $(SRC)
        cat $(SRC) | grep '$$Id: ' | sort -k 5 | tail -1 \
           | $(NAWK) '{print $$5,$$6}' >last_change
 
-libsqlite3.a:  $(LIBOBJ) $(EXTOBJ)
-       $(AR) libsqlite3.a $(LIBOBJ) $(EXTOBJ)
+libsqlite3.a:  $(LIBOBJ)
+       $(AR) libsqlite3.a $(LIBOBJ)
        $(RANLIB) libsqlite3.a
 
 sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h
index ca173b5a1ac043546e1d7645baa8d566f1a6ea18..fd85a2f2df794ce187415385fd52fb2aaf4a0186 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Oops\s-\sa\srandom\sfts2\stest\sI\shad\sabandoned\sslipped\sinto\sthe\sfts3\sbatch.\s(CVS\s4252)
-D 2007-08-20T17:53:00
+C Work\stoward\smultithreading\ssupport.\s\sCurrently\scrashes\squickly\son\sa\stest.\s(CVS\s4253)
+D 2007-08-20T22:48:42
 F Makefile.in 0c0e53720f658c7a551046442dd7afba0b72bfbe
 F Makefile.linux-gcc 65241babba6faf1152bf86574477baab19190499
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -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 e90dcb16ce27c6d8d8ed18335c4cc03f5a5fed0b
+F main.mk b1d97fe47a0633bc2ee5a09c7fdf7f4105156437
 F mkdll.sh 37fa8a7412e51b5ab2bc6d4276135f022a0feffb
 F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
 F mkextw.sh 1a866b53637dab137191341cc875575a5ca110fb
@@ -80,9 +80,9 @@ F src/alter.c f0aac0060ae8102e58f210b44d35b53438d53173
 F src/analyze.c a14237d869c6bea0846493b59317e4097e81a0b6
 F src/attach.c a52225c75b107be8c5bc144a2b6d20201be3f8f8
 F src/auth.c 5ea90bc93dfea46e9fe4bf531e14c7cd98219ecb
-F src/btree.c f8a04f35eb81360773899983f7c2008145e13935
-F src/btree.h 91ee529d581c1473d8e6e15299acc3b8de1d0674
-F src/btreeInt.h 9b4ca8999e52f713420e5f297dd86887a7a9820f
+F src/btree.c ae710a86a5ee2f27fbcba34033e4f048bd97cde9
+F src/btree.h 525105564c87111922412368f2e4301c36e74ac1
+F src/btreeInt.h e93edf57832278138b98cf60cbc54241103c6988
 F src/build.c add67be992307b4b11849a6611bfd3352aacde92
 F src/callback.c 143436453bb93e831c9574fea0b9b9eb90e40ff3
 F src/complete.c ea63834e798a0ab14159bdc6e6cabc3df21aa346
@@ -97,20 +97,20 @@ F src/insert.c 633322aef1799f6604fa805e12488bc628570b0c
 F src/legacy.c 6013a7cb7da1b72550b3d35d4fc598b3c3e5b8c1
 F src/limits.h 71ab25f17e35e0a9f3f6f234b8ed49cc56731d35
 F src/loadext.c c0ccda3dbda109da087a8fd762deebe5fdf24a1d
-F src/main.c 685aa31a7af0d650c94e6968d713c3a9374aa72e
+F src/main.c 7310dd532c1ff751772b196c98f7e0534afd7e58
 F src/malloc.c 613c65f12ff0ee4edd017aa458209ab7a23cd7b1
 F src/md5.c c5fdfa5c2593eaee2e32a5ce6c6927c986eaf217
 F src/mem1.c 30bf8be3846f92fdf88c490c5e5378512383bcbe
-F src/mem2.c 661ca7ebf6e4b964fecc95d24e8c89dbcfc9dfea
-F src/mutex.c 67b2efd36a1e67a7dc7b7fa852fd69953462c943
-F src/os.c 68c46a16bcd47aa3f9418a01dc2812b1ff2c4c17
+F src/mem2.c 482f0aaf14e8ef1db64cb8c5b9a9bfe708297c92
+F src/mutex.c 60051a86c49cfe2ec15dd8b1d9d0d530b8893b07
+F src/os.c c9f91550afa071c5b98042ce14d38a2d87c3f4df
 F src/os.h da098cad985b4849fefdd6a96d671b332008aa55
 F src/os_common.h a5c446d3b93f09f369d13bf217de4bed3437dd1c
 F src/os_os2.c cba4e96fadb949076c717108fe0599d1a3c2e446
-F src/os_os2.h e5f17dd69333632bbc3112881ea407c37d245eb3
+F src/os_os2.h c3f7d0af7e3453d1d7aa81b06c0a56f5a226530b
 F src/os_test.c 49833426101f99aee4bb5f6a44b7c4b2029fda1c
 F src/os_test.h 903c93554c23d88f34f667f1979e4a1cee792af3
-F src/os_unix.c 10fb60a8d9174dcb609d72f6b97211c58da25b47
+F src/os_unix.c 41a737d14d00b93fae8efd0d0555922f07a29ada
 F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e
 F src/os_win.c d868d5f9e95ec9c1b9e2a30c54c996053db6dddd
 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
@@ -120,35 +120,35 @@ F src/parse.y c03529c3b82702ada98ce405b390e3a9409708cf
 F src/pragma.c 8f5e37c3cf6dbdeb3645bb80cc58cfc3324c0178
 F src/prepare.c 03292beeffce2d65debab12508a8ec1f5aec7241
 F src/printf.c a8f46e0ed360c18d40e89aa636533be300b406c2
-F src/random.c 00b30565f018f3a256c157432935de070231c73b
+F src/random.c af7264b4ed93330b3bf40dd123e16e7f8f62e5bb
 F src/select.c 98c367bce3f38c5adfcc97de9ab5c79b0e5dc2b2
 F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96
 F src/shell.c ac29402b538515fa4697282387be9c1205e6e9eb
-F src/sqlite.h.in a0baef0f4c969a4eb9dfc9096bf527ca543485e5
+F src/sqlite.h.in 09d8d2780c2c25dedbcfdfcafc39e8b213fc8432
 F src/sqlite3ext.h 647a6b8a8f76ff6c9611e4a071531d8e63ff2d6b
-F src/sqliteInt.h 8932f2a2f855e5ce566ab3805002f074eb56ae11
+F src/sqliteInt.h 6891d6864a6b9fc661eadaa0328e81dff7523584
 F src/sqliteLimit.h f14609c27636ebc217c9603ade26dbdd7d0f6afa
 F src/table.c c725e47f6f3092b9a7b569fc58e408e2173ee008
 F src/tclsqlite.c 0606c4f31711492eb4d7480a981eebb80914f3d9
-F src/test1.c a226ab03048491aa6c5d43d26097df96bdb162e7
-F src/test2.c 016380989929fd41bdf790058b1f2fd698f8af28
+F src/test1.c 6ae17d70dac0f14ab632f66c026a44fb0c71f22e
+F src/test2.c 4506b6635e193a19a9bccdbe366ff4aed09cbe79
 F src/test3.c b87e8fcce45e1d3153aae9f04236076b7707a714
-F src/test4.c d22cb3ab4f9fdfd0a595b70d5328cee923b7322c
+F src/test4.c 1f4d2ed89867bac187d49ed8004f121592987d3e
 F src/test5.c 7bc8a87c2b6fd076ec2ca9972946e71a367883ad
-F src/test6.c d67117b2c1df9b07889f3f3779ecc9ec2663fd1e
-F src/test7.c 91d914c2c2b2806157213f41f4185ad3a4970c07
+F src/test6.c b0090b4826d5e06df2ff2d5acaddf3f8f708fcd2
+F src/test7.c 0f9d91ce8018740d5eb6e258f0fac2a2943c40f0
 F src/test8.c 719c284607c1e91a893f5425df1e92b74c859aef
 F src/test9.c c0f38f7795cc51d37db6c63874d90f40f10d0f0e
-F src/test_async.c 871ffbe4a520be74b403aca87aa622ebdb690232
+F src/test_async.c ba48913e4bab43b149747d3bdd3b75b1f66658ec
 F src/test_autoext.c 855157d97aa28cf84233847548bfacda21807436
 F src/test_btree.c c1308ba0b88ab577fa56c9e493a09829dfcded9c
-F src/test_config.c 1d0da26fd51757765f2073c57a925db8ddc7120c
+F src/test_config.c ddced28aedfa324292005d991d74b595d76a838a
 F src/test_hexio.c 82916f918687502658f02533b519c38cb180db6d
 F src/test_loadext.c 22065d601a18878e5542191001f0eaa5d77c0ed8
 F src/test_malloc.c d9ba6be85f9c4a439b19f6e0a72d91c369d72c63
 F src/test_md5.c d9f828765b242ff86f58cd879259c3da4eaede02
 F src/test_schema.c 12c9de7661d6294eec2d57afbb52e2af1128084f
-F src/test_server.c 76c0baf509abe65ca6e5c7974ab0097cfdd8b833
+F src/test_server.c 319f6b1a99bab5f7149387442243d6e65a8ab4eb
 F src/test_tclvar.c 160290ba5c19964da7cb97defc6c104712074b69
 F src/tokenize.c 3a3fd71cfb2abb8e11ed6ab6b764b790c534defc
 F src/trigger.c dccc6fbf37d12193c90ede5b026bbd195d505ff4
@@ -557,7 +557,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
 F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
-P 24739f148e7687532fd04794a041aade8626c630
-R 91022a585940fe2f4f8ce2cc48394d34
-U shess
-Z 50354b12eb6453839bc699aa3eacaa47
+P 709f2aa18a4802a7ca6638c00b2b99440b4a3191
+R dff2f29b0cfe4e6e9309aeb129fa4ba9
+U drh
+Z b2675de4c3469ebcd0e4f20229838975
index f401abc2b24520254c7da41b4940c04c7b16bde0..3bf1833f51786256e3baa2d35196e3a158744434 100644 (file)
@@ -1 +1 @@
-709f2aa18a4802a7ca6638c00b2b99440b4a3191
\ No newline at end of file
+1315bd8e125602275fb718780f9b2730bd37f6ab
\ No newline at end of file
index d5710b71b4ac7cca7ffe365bee1b530db0e3e3c0..0f665f46f65e942758fa6fe8000f9e7b1e89337c 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.402 2007/08/20 13:14:29 drh Exp $
+** $Id: btree.c,v 1.403 2007/08/20 22:48:42 drh Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** See the header comment on "btreeInt.h" for additional information.
@@ -23,7 +23,6 @@
 */
 static const char zMagicHeader[] = SQLITE_FILE_HEADER;
 
-
 /*
 ** Set this global variable to 1 to enable tracing using the TRACE
 ** macro.
@@ -36,7 +35,9 @@ int sqlite3_btree_trace=0;  /* True to enable tracing */
 /*
 ** A flag to indicate whether or not shared cache is enabled.  Also,
 ** a list of BtShared objects that are eligible for participation
-** in shared cache.
+** in shared cache.  The variables have file scope during normal builds,
+** but the test harness needs to access these variables so make them
+** global for test builds.
 */
 #ifdef SQLITE_TEST
 BtShared *sqlite3SharedCacheList = 0;
@@ -45,7 +46,6 @@ int sqlite3SharedCacheEnabled = 0;
 static BtShared *sqlite3SharedCacheList = 0;
 static int sqlite3SharedCacheEnabled = 0;
 #endif
-
 #endif /* SQLITE_OMIT_SHARED_CACHE */
 
 #ifndef SQLITE_OMIT_SHARED_CACHE
@@ -62,6 +62,7 @@ int sqlite3_enable_shared_cache(int enable){
 }
 #endif
 
+
 /*
 ** Forward declaration
 */
@@ -93,6 +94,8 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){
   BtShared *pBt = p->pBt;
   BtLock *pIter;
 
+  assert( sqlite3_mutex_held(pBt->mutex) );
+  
   /* This is a no-op if the shared-cache is not enabled */
   if( !p->sharable ){
     return SQLITE_OK;
@@ -143,6 +146,8 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
   BtLock *pLock = 0;
   BtLock *pIter;
 
+  assert( sqlite3_mutex_held(pBt->mutex) );
+
   /* This is a no-op if the shared-cache is not enabled */
   if( !p->sharable ){
     return SQLITE_OK;
@@ -207,6 +212,7 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
 static void unlockAllTables(Btree *p){
   BtLock **ppIter = &p->pBt->pLock;
 
+  assert( sqlite3_mutex_held(p->pBt->mutex) );
   assert( p->sharable || 0==*ppIter );
 
   while( *ppIter ){
@@ -238,6 +244,7 @@ static void invalidateOverflowCache(BtCursor *pCur){
 */
 static void invalidateAllOverflowCache(BtShared *pBt){
   BtCursor *p;
+  assert( sqlite3_mutex_held(pBt->mutex) );
   for(p=pBt->pCursor; p; p=p->pNext){
     invalidateOverflowCache(p);
   }
@@ -256,6 +263,7 @@ static int saveCursorPosition(BtCursor *pCur){
 
   assert( CURSOR_VALID==pCur->eState );
   assert( 0==pCur->pKey );
+  assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
 
   rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
 
@@ -297,6 +305,7 @@ static int saveCursorPosition(BtCursor *pCur){
 */
 static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
   BtCursor *p;
+  assert( sqlite3_mutex_held(pBt->mutex) );
   for(p=pBt->pCursor; p; p=p->pNext){
     if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) && 
         p->eState==CURSOR_VALID ){
@@ -313,6 +322,7 @@ static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
 ** Clear the current cursor position.
 */
 static void clearCursorPosition(BtCursor *pCur){
+  assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
   sqlite3_free(pCur->pKey);
   pCur->pKey = 0;
   pCur->eState = CURSOR_INVALID;
@@ -359,9 +369,11 @@ int sqlite3BtreeRestoreOrClearCursorPosition(BtCursor *pCur){
 ** input page number.
 */
 static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){
-  int nPagesPerMapPage = (pBt->usableSize/5)+1;
-  int iPtrMap = (pgno-2)/nPagesPerMapPage;
-  int ret = (iPtrMap*nPagesPerMapPage) + 2; 
+  int nPagesPerMapPage, iPtrMap, ret;
+  assert( sqlite3_mutex_held(pBt->mutex) );
+  nPagesPerMapPage = (pBt->usableSize/5)+1;
+  iPtrMap = (pgno-2)/nPagesPerMapPage;
+  ret = (iPtrMap*nPagesPerMapPage) + 2; 
   if( ret==PENDING_BYTE_PAGE(pBt) ){
     ret++;
   }
@@ -382,6 +394,7 @@ static int ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent){
   int offset;       /* Offset in pointer map page */
   int rc;
 
+  assert( sqlite3_mutex_held(pBt->mutex) );
   /* The master-journal page number must never be used as a pointer map page */
   assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) );
 
@@ -424,6 +437,8 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
   int offset;        /* Offset of entry in pointer map */
   int rc;
 
+  assert( sqlite3_mutex_held(pBt->mutex) );
+
   iPtrmap = PTRMAP_PAGENO(pBt, key);
   rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage);
   if( rc!=0 ){
@@ -466,6 +481,7 @@ u8 *sqlite3BtreeFindCell(MemPage *pPage, int iCell){
 */
 static u8 *findOverflowCell(MemPage *pPage, int iCell){
   int i;
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   for(i=pPage->nOverflow-1; i>=0; i--){
     int k;
     struct _OvflCell *pOvfl;
@@ -498,6 +514,8 @@ void sqlite3BtreeParseCellPtr(
   int n;                  /* Number bytes in cell content header */
   u32 nPayload;           /* Number of bytes of cell payload */
 
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+
   pInfo->pCell = pCell;
   assert( pPage->leaf==0 || pPage->leaf==1 );
   n = pPage->childPtrSize;
@@ -610,6 +628,7 @@ static int ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell){
 */
 static int ptrmapPutOvfl(MemPage *pPage, int iCell){
   u8 *pCell;
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   pCell = findOverflowCell(pPage, iCell);
   return ptrmapPutOvflPtr(pPage, pCell);
 }
@@ -639,6 +658,7 @@ static int defragmentPage(MemPage *pPage){
   assert( pPage->pBt!=0 );
   assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE );
   assert( pPage->nOverflow==0 );
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   temp = sqlite3_malloc( pPage->pBt->pageSize );
   if( temp==0 ) return SQLITE_NOMEM;
   data = pPage->aData;
@@ -695,6 +715,7 @@ static int allocateSpace(MemPage *pPage, int nByte){
   data = pPage->aData;
   assert( sqlite3PagerIswriteable(pPage->pDbPage) );
   assert( pPage->pBt );
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   if( nByte<4 ) nByte = 4;
   if( pPage->nFree<nByte || pPage->nOverflow>0 ) return 0;
   pPage->nFree -= nByte;
@@ -753,6 +774,7 @@ static void freeSpace(MemPage *pPage, int start, int size){
   assert( sqlite3PagerIswriteable(pPage->pDbPage) );
   assert( start>=pPage->hdrOffset+6+(pPage->leaf?0:4) );
   assert( (start + size)<=pPage->pBt->usableSize );
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   if( size<4 ) size = 4;
 
 #ifdef SQLITE_SECURE_DELETE
@@ -813,6 +835,7 @@ static void decodeFlags(MemPage *pPage, int flagByte){
   BtShared *pBt;     /* A copy of pPage->pBt */
 
   assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) );
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   pPage->intKey = (flagByte & (PTF_INTKEY|PTF_LEAFDATA))!=0;
   pPage->zeroData = (flagByte & PTF_ZERODATA)!=0;
   pPage->leaf = (flagByte & PTF_LEAF)!=0;
@@ -859,6 +882,7 @@ int sqlite3BtreeInitPage(
   pBt = pPage->pBt;
   assert( pBt!=0 );
   assert( pParent==0 || pParent->pBt==pBt );
+  assert( sqlite3_mutex_held(pBt->mutex) );
   assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) );
   assert( pPage->aData == &((unsigned char*)pPage)[-pBt->pageSize] );
   if( pPage->pParent!=pParent && (pPage->pParent!=0 || pPage->isInit) ){
@@ -929,6 +953,7 @@ static void zeroPage(MemPage *pPage, int flags){
   assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno );
   assert( &data[pBt->pageSize] == (unsigned char*)pPage );
   assert( sqlite3PagerIswriteable(pPage->pDbPage) );
+  assert( sqlite3_mutex_held(pBt->mutex) );
   memset(&data[hdr], 0, pBt->usableSize - hdr);
   data[hdr] = flags;
   first = hdr + 8 + 4*((flags&PTF_LEAF)==0);
@@ -966,6 +991,7 @@ int sqlite3BtreeGetPage(
   MemPage *pPage;
   DbPage *pDbPage;
 
+  assert( sqlite3_mutex_held(pBt->mutex) );
   rc = sqlite3PagerAcquire(pBt->pPager, pgno, (DbPage**)&pDbPage, noContent);
   if( rc ) return rc;
   pPage = (MemPage *)sqlite3PagerGetExtra(pDbPage);
@@ -990,6 +1016,7 @@ static int getAndInitPage(
   MemPage *pParent     /* Parent of the page */
 ){
   int rc;
+  assert( sqlite3_mutex_held(pBt->mutex) );
   if( pgno==0 ){
     return SQLITE_CORRUPT_BKPT; 
   }
@@ -1009,6 +1036,7 @@ static void releasePage(MemPage *pPage){
     assert( pPage->aData );
     assert( pPage->pBt );
     assert( &pPage->aData[pPage->pBt->pageSize]==(unsigned char*)pPage );
+    assert( sqlite3_mutex_held(pPage->pBt->mutex) );
     sqlite3PagerUnref(pPage->pDbPage);
   }
 }
@@ -1022,6 +1050,7 @@ static void pageDestructor(DbPage *pData, int pageSize){
   MemPage *pPage;
   assert( (pageSize & 7)==0 );
   pPage = (MemPage *)sqlite3PagerGetExtra(pData);
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   if( pPage->pParent ){
     MemPage *pParent = pPage->pParent;
     pPage->pParent = 0;
@@ -1042,6 +1071,7 @@ static void pageReinit(DbPage *pData, int pageSize){
   MemPage *pPage;
   assert( (pageSize & 7)==0 );
   pPage = (MemPage *)sqlite3PagerGetExtra(pData);
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   if( pPage->isInit ){
     pPage->isInit = 0;
     sqlite3BtreeInitPage(pPage, pPage->pParent);
@@ -1063,13 +1093,20 @@ int sqlite3BtreeOpen(
   Btree **ppBtree,        /* Pointer to new Btree object written here */
   int flags               /* Options */
 ){
-  sqlite3_vfs *pVfs = (pSqlite?pSqlite->pVfs:sqlite3_find_vfs(0));
+  sqlite3_vfs *pVfs;      /* The VFS to use for this btree */
   BtShared *pBt = 0;      /* Shared part of btree structure */
   Btree *p;               /* Handle to return */
   int rc = SQLITE_OK;
   int nReserve;
   unsigned char zDbHeader[100];
 
+  if( pSqlite ){
+    pVfs = pSqlite->pVfs;
+  }else{
+    pVfs = sqlite3_vfs_find(0);
+  }
+  assert( pSqlite==0 || sqlite3_mutex_held(pSqlite->mutex) );
+
   /* Set the variable isMemdb to true for an in-memory database, or 
   ** false for a file-based database. This symbol is only required if
   ** either of the shared-data or autovacuum features are compiled 
@@ -1261,6 +1298,7 @@ static int removeFromSharingList(BtShared *pBt){
   BtShared *pList;
   int removed = 0;
 
+  assert( sqlite3_mutex_notheld(pBt->mutex) );
   pMaster = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
   sqlite3_mutex_enter(pMaster);
   pBt->nRef--;
@@ -1345,7 +1383,7 @@ int sqlite3BtreeClose(Btree *p){
   return SQLITE_OK;
 }
 
-#ifndef SQLITE_OMIT_SHARED_CACHE
+#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
 /*
 ** Enter a mutex on the given BTree object.
 **
@@ -1379,6 +1417,9 @@ void sqlite3BtreeEnter(Btree *p){
   assert( !p->locked || p->wantToLock>0 );
   assert( p->sharable || p->wantToLock==0 );
 
+  /* We should already hold a lock on the database connection */
+  assert( sqlite3_mutex_held(p->pSqlite->mutex) );
+
   if( !p->sharable ) return;
   p->wantToLock++;
   if( p->locked ) return;
@@ -1419,7 +1460,7 @@ void sqlite3BtreeEnter(Btree *p){
 /*
 ** Exit the recursive mutex on a Btree.
 */
-#ifndef SQLITE_OMIT_SHARED_CACHE
+#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
 void sqlite3BtreeLeave(Btree *p){
   if( p->sharable ){
     assert( p->wantToLock>0 );
@@ -1431,6 +1472,22 @@ void sqlite3BtreeLeave(Btree *p){
     }
   }
 }
+#endif
+
+
+#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
+/*
+** Short-cuts for entering and leaving mutexes on a cursor.
+*/
+static void cursorLeave(BtCursor *p){
+  sqlite3BtreeLeave(p->pBt);
+}
+static void cursorEnter(BtCursor *pCur){
+  sqlite3BtreeEnter(pCur->pBt);
+}
+#else
+# define cursorEnter(X)
+# define cursorLeave(X)
 #endif /* !SQLITE_OMIT_SHARED_CACHE */
 
 /*
@@ -1438,8 +1495,10 @@ void sqlite3BtreeLeave(Btree *p){
 */
 int sqlite3BtreeSetBusyHandler(Btree *p, BusyHandler *pHandler){
   BtShared *pBt = p->pBt;
+  sqlite3BtreeEnter(p);
   pBt->pBusyHandler = pHandler;
   sqlite3PagerSetBusyhandler(pBt->pPager, pHandler);
+  sqlite3BtreeLeave(p);
   return SQLITE_OK;
 }
 
@@ -1460,7 +1519,9 @@ int sqlite3BtreeSetBusyHandler(Btree *p, BusyHandler *pHandler){
 */
 int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
   BtShared *pBt = p->pBt;
+  sqlite3BtreeEnter(p);
   sqlite3PagerSetCachesize(pBt->pPager, mxPage);
+  sqlite3BtreeLeave(p);
   return SQLITE_OK;
 }
 
@@ -1475,7 +1536,9 @@ int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
 #ifndef SQLITE_OMIT_PAGER_PRAGMAS
 int sqlite3BtreeSetSafetyLevel(Btree *p, int level, int fullSync){
   BtShared *pBt = p->pBt;
+  sqlite3BtreeEnter(p);
   sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync);
+  sqlite3BtreeLeave(p);
   return SQLITE_OK;
 }
 #endif
@@ -1486,8 +1549,12 @@ int sqlite3BtreeSetSafetyLevel(Btree *p, int level, int fullSync){
 */
 int sqlite3BtreeSyncDisabled(Btree *p){
   BtShared *pBt = p->pBt;
+  int rc;
   assert( pBt && pBt->pPager );
-  return sqlite3PagerNosync(pBt->pPager);
+  sqlite3BtreeEnter(p);
+  rc = sqlite3PagerNosync(pBt->pPager);
+  sqlite3BtreeLeave(p);
+  return rc;
 }
 
 #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM)
@@ -1508,7 +1575,9 @@ int sqlite3BtreeSyncDisabled(Btree *p){
 */
 int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve){
   BtShared *pBt = p->pBt;
+  sqlite3BtreeEnter(p);
   if( pBt->pageSizeFixed ){
+    sqlite3BtreeLeave(p);
     return SQLITE_READONLY;
   }
   if( nReserve<0 ){
@@ -1521,6 +1590,7 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve){
     pBt->pageSize = sqlite3PagerSetPagesize(pBt->pPager, pageSize);
   }
   pBt->usableSize = pBt->pageSize - nReserve;
+  sqlite3BtreeLeave(p);
   return SQLITE_OK;
 }
 
@@ -1531,7 +1601,11 @@ int sqlite3BtreeGetPageSize(Btree *p){
   return p->pBt->pageSize;
 }
 int sqlite3BtreeGetReserve(Btree *p){
-  return p->pBt->pageSize - p->pBt->usableSize;
+  int n;
+  sqlite3BtreeEnter(p);
+  n = p->pBt->pageSize - p->pBt->usableSize;
+  sqlite3BtreeLeave(p);
+  return n;
 }
 
 /*
@@ -1540,7 +1614,11 @@ int sqlite3BtreeGetReserve(Btree *p){
 ** Regardless of the value of mxPage, return the maximum page count.
 */
 int sqlite3BtreeMaxPageCount(Btree *p, int mxPage){
-  return sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage);
+  int n;
+  sqlite3BtreeEnter(p);
+  n = sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage);
+  sqlite3BtreeLeave(p);
+  return n;
 }
 #endif /* !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) */
 
@@ -1555,12 +1633,17 @@ int sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){
   return SQLITE_READONLY;
 #else
   BtShared *pBt = p->pBt;
+  int rc = SQLITE_OK;
   int av = (autoVacuum?1:0);
+
+  sqlite3BtreeEnter(p);
   if( pBt->pageSizeFixed && av!=pBt->autoVacuum ){
-    return SQLITE_READONLY;
+    rc = SQLITE_READONLY;
+  }else{
+    pBt->autoVacuum = av;
   }
-  pBt->autoVacuum = av;
-  return SQLITE_OK;
+  sqlite3BtreeLeave(p);
+  return rc;
 #endif
 }
 
@@ -1572,11 +1655,15 @@ int sqlite3BtreeGetAutoVacuum(Btree *p){
 #ifdef SQLITE_OMIT_AUTOVACUUM
   return BTREE_AUTOVACUUM_NONE;
 #else
-  return (
+  int rc;
+  sqlite3BtreeEnter(p);
+  rc = (
     (!p->pBt->autoVacuum)?BTREE_AUTOVACUUM_NONE:
     (!p->pBt->incrVacuum)?BTREE_AUTOVACUUM_FULL:
     BTREE_AUTOVACUUM_INCR
   );
+  sqlite3BtreeLeave(p);
+  return rc;
 #endif
 }
 
@@ -1593,6 +1680,8 @@ int sqlite3BtreeGetAutoVacuum(Btree *p){
 static int lockBtree(BtShared *pBt){
   int rc, pageSize;
   MemPage *pPage1;
+
+  assert( sqlite3_mutex_held(pBt->mutex) );
   if( pBt->pPage1 ) return SQLITE_OK;
   rc = sqlite3BtreeGetPage(pBt, 1, &pPage1, 0);
   if( rc!=SQLITE_OK ) return rc;
@@ -1668,6 +1757,9 @@ page1_init_failed:
 */
 static int lockBtreeWithRetry(Btree *pRef){
   int rc = SQLITE_OK;
+
+  assert( sqlite3_mutex_held(pRef->pSqlite->mutex) );
+  assert( sqlite3_mutex_held(pRef->pBt->mutex) );
   if( pRef->inTrans==TRANS_NONE ){
     u8 inTransaction = pRef->pBt->inTransaction;
     btreeIntegrity(pRef);
@@ -1694,6 +1786,7 @@ static int lockBtreeWithRetry(Btree *pRef){
 ** If there is a transaction in progress, this routine is a no-op.
 */
 static void unlockBtreeIfUnused(BtShared *pBt){
+  assert( sqlite3_mutex_held(pBt->mutex) );
   if( pBt->inTransaction==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){
     if( sqlite3PagerRefcount(pBt->pPager)>=1 ){
       if( pBt->pPage1->aData==0 ){
@@ -1717,6 +1810,8 @@ static int newDatabase(BtShared *pBt){
   MemPage *pP1;
   unsigned char *data;
   int rc;
+
+  assert( sqlite3_mutex_held(pBt->mutex) );
   if( sqlite3PagerPagecount(pBt->pPager)>0 ) return SQLITE_OK;
   pP1 = pBt->pPage1;
   assert( pP1!=0 );
@@ -1783,6 +1878,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
   BtShared *pBt = p->pBt;
   int rc = SQLITE_OK;
 
+  sqlite3BtreeEnter(p);
   btreeIntegrity(p);
 
   /* If the btree is already in a write-transaction, or it
@@ -1790,12 +1886,13 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
   ** is requested, this is a no-op.
   */
   if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){
-    return SQLITE_OK;
+    goto trans_begun;
   }
 
   /* Write transactions are not possible on a read-only database */
   if( pBt->readOnly && wrflag ){
-    return SQLITE_READONLY;
+    rc = SQLITE_READONLY;
+    goto trans_begun;
   }
 
   /* If another database handle has already opened a write transaction 
@@ -1803,7 +1900,8 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
   ** requested, return SQLITE_BUSY.
   */
   if( pBt->inTransaction==TRANS_WRITE && wrflag ){
-    return SQLITE_BUSY;
+    rc = SQLITE_BUSY;
+    goto trans_begun;
   }
 
   do {
@@ -1840,7 +1938,10 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
     }
   }
 
+
+trans_begun:
   btreeIntegrity(p);
+  sqlite3BtreeLeave(p);
   return rc;
 }
 
@@ -1859,6 +1960,7 @@ static int setChildPtrmaps(MemPage *pPage){
   int isInitOrig = pPage->isInit;
   Pgno pgno = pPage->pgno;
 
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   rc = sqlite3BtreeInitPage(pPage, pPage->pParent);
   if( rc!=SQLITE_OK ){
     goto set_child_ptrmaps_out;
@@ -1906,6 +2008,7 @@ set_child_ptrmaps_out:
 **                   overflow page in the list.
 */
 static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   if( eType==PTRMAP_OVERFLOW2 ){
     /* The pointer is always the first 4 bytes of the page in this case.  */
     if( get4byte(pPage->aData)!=iFrom ){
@@ -1971,6 +2074,7 @@ static int relocatePage(
 
   assert( eType==PTRMAP_OVERFLOW2 || eType==PTRMAP_OVERFLOW1 || 
       eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE );
+  assert( sqlite3_mutex_held(pBt->mutex) );
 
   /* Move page iDbPage from it's current location to page number iFreePage */
   TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", 
@@ -2049,6 +2153,7 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin){
   Pgno iLastPg;             /* Last page in the database */
   Pgno nFreeList;           /* Number of pages still on the free-list */
 
+  assert( sqlite3_mutex_held(pBt->mutex) );
   iLastPg = pBt->nTrunc;
   if( iLastPg==0 ){
     iLastPg = sqlite3PagerPagecount(pBt->pPager);
@@ -2143,13 +2248,19 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin){
 ** SQLITE_OK is returned. Otherwise an SQLite error code. 
 */
 int sqlite3BtreeIncrVacuum(Btree *p){
+  int rc;
   BtShared *pBt = p->pBt;
+
+  sqlite3BtreeEnter(p);
   assert( pBt->inTransaction==TRANS_WRITE && p->inTrans==TRANS_WRITE );
   if( !pBt->autoVacuum ){
-    return SQLITE_DONE;
+    rc = SQLITE_DONE;
+  }else{
+    invalidateAllOverflowCache(pBt);
+    rc = incrVacuumStep(pBt, 0);
   }
-  invalidateAllOverflowCache(pBt);
-  return incrVacuumStep(pBt, 0);
+  sqlite3BtreeLeave(p);
+  return rc;
 }
 
 /*
@@ -2168,6 +2279,7 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *pnTrunc){
   int nRef = sqlite3PagerRefcount(pPager);
 #endif
 
+  assert( sqlite3_mutex_held(pBt->mutex) );
   invalidateAllOverflowCache(pBt);
   assert(pBt->autoVacuum);
   if( !pBt->incrVacuum ){
@@ -2255,15 +2367,18 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
   if( p->inTrans==TRANS_WRITE ){
     BtShared *pBt = p->pBt;
     Pgno nTrunc = 0;
+    sqlite3BtreeEnter(p);
 #ifndef SQLITE_OMIT_AUTOVACUUM
     if( pBt->autoVacuum ){
       rc = autoVacuumCommit(pBt, &nTrunc); 
       if( rc!=SQLITE_OK ){
+        sqlite3BtreeLeave(p);
         return rc;
       }
     }
 #endif
     rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, nTrunc);
+    sqlite3BtreeLeave(p);
   }
   return rc;
 }
@@ -2285,6 +2400,7 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
 int sqlite3BtreeCommitPhaseTwo(Btree *p){
   BtShared *pBt = p->pBt;
 
+  sqlite3BtreeEnter(p);
   btreeIntegrity(p);
 
   /* If the handle has a write-transaction open, commit the shared-btrees 
@@ -2296,6 +2412,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
     assert( pBt->nTransaction>0 );
     rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
     if( rc!=SQLITE_OK ){
+      sqlite3BtreeLeave(p);
       return rc;
     }
     pBt->inTransaction = TRANS_READ;
@@ -2322,6 +2439,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
   unlockBtreeIfUnused(pBt);
 
   btreeIntegrity(p);
+  sqlite3BtreeLeave(p);
   return SQLITE_OK;
 }
 
@@ -2330,10 +2448,12 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
 */
 int sqlite3BtreeCommit(Btree *p){
   int rc;
+  sqlite3BtreeEnter(p);
   rc = sqlite3BtreeCommitPhaseOne(p, 0);
   if( rc==SQLITE_OK ){
     rc = sqlite3BtreeCommitPhaseTwo(p);
   }
+  sqlite3BtreeLeave(p);
   return rc;
 }
 
@@ -2367,6 +2487,7 @@ int sqlite3BtreeRollback(Btree *p){
   BtShared *pBt = p->pBt;
   MemPage *pPage1;
 
+  sqlite3BtreeEnter(p);
   rc = saveAllCursors(pBt, 0, 0);
 #ifndef SQLITE_OMIT_SHARED_CACHE
   if( rc!=SQLITE_OK ){
@@ -2424,6 +2545,7 @@ int sqlite3BtreeRollback(Btree *p){
   unlockBtreeIfUnused(pBt);
 
   btreeIntegrity(p);
+  sqlite3BtreeLeave(p);
   return rc;
 }
 
@@ -2445,12 +2567,15 @@ int sqlite3BtreeRollback(Btree *p){
 int sqlite3BtreeBeginStmt(Btree *p){
   int rc;
   BtShared *pBt = p->pBt;
+  sqlite3BtreeEnter(p);
   if( (p->inTrans!=TRANS_WRITE) || pBt->inStmt ){
-    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+    rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+  }else{
+    assert( pBt->inTransaction==TRANS_WRITE );
+    rc = pBt->readOnly ? SQLITE_OK : sqlite3PagerStmtBegin(pBt->pPager);
+    pBt->inStmt = 1;
   }
-  assert( pBt->inTransaction==TRANS_WRITE );
-  rc = pBt->readOnly ? SQLITE_OK : sqlite3PagerStmtBegin(pBt->pPager);
-  pBt->inStmt = 1;
+  sqlite3BtreeLeave(p);
   return rc;
 }
 
@@ -2462,12 +2587,14 @@ int sqlite3BtreeBeginStmt(Btree *p){
 int sqlite3BtreeCommitStmt(Btree *p){
   int rc;
   BtShared *pBt = p->pBt;
+  sqlite3BtreeEnter(p);
   if( pBt->inStmt && !pBt->readOnly ){
     rc = sqlite3PagerStmtCommit(pBt->pPager);
   }else{
     rc = SQLITE_OK;
   }
   pBt->inStmt = 0;
+  sqlite3BtreeLeave(p);
   return rc;
 }
 
@@ -2482,6 +2609,7 @@ int sqlite3BtreeCommitStmt(Btree *p){
 int sqlite3BtreeRollbackStmt(Btree *p){
   int rc = SQLITE_OK;
   BtShared *pBt = p->pBt;
+  sqlite3BtreeEnter(p);
   sqlite3MallocDisallow();
   if( pBt->inStmt && !pBt->readOnly ){
     rc = sqlite3PagerStmtRollback(pBt->pPager);
@@ -2489,6 +2617,7 @@ int sqlite3BtreeRollbackStmt(Btree *p){
     pBt->inStmt = 0;
   }
   sqlite3MallocAllow();
+  sqlite3BtreeLeave(p);
   return rc;
 }
 
@@ -2542,7 +2671,7 @@ static int dfltCompare(
 ** default comparison function is used.  The comparison function is
 ** always ignored for INTKEY tables.
 */
-int sqlite3BtreeCursor(
+static int btreeCursor(
   Btree *p,                                   /* The btree */
   int iTable,                                 /* Root page of table to open */
   int wrFlag,                                 /* 1 to write. 0 read-only */
@@ -2554,6 +2683,7 @@ int sqlite3BtreeCursor(
   BtCursor *pCur;
   BtShared *pBt = p->pBt;
 
+  assert( sqlite3_mutex_held(pBt->mutex) );
   *ppCur = 0;
   if( wrFlag ){
     if( pBt->readOnly ){
@@ -2605,6 +2735,7 @@ int sqlite3BtreeCursor(
   *ppCur = pCur;
 
   return SQLITE_OK;
+
 create_cursor_exception:
   if( pCur ){
     releasePage(pCur->pPage);
@@ -2613,6 +2744,21 @@ create_cursor_exception:
   unlockBtreeIfUnused(pBt);
   return rc;
 }
+int sqlite3BtreeCursor(
+  Btree *p,                                   /* The btree */
+  int iTable,                                 /* Root page of table to open */
+  int wrFlag,                                 /* 1 to write. 0 read-only */
+  int (*xCmp)(void*,int,const void*,int,const void*), /* Key Comparison func */
+  void *pArg,                                 /* First arg to xCompare() */
+  BtCursor **ppCur                            /* Write new cursor here */
+){
+  int rc;
+  sqlite3BtreeEnter(p);
+  rc = btreeCursor(p, iTable, wrFlag, xCmp, pArg, ppCur);
+  sqlite3BtreeLeave(p);
+  return rc;
+}
+
 
 /*
 ** Close a cursor.  The read lock on the database file is released
@@ -2620,6 +2766,8 @@ create_cursor_exception:
 */
 int sqlite3BtreeCloseCursor(BtCursor *pCur){
   BtShared *pBt = pCur->pBtree->pBt;
+
+  cursorEnter(pCur);
   clearCursorPosition(pCur);
   if( pCur->pPrev ){
     pCur->pPrev->pNext = pCur->pNext;
@@ -2632,6 +2780,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
   releasePage(pCur->pPage);
   unlockBtreeIfUnused(pBt);
   invalidateOverflowCache(pCur);
+  cursorLeave(pCur);
   sqlite3_free(pCur);
   return SQLITE_OK;
 }
@@ -2645,7 +2794,9 @@ void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur){
   pTempCur->pNext = 0;
   pTempCur->pPrev = 0;
   if( pTempCur->pPage ){
+    cursorEnter(pCur);
     sqlite3PagerRef(pTempCur->pPage->pDbPage);
+    cursorLeave(pCur);
   }
 }
 
@@ -2655,7 +2806,9 @@ void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur){
 */
 void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){
   if( pCur->pPage ){
+    cursorEnter(pCur);
     sqlite3PagerUnref(pCur->pPage->pDbPage);
+    cursorLeave(pCur);
   }
 }
 
@@ -2712,7 +2865,10 @@ void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){
 ** itself, not the number of bytes in the key.
 */
 int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
-  int rc = restoreOrClearCursorPosition(pCur);
+  int rc;
+
+  sqlite3BtreeEnter(pCur->pBtree);
+  rc = restoreOrClearCursorPosition(pCur);
   if( rc==SQLITE_OK ){
     assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
     if( pCur->eState==CURSOR_INVALID ){
@@ -2722,6 +2878,7 @@ int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
       *pSize = pCur->info.nKey;
     }
   }
+  sqlite3BtreeLeave(pCur->pBtree);
   return rc;
 }
 
@@ -2733,7 +2890,10 @@ int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
 ** the database is empty) then *pSize is set to 0.
 */
 int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
-  int rc = restoreOrClearCursorPosition(pCur);
+  int rc;
+
+  sqlite3BtreeEnter(pCur->pBtree);
+  rc = restoreOrClearCursorPosition(pCur);
   if( rc==SQLITE_OK ){
     assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
     if( pCur->eState==CURSOR_INVALID ){
@@ -2744,6 +2904,7 @@ int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
       *pSize = pCur->info.nData;
     }
   }
+  sqlite3BtreeLeave(pCur->pBtree);
   return rc;
 }
 
@@ -2773,6 +2934,7 @@ static int getOverflowPage(
   Pgno next = 0;
   int rc;
 
+  assert( sqlite3_mutex_held(pBt->mutex) );
   /* One of these must not be NULL. Otherwise, why call this function? */
   assert(ppPage || pPgnoNext);
 
@@ -2912,6 +3074,7 @@ static int accessPayload(
   assert( pCur->eState==CURSOR_VALID );
   assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
   assert( offset>=0 );
+  assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
 
   getCellInfo(pCur);
   aPayload = pCur->info.pCell + pCur->info.nHeader;
@@ -3035,17 +3198,22 @@ static int accessPayload(
 ** the available payload.
 */
 int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
-  int rc = restoreOrClearCursorPosition(pCur);
+  int rc;
+
+  sqlite3BtreeEnter(pCur->pBtree);
+  rc = restoreOrClearCursorPosition(pCur);
   if( rc==SQLITE_OK ){
     assert( pCur->eState==CURSOR_VALID );
     assert( pCur->pPage!=0 );
     if( pCur->pPage->intKey ){
+      sqlite3BtreeLeave(pCur->pBtree);
       return SQLITE_CORRUPT_BKPT;
     }
     assert( pCur->pPage->intKey==0 );
     assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
     rc = accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0, 0);
   }
+  sqlite3BtreeLeave(pCur->pBtree);
   return rc;
 }
 
@@ -3059,13 +3227,17 @@ int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
 ** the available payload.
 */
 int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
-  int rc = restoreOrClearCursorPosition(pCur);
+  int rc;
+
+  sqlite3BtreeEnter(pCur->pBtree);
+  rc = restoreOrClearCursorPosition(pCur);
   if( rc==SQLITE_OK ){
     assert( pCur->eState==CURSOR_VALID );
     assert( pCur->pPage!=0 );
     assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
     rc = accessPayload(pCur, offset, amt, pBuf, 1, 0);
   }
+  sqlite3BtreeLeave(pCur->pBtree);
   return rc;
 }
 
@@ -3100,6 +3272,7 @@ static const unsigned char *fetchPayload(
 
   assert( pCur!=0 && pCur->pPage!=0 );
   assert( pCur->eState==CURSOR_VALID );
+  assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
   pPage = pCur->pPage;
   assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
   getCellInfo(pCur);
@@ -3130,18 +3303,23 @@ static const unsigned char *fetchPayload(
 ** b-tree page.  Write the number of available bytes into *pAmt.
 **
 ** The pointer returned is ephemeral.  The key/data may move
-** or be destroyed on the next call to any Btree routine.
+** or be destroyed on the next call to any Btree routine,
+** including calls from other threads against the same cache.
+** Hence, a mutex on the BtShared should be held prior to calling
+** this routine.
 **
 ** These routines is used to get quick access to key and data
 ** in the common case where no overflow pages are used.
 */
 const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){
+  assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
   if( pCur->eState==CURSOR_VALID ){
     return (const void*)fetchPayload(pCur, pAmt, 0);
   }
   return 0;
 }
 const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){
+  assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
   if( pCur->eState==CURSOR_VALID ){
     return (const void*)fetchPayload(pCur, pAmt, 1);
   }
@@ -3159,6 +3337,7 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
   MemPage *pOldPage;
   BtShared *pBt = pCur->pBtree->pBt;
 
+  assert( sqlite3_mutex_held(pBt->mutex) );
   assert( pCur->eState==CURSOR_VALID );
   rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage);
   if( rc ) return rc;
@@ -3185,7 +3364,10 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
 ** 1 is pointing to.
 */
 int sqlite3BtreeIsRootPage(MemPage *pPage){
-  MemPage *pParent = pPage->pParent;
+  MemPage *pParent;
+
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+  pParent = pPage->pParent;
   if( pParent==0 ) return 1;
   if( pParent->pgno>1 ) return 0;
   if( get2byte(&pParent->aData[pParent->hdrOffset+3])==0 ) return 1;
@@ -3205,6 +3387,7 @@ void sqlite3BtreeMoveToParent(BtCursor *pCur){
   MemPage *pPage;
   int idxParent;
 
+  sqlite3BtreeEnter(pCur->pBtree);
   assert( pCur->eState==CURSOR_VALID );
   pPage = pCur->pPage;
   assert( pPage!=0 );
@@ -3218,6 +3401,7 @@ void sqlite3BtreeMoveToParent(BtCursor *pCur){
   pCur->info.nSize = 0;
   assert( pParent->idxShift==0 );
   pCur->idx = idxParent;
+  sqlite3BtreeLeave(pCur->pBtree);
 }
 
 /*
@@ -3226,8 +3410,11 @@ void sqlite3BtreeMoveToParent(BtCursor *pCur){
 static int moveToRoot(BtCursor *pCur){
   MemPage *pRoot;
   int rc = SQLITE_OK;
-  BtShared *pBt = pCur->pBtree->pBt;
+  Btree *p = pCur->pBtree;
+  BtShared *pBt = p->pBt;
 
+  assert( sqlite3_mutex_held(p->pSqlite->mutex) );
+  assert( sqlite3_mutex_held(pBt->mutex) );
   if( pCur->eState==CURSOR_REQUIRESEEK ){
     clearCursorPosition(pCur);
   }
@@ -3267,17 +3454,18 @@ static int moveToRoot(BtCursor *pCur){
 */
 static int moveToLeftmost(BtCursor *pCur){
   Pgno pgno;
-  int rc;
+  int rc = SQLITE_OK;
   MemPage *pPage;
 
+  assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
+  assert( sqlite3_mutex_held(pCur->pBtree->pSqlite->mutex) );
   assert( pCur->eState==CURSOR_VALID );
-  while( !(pPage = pCur->pPage)->leaf ){
+  while( rc==SQLITE_OK && !(pPage = pCur->pPage)->leaf ){
     assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
     pgno = get4byte(findCell(pPage, pCur->idx));
     rc = moveToChild(pCur, pgno);
-    if( rc ) return rc;
   }
-  return SQLITE_OK;
+  return rc;
 }
 
 /*
@@ -3292,18 +3480,21 @@ static int moveToLeftmost(BtCursor *pCur){
 */
 static int moveToRightmost(BtCursor *pCur){
   Pgno pgno;
-  int rc;
+  int rc = SQLITE_OK;
   MemPage *pPage;
 
+  assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
+  assert( sqlite3_mutex_held(pCur->pBtree->pSqlite->mutex) );
   assert( pCur->eState==CURSOR_VALID );
-  while( !(pPage = pCur->pPage)->leaf ){
+  while( rc==SQLITE_OK && !(pPage = pCur->pPage)->leaf ){
     pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
     pCur->idx = pPage->nCell;
     rc = moveToChild(pCur, pgno);
-    if( rc ) return rc;
   }
-  pCur->idx = pPage->nCell - 1;
-  pCur->info.nSize = 0;
+  if( rc==SQLITE_OK ){
+    pCur->idx = pPage->nCell - 1;
+    pCur->info.nSize = 0;
+  }
   return SQLITE_OK;
 }
 
@@ -3313,16 +3504,21 @@ static int moveToRightmost(BtCursor *pCur){
 */
 int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
   int rc;
+
+  assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
+  assert( sqlite3_mutex_held(pCur->pBtree->pSqlite->mutex) );
   rc = moveToRoot(pCur);
-  if( rc ) return rc;
-  if( pCur->eState==CURSOR_INVALID ){
-    assert( pCur->pPage->nCell==0 );
-    *pRes = 1;
-    return SQLITE_OK;
+  if( rc==SQLITE_OK ){
+    if( pCur->eState==CURSOR_INVALID ){
+      assert( pCur->pPage->nCell==0 );
+      *pRes = 1;
+      rc = SQLITE_OK;
+    }else{
+      assert( pCur->pPage->nCell>0 );
+      *pRes = 0;
+      rc = moveToLeftmost(pCur);
+    }
   }
-  assert( pCur->pPage->nCell>0 );
-  *pRes = 0;
-  rc = moveToLeftmost(pCur);
   return rc;
 }
 
@@ -3332,16 +3528,20 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
 */
 int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
   int rc;
+  assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
+  assert( sqlite3_mutex_held(pCur->pBtree->pSqlite->mutex) );
   rc = moveToRoot(pCur);
-  if( rc ) return rc;
-  if( CURSOR_INVALID==pCur->eState ){
-    assert( pCur->pPage->nCell==0 );
-    *pRes = 1;
-    return SQLITE_OK;
+  if( rc==SQLITE_OK ){
+    if( CURSOR_INVALID==pCur->eState ){
+      assert( pCur->pPage->nCell==0 );
+      *pRes = 1;
+    }else{
+      assert( pCur->eState==CURSOR_VALID );
+      *pRes = 0;
+      rc = moveToRightmost(pCur);
+    }
   }
-  assert( pCur->eState==CURSOR_VALID );
-  *pRes = 0;
-  rc = moveToRightmost(pCur);
   return rc;
 }
 
@@ -3371,6 +3571,7 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
 **
 **     *pRes>0      The cursor is left pointing at an entry that
 **                  is larger than pKey.
+**
 */
 int sqlite3BtreeMoveto(
   BtCursor *pCur,        /* The cursor to be moved */
@@ -3380,8 +3581,13 @@ int sqlite3BtreeMoveto(
   int *pRes              /* Search result flag */
 ){
   int rc;
+
+  assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
+  assert( sqlite3_mutex_held(pCur->pBtree->pSqlite->mutex) );
   rc = moveToRoot(pCur);
-  if( rc ) return rc;
+  if( rc ){
+    return rc;
+  }
   assert( pCur->pPage );
   assert( pCur->pPage->isInit );
   if( pCur->eState==CURSOR_INVALID ){
@@ -3435,7 +3641,9 @@ int sqlite3BtreeMoveto(
           rc = sqlite3BtreeKey(pCur, 0, nCellKey, (void *)pCellKey);
           c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey);
           sqlite3_free(pCellKey);
-          if( rc ) return rc;
+          if( rc ){
+            return rc;
+          }
         }
       }
       if( c==0 ){
@@ -3482,6 +3690,7 @@ int sqlite3BtreeMoveto(
   /* NOT REACHED */
 }
 
+
 /*
 ** Return TRUE if the cursor is not pointing at an entry of the table.
 **
@@ -3503,7 +3712,7 @@ int sqlite3BtreeEof(BtCursor *pCur){
 ** was already pointing to the last entry in the database before
 ** this routine was called, then set *pRes=1.
 */
-int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
+static int btreeNext(BtCursor *pCur, int *pRes){
   int rc;
   MemPage *pPage;
 
@@ -3541,6 +3750,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
       if( sqlite3BtreeIsRootPage(pPage) ){
         *pRes = 1;
         pCur->eState = CURSOR_INVALID;
+        cursorLeave(pCur);
         return SQLITE_OK;
       }
       sqlite3BtreeMoveToParent(pCur);
@@ -3561,6 +3771,14 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
   rc = moveToLeftmost(pCur);
   return rc;
 }
+int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
+  int rc;
+  cursorEnter(pCur);
+  rc = btreeNext(pCur, pRes);
+  cursorLeave(pCur);
+  return rc;
+}
+
 
 /*
 ** Step the cursor to the back to the previous entry in the database.  If
@@ -3568,7 +3786,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
 ** was already pointing to the first entry in the database before
 ** this routine was called, then set *pRes=1.
 */
-int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
+static int btreePrevious(BtCursor *pCur, int *pRes){
   int rc;
   Pgno pgno;
   MemPage *pPage;
@@ -3594,7 +3812,9 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
   if( !pPage->leaf ){
     pgno = get4byte( findCell(pPage, pCur->idx) );
     rc = moveToChild(pCur, pgno);
-    if( rc ) return rc;
+    if( rc ){
+      return rc;
+    }
     rc = moveToRightmost(pCur);
   }else{
     while( pCur->idx==0 ){
@@ -3617,6 +3837,13 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
   *pRes = 0;
   return rc;
 }
+int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
+  int rc;
+  cursorEnter(pCur);
+  rc = btreePrevious(pCur, pRes);
+  cursorLeave(pCur);
+  return rc;
+}
 
 /*
 ** Allocate a new page from the database file.
@@ -3653,6 +3880,7 @@ static int allocateBtreePage(
   MemPage *pTrunk = 0;
   MemPage *pPrevTrunk = 0;
 
+  assert( sqlite3_mutex_held(pBt->mutex) );
   pPage1 = pBt->pPage1;
   n = get4byte(&pPage1->aData[36]);
   if( n>0 ){
@@ -3885,6 +4113,7 @@ static int freePage(MemPage *pPage){
   int rc, n, k;
 
   /* Prepare the page for freeing */
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   assert( pPage->pgno>1 );
   pPage->isInit = 0;
   releasePage(pPage->pParent);
@@ -3967,6 +4196,7 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){
   int nOvfl;
   int ovflPageSize;
 
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   sqlite3BtreeParseCellPtr(pPage, pCell, &info);
   if( info.iOverflow==0 ){
     return SQLITE_OK;  /* No overflow pages. Return without doing anything */
@@ -4023,6 +4253,8 @@ static int fillInCell(
   int nHeader;
   CellInfo info;
 
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+
   /* Fill in the header. */
   nHeader = 0;
   if( !pPage->leaf ){
@@ -4135,6 +4367,7 @@ static int reparentPage(BtShared *pBt, Pgno pgno, MemPage *pNewParent, int idx){
   MemPage *pThis;
   DbPage *pDbPage;
 
+  assert( sqlite3_mutex_held(pBt->mutex) );
   assert( pNewParent!=0 );
   if( pgno==0 ) return SQLITE_OK;
   assert( pBt->pPager!=0 );
@@ -4178,6 +4411,7 @@ static int reparentChildPages(MemPage *pPage){
   BtShared *pBt = pPage->pBt;
   int rc = SQLITE_OK;
 
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   if( pPage->leaf ) return SQLITE_OK;
 
   for(i=0; i<pPage->nCell; i++){
@@ -4212,6 +4446,7 @@ static void dropCell(MemPage *pPage, int idx, int sz){
   assert( idx>=0 && idx<pPage->nCell );
   assert( sz==cellSize(pPage, idx) );
   assert( sqlite3PagerIswriteable(pPage->pDbPage) );
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   data = pPage->aData;
   ptr = &data[pPage->cellOffset + 2*idx];
   pc = get2byte(ptr);
@@ -4265,6 +4500,7 @@ static int insertCell(
   assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
   assert( sz==cellSizePtr(pPage, pCell) );
   assert( sqlite3PagerIswriteable(pPage->pDbPage) );
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   if( pPage->nOverflow || sz+2>pPage->nFree ){
     if( pTemp ){
       memcpy(pTemp+nSkip, pCell+nSkip, sz-nSkip);
@@ -4339,6 +4575,7 @@ static void assemblePage(
   u8 *data;         /* Data for the page */
 
   assert( pPage->nOverflow==0 );
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   totalSize = 0;
   for(i=0; i<nCell; i++){
     totalSize += aSize[i];
@@ -4413,6 +4650,8 @@ static int balance_quick(MemPage *pPage, MemPage *pParent){
   int parentSize;                   /* Size of new divider cell */
   u8 parentCell[64];                /* Space for the new divider cell */
 
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+
   /* Allocate a new page. Insert the overflow cell from pPage
   ** into it. Then remove the overflow cell from pPage.
   */
@@ -4537,6 +4776,8 @@ static int balance_nonroot(MemPage *pPage){
   u8 *aFrom = 0;
 #endif
 
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+
   /* 
   ** Find the parent page.
   */
@@ -5084,6 +5325,7 @@ static int balance_shallower(MemPage *pPage){
 
   assert( pPage->pParent==0 );
   assert( pPage->nCell==0 );
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   pBt = pPage->pBt;
   mxCellPerPage = MX_CELL(pBt);
   apCell = sqlite3_malloc( mxCellPerPage*(sizeof(u8*)+sizeof(int)) );
@@ -5188,6 +5430,7 @@ static int balance_deeper(MemPage *pPage){
   assert( pPage->pParent==0 );
   assert( pPage->nOverflow>0 );
   pBt = pPage->pBt;
+  assert( sqlite3_mutex_held(pBt->mutex) );
   rc = allocateBtreePage(pBt, &pChild, &pgnoChild, pPage->pgno, 0);
   if( rc ) return rc;
   assert( sqlite3PagerIswriteable(pChild->pDbPage) );
@@ -5236,6 +5479,7 @@ balancedeeper_out:
 */
 static int balance(MemPage *pPage, int insert){
   int rc = SQLITE_OK;
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   if( pPage->pParent==0 ){
     if( pPage->nOverflow>0 ){
       rc = balance_deeper(pPage);
@@ -5272,6 +5516,8 @@ static int checkReadLocks(Btree *pBtree, Pgno pgnoRoot, BtCursor *pExclude){
   BtCursor *p;
   BtShared *pBt = pBtree->pBt;
   sqlite3 *db = pBtree->pSqlite;
+  assert( sqlite3_mutex_held(pBt->mutex) );
+  assert( sqlite3_mutex_held(db->mutex) );
   for(p=pBt->pCursor; p; p=p->pNext){
     if( p==pExclude ) continue;
     if( p->eState!=CURSOR_VALID ) continue;
@@ -5309,19 +5555,25 @@ int sqlite3BtreeInsert(
   int loc;
   int szNew;
   MemPage *pPage;
-  BtShared *pBt = pCur->pBtree->pBt;
+  Btree *p = pCur->pBtree;
+  BtShared *pBt = p->pBt;
   unsigned char *oldCell;
   unsigned char *newCell = 0;
 
+  sqlite3BtreeEnter(p);
   if( pBt->inTransaction!=TRANS_WRITE ){
     /* Must start a transaction before doing an insert */
-    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+    rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+    sqlite3BtreeLeave(p);
+    return rc;
   }
   assert( !pBt->readOnly );
   if( !pCur->wrFlag ){
+    sqlite3BtreeLeave(p);
     return SQLITE_PERM;   /* Cursor not open for writing */
   }
   if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur) ){
+    sqlite3BtreeLeave(p);
     return SQLITE_LOCKED; /* The table pCur points to has a read lock */
   }
 
@@ -5331,6 +5583,7 @@ int sqlite3BtreeInsert(
     SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) ||
     SQLITE_OK!=(rc = sqlite3BtreeMoveto(pCur, pKey, nKey, appendBias, &loc))
   ){
+    sqlite3BtreeLeave(p);
     return rc;
   }
 
@@ -5342,7 +5595,10 @@ int sqlite3BtreeInsert(
           loc==0 ? "overwrite" : "new entry"));
   assert( pPage->isInit );
   rc = sqlite3PagerWrite(pPage->pDbPage);
-  if( rc ) return rc;
+  if( rc ){
+    sqlite3BtreeLeave(p);
+    return rc;
+  }
   newCell = sqlite3_malloc( MX_CELL_SIZE(pBt) );
   if( newCell==0 ) return SQLITE_NOMEM;
   rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew);
@@ -5377,6 +5633,7 @@ int sqlite3BtreeInsert(
   }
 end_insert:
   sqlite3_free(newCell);
+  sqlite3BtreeLeave(p);
   return rc;
 }
 
@@ -5389,21 +5646,28 @@ int sqlite3BtreeDelete(BtCursor *pCur){
   unsigned char *pCell;
   int rc;
   Pgno pgnoChild = 0;
-  BtShared *pBt = pCur->pBtree->pBt;
+  Btree *p = pCur->pBtree;
+  BtShared *pBt = p->pBt;
 
+  sqlite3BtreeEnter(p);
   assert( pPage->isInit );
   if( pBt->inTransaction!=TRANS_WRITE ){
     /* Must start a transaction before doing a delete */
-    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+    rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+    sqlite3BtreeLeave(p);
+    return rc;
   }
   assert( !pBt->readOnly );
   if( pCur->idx >= pPage->nCell ){
+    sqlite3BtreeLeave(p);
     return SQLITE_ERROR;  /* The cursor is not pointing to anything */
   }
   if( !pCur->wrFlag ){
+    sqlite3BtreeLeave(p);
     return SQLITE_PERM;   /* Did not open this cursor for writing */
   }
   if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur) ){
+    sqlite3BtreeLeave(p);
     return SQLITE_LOCKED; /* The table pCur points to has a read lock */
   }
 
@@ -5417,6 +5681,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
     (rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur))!=0 ||
     (rc = sqlite3PagerWrite(pPage->pDbPage))!=0
   ){
+    sqlite3BtreeLeave(p);
     return rc;
   }
 
@@ -5429,7 +5694,10 @@ int sqlite3BtreeDelete(BtCursor *pCur){
     pgnoChild = get4byte(pCell);
   }
   rc = clearCell(pPage, pCell);
-  if( rc ) return rc;
+  if( rc ){
+    sqlite3BtreeLeave(p);
+    return rc;
+  }
 
   if( !pPage->leaf ){
     /*
@@ -5486,6 +5754,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
   if( rc==SQLITE_OK ){
     moveToRoot(pCur);
   }
+  sqlite3BtreeLeave(p);
   return rc;
 }
 
@@ -5500,20 +5769,24 @@ int sqlite3BtreeDelete(BtCursor *pCur){
 **     BTREE_INTKEY|BTREE_LEAFDATA     Used for SQL tables with rowid keys
 **     BTREE_ZERODATA                  Used for SQL indices
 */
-int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
+static int btreeCreateTable(Btree *p, int *piTable, int flags){
   BtShared *pBt = p->pBt;
   MemPage *pRoot;
   Pgno pgnoRoot;
   int rc;
+
   if( pBt->inTransaction!=TRANS_WRITE ){
     /* Must start a transaction first */
-    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+    rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+    return rc;
   }
   assert( !pBt->readOnly );
 
 #ifdef SQLITE_OMIT_AUTOVACUUM
   rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0);
-  if( rc ) return rc;
+  if( rc ){
+    return rc;
+  }
 #else
   if( pBt->autoVacuum ){
     Pgno pgnoMove;      /* Move a page here to make room for the root-page */
@@ -5531,7 +5804,9 @@ int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
     ** created so far, so the new root-page is (meta[3]+1).
     */
     rc = sqlite3BtreeGetMeta(p, 4, &pgnoRoot);
-    if( rc!=SQLITE_OK ) return rc;
+    if( rc!=SQLITE_OK ){
+      return rc;
+    }
     pgnoRoot++;
 
     /* The new root-page may not be allocated on a pointer-map page, or the
@@ -5624,6 +5899,13 @@ int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
   *piTable = (int)pgnoRoot;
   return SQLITE_OK;
 }
+int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
+  int rc;
+  sqlite3BtreeEnter(p);
+  rc = btreeCreateTable(p, piTable, flags);
+  sqlite3BtreeLeave(p);
+  return rc;
+}
 
 /*
 ** Erase the given database page and all its children.  Return
@@ -5640,6 +5922,7 @@ static int clearDatabasePage(
   unsigned char *pCell;
   int i;
 
+  assert( sqlite3_mutex_held(pBt->mutex) );
   if( pgno>sqlite3PagerPagecount(pBt->pPager) ){
     return SQLITE_CORRUPT_BKPT;
   }
@@ -5682,20 +5965,18 @@ cleardatabasepage_out:
 int sqlite3BtreeClearTable(Btree *p, int iTable){
   int rc;
   BtShared *pBt = p->pBt;
+  sqlite3BtreeEnter(p);
   if( p->inTrans!=TRANS_WRITE ){
-    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
-  }
-  rc = checkReadLocks(p, iTable, 0);
-  if( rc ){
-    return rc;
-  }
-
-  /* Save the position of all cursors open on this table */
-  if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){
-    return rc;
+    rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+  }else if( (rc = checkReadLocks(p, iTable, 0))!=SQLITE_OK ){
+    /* nothing to do */
+  }else if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){
+    /* nothing to do */
+  }else{
+    rc = clearDatabasePage(pBt, (Pgno)iTable, 0, 0);
   }
-
-  return clearDatabasePage(pBt, (Pgno)iTable, 0, 0);
+  sqlite3BtreeLeave(p);
+  return rc;
 }
 
 /*
@@ -5718,11 +5999,12 @@ int sqlite3BtreeClearTable(Btree *p, int iTable){
 ** The last root page is recorded in meta[3] and the value of
 ** meta[3] is updated by this procedure.
 */
-int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
+static int btreeDropTable(Btree *p, int iTable, int *piMoved){
   int rc;
   MemPage *pPage = 0;
   BtShared *pBt = p->pBt;
 
+  assert( sqlite3_mutex_held(pBt->mutex) );
   if( p->inTrans!=TRANS_WRITE ){
     return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
   }
@@ -5824,6 +6106,13 @@ int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
   }
   return rc;  
 }
+int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
+  int rc;
+  sqlite3BtreeEnter(p);
+  rc = btreeDropTable(p, iTable, piMoved);
+  sqlite3BtreeLeave(p);
+  return rc;
+}
 
 
 /*
@@ -5842,6 +6131,8 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
   unsigned char *pP1;
   BtShared *pBt = p->pBt;
 
+  sqlite3BtreeEnter(p);
+
   /* Reading a meta-data value requires a read-lock on page 1 (and hence
   ** the sqlite_master table. We grab this lock regardless of whether or
   ** not the SQLITE_ReadUncommitted flag is set (the table rooted at page
@@ -5849,12 +6140,16 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
   */
   rc = queryTableLock(p, 1, READ_LOCK);
   if( rc!=SQLITE_OK ){
+    sqlite3BtreeLeave(p);
     return rc;
   }
 
   assert( idx>=0 && idx<=15 );
   rc = sqlite3PagerGet(pBt->pPager, 1, &pDbPage);
-  if( rc ) return rc;
+  if( rc ){
+    sqlite3BtreeLeave(p);
+    return rc;
+  }
   pP1 = (unsigned char *)sqlite3PagerGetData(pDbPage);
   *pMeta = get4byte(&pP1[36 + idx*4]);
   sqlite3PagerUnref(pDbPage);
@@ -5868,6 +6163,7 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
 
   /* Grab the read-lock on page 1. */
   rc = lockTable(p, 1, READ_LOCK);
+  sqlite3BtreeLeave(p);
   return rc;
 }
 
@@ -5880,20 +6176,24 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
   unsigned char *pP1;
   int rc;
   assert( idx>=1 && idx<=15 );
+  sqlite3BtreeEnter(p);
   if( p->inTrans!=TRANS_WRITE ){
-    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
-  }
-  assert( pBt->pPage1!=0 );
-  pP1 = pBt->pPage1->aData;
-  rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
-  if( rc ) return rc;
-  put4byte(&pP1[36 + idx*4], iMeta);
-  if( idx==7 ){
-    assert( pBt->autoVacuum || iMeta==0 );
-    assert( iMeta==0 || iMeta==1 );
-    pBt->incrVacuum = iMeta;
+    rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+  }else{
+    assert( pBt->pPage1!=0 );
+    pP1 = pBt->pPage1->aData;
+    rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
+    if( rc==SQLITE_OK ){
+      put4byte(&pP1[36 + idx*4], iMeta);
+      if( idx==7 ){
+        assert( pBt->autoVacuum || iMeta==0 );
+        assert( iMeta==0 || iMeta==1 );
+        pBt->incrVacuum = iMeta;
+      }
+    }
   }
-  return SQLITE_OK;
+  sqlite3BtreeLeave(p);
+  return rc;
 }
 
 /*
@@ -5905,6 +6205,7 @@ int sqlite3BtreeFlags(BtCursor *pCur){
   ** restoreOrClearCursorPosition() here.
   */
   MemPage *pPage = pCur->pPage;
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   return pPage ? pPage->aData[pPage->hdrOffset] : 0;
 }
 
@@ -5914,6 +6215,8 @@ int sqlite3BtreeFlags(BtCursor *pCur){
 ** testing and debugging only.
 */
 Pager *sqlite3BtreePager(Btree *p){
+  assert( sqlite3_mutex_held(p->pSqlite->mutex) );
+  assert( sqlite3_mutex_held(p->pBt->mutex) );
   return p->pBt->pPager;
 }
 
@@ -6260,8 +6563,10 @@ char *sqlite3BtreeIntegrityCheck(
   IntegrityCk sCheck;
   BtShared *pBt = p->pBt;
 
+  sqlite3BtreeEnter(p);
   nRef = sqlite3PagerRefcount(pBt->pPager);
   if( lockBtreeWithRetry(p)!=SQLITE_OK ){
+    sqlite3BtreeLeave(p);
     return sqlite3StrDup("Unable to acquire a read lock on the database");
   }
   sCheck.pBt = pBt;
@@ -6277,12 +6582,14 @@ char *sqlite3BtreeIntegrityCheck(
 #endif
   if( sCheck.nPage==0 ){
     unlockBtreeIfUnused(pBt);
+    sqlite3BtreeLeave(p);
     return 0;
   }
   sCheck.anRef = sqlite3_malloc( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) );
   if( !sCheck.anRef ){
     unlockBtreeIfUnused(pBt);
     *pnErr = 1;
+    sqlite3BtreeLeave(p);
     return sqlite3MPrintf(p->pSqlite, "Unable to malloc %d bytes", 
         (sCheck.nPage+1)*sizeof(sCheck.anRef[0]));
   }
@@ -6344,6 +6651,7 @@ char *sqlite3BtreeIntegrityCheck(
 
   /* Clean  up and report errors.
   */
+  sqlite3BtreeLeave(p);
   sqlite3_free(sCheck.anRef);
   *pnErr = sCheck.nErr;
   return sCheck.zErrMsg;
@@ -6355,6 +6663,7 @@ char *sqlite3BtreeIntegrityCheck(
 */
 const char *sqlite3BtreeGetFilename(Btree *p){
   assert( p->pBt->pPager!=0 );
+  assert( sqlite3_mutex_held(p->pBt->mutex) );
   return sqlite3PagerFilename(p->pBt->pPager);
 }
 
@@ -6363,6 +6672,7 @@ const char *sqlite3BtreeGetFilename(Btree *p){
 */
 const char *sqlite3BtreeGetDirname(Btree *p){
   assert( p->pBt->pPager!=0 );
+  assert( sqlite3_mutex_held(p->pBt->mutex) );
   return sqlite3PagerDirname(p->pBt->pPager);
 }
 
@@ -6373,6 +6683,7 @@ const char *sqlite3BtreeGetDirname(Btree *p){
 */
 const char *sqlite3BtreeGetJournalname(Btree *p){
   assert( p->pBt->pPager!=0 );
+  assert( sqlite3_mutex_held(p->pBt->mutex) );
   return sqlite3PagerJournalname(p->pBt->pPager);
 }
 
@@ -6384,7 +6695,7 @@ const char *sqlite3BtreeGetJournalname(Btree *p){
 ** The size of file pBtFrom may be reduced by this operation.
 ** If anything goes wrong, the transaction on pBtFrom is rolled back.
 */
-int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
+static int btreeCopyFile(Btree *pTo, Btree *pFrom){
   int rc = SQLITE_OK;
   Pgno i, nPage, nToPage, iSkip;
 
@@ -6436,12 +6747,24 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
   }
   return rc;  
 }
+int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
+  int rc;
+  sqlite3BtreeEnter(pTo);
+  sqlite3BtreeEnter(pFrom);
+  rc = btreeCopyFile(pTo, pFrom);
+  sqlite3BtreeLeave(pFrom);
+  sqlite3BtreeLeave(pTo);
+  return rc;
+}
+
 #endif /* SQLITE_OMIT_VACUUM */
 
 /*
 ** Return non-zero if a transaction is active.
 */
 int sqlite3BtreeIsInTrans(Btree *p){
+  assert( sqlite3_mutex_held(p->pBt->mutex) );
+  assert( sqlite3_mutex_held(p->pSqlite->mutex) );
   return (p && (p->inTrans==TRANS_WRITE));
 }
 
@@ -6449,6 +6772,8 @@ int sqlite3BtreeIsInTrans(Btree *p){
 ** Return non-zero if a statement transaction is active.
 */
 int sqlite3BtreeIsInStmt(Btree *p){
+  assert( sqlite3_mutex_held(p->pBt->mutex) );
+  assert( sqlite3_mutex_held(p->pSqlite->mutex) );
   return (p->pBt && p->pBt->inStmt);
 }
 
@@ -6456,6 +6781,8 @@ int sqlite3BtreeIsInStmt(Btree *p){
 ** Return non-zero if a read (or write) transaction is active.
 */
 int sqlite3BtreeIsInReadTrans(Btree *p){
+  assert( sqlite3_mutex_held(p->pBt->mutex) );
+  assert( sqlite3_mutex_held(p->pSqlite->mutex) );
   return (p && (p->inTrans!=TRANS_NONE));
 }
 
@@ -6477,6 +6804,8 @@ int sqlite3BtreeIsInReadTrans(Btree *p){
 */
 void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
   BtShared *pBt = p->pBt;
+  assert( sqlite3_mutex_held(pBt->mutex) );
+  assert( sqlite3_mutex_held(p->pSqlite->mutex) );
   if( !pBt->pSchema ){
     pBt->pSchema = sqlite3MallocZero(nBytes);
     pBt->xFreeSchema = xFree;
@@ -6489,6 +6818,8 @@ void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
 ** handle holds an exclusive lock on the sqlite_master table.
 */
 int sqlite3BtreeSchemaLocked(Btree *p){
+  assert( sqlite3_mutex_held(p->pBt->mutex) );
+  assert( sqlite3_mutex_held(p->pSqlite->mutex) );
   return (queryTableLock(p, MASTER_ROOT, READ_LOCK)!=SQLITE_OK);
 }
 
@@ -6502,10 +6833,12 @@ int sqlite3BtreeSchemaLocked(Btree *p){
 int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
   int rc = SQLITE_OK;
   u8 lockType = (isWriteLock?WRITE_LOCK:READ_LOCK);
+  sqlite3BtreeEnter(p);
   rc = queryTableLock(p, iTab, lockType);
   if( rc==SQLITE_OK ){
     rc = lockTable(p, iTab, lockType);
   }
+  sqlite3BtreeLeave(p);
   return rc;
 }
 #endif
@@ -6519,7 +6852,8 @@ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
 ** to change the length of the data stored.
 */
 int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
-
+  assert( sqlite3_mutex_held(pCsr->pBtree->pBt->mutex) );
+  assert( sqlite3_mutex_held(pCsr->pBtree->pSqlite->mutex) );
   assert(pCsr->isIncrblobHandle);
   if( pCsr->eState==CURSOR_REQUIRESEEK ){
     return SQLITE_ABORT;
@@ -6556,6 +6890,8 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
 ** sqlite3BtreePutData()).
 */
 void sqlite3BtreeCacheOverflow(BtCursor *pCur){
+  assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
+  assert( sqlite3_mutex_held(pCur->pBtree->pSqlite->mutex) );
   assert(!pCur->isIncrblobHandle);
   assert(!pCur->aOverflow);
   pCur->isIncrblobHandle = 1;
index d67a432aee9d51ae345343794edbe0c466367997..5f15d101bd0357f0d711bb1121f4ef25c6f380a9 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.83 2007/08/17 01:14:38 drh Exp $
+** @(#) $Id: btree.h,v 1.84 2007/08/20 22:48:42 drh Exp $
 */
 #ifndef _BTREE_H_
 #define _BTREE_H_
@@ -100,12 +100,12 @@ int sqlite3BtreeLockTable(Btree *, int, u8);
 ** use mutexes to access the BtShared structures.  So make the
 ** Enter and Leave procedures no-ops.
 */
-#ifdef SQLITE_OMIT_SHARED_CACHE
-# define sqlite3BtreeEnter(X)
-# define sqlite3BtreeLeave(X)
-#else
+#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
   void sqlite3BtreeEnter(Btree*);
   void sqlite3BtreeLeave(Btree*);
+#else
+# define sqlite3BtreeEnter(X)
+# define sqlite3BtreeLeave(X)
 #endif
 
 const char *sqlite3BtreeGetFilename(Btree *);
index 343d1eb6e8a9326be0a8294b785e4ccac39c79d8..6d2335870a723fddf33d3e442fdadd6fa99a5c76 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btreeInt.h,v 1.7 2007/08/20 13:14:29 drh Exp $
+** $Id: btreeInt.h,v 1.8 2007/08/20 22:48:42 drh Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** For a detailed discussion of BTrees, refer to
@@ -264,6 +264,9 @@ typedef struct BtLock BtLock;
 ** walk up the BTree from any leaf to the root.  Care must be taken to
 ** unref() the parent page pointer when this page is no longer referenced.
 ** The pageDestructor() routine handles that chore.
+**
+** Access to all fields of this structure is controlled by the mutex
+** stored in MemPage.pBt->mutex.
 */
 struct MemPage {
   u8 isInit;           /* True if previously initialized. MUST BE FIRST! */
@@ -315,8 +318,7 @@ struct MemPage {
 ** schema associated with the database file are all contained within
 ** the BtShared object.
 **
-** All fields in this structure are accessed under the sqlite3.pMutex
-** mutex.
+** All fields in this structure are accessed under the sqlite3.mutex.
 */
 struct Btree {
   sqlite3 *pSqlite;  /* The database connection holding this btree */
@@ -416,9 +418,9 @@ struct CellInfo {
 ** but cursors cannot be shared.  Each cursor is associated with a
 ** particular database connection identified BtCursor.pBtree.pSqlite.
 **
-** The fields in this structure are accessed under the sqlite3.pMutex
-** mutex, specifically the BtCurser.pBtree->pSqlite->pMutex mutex.
-** The pNext and pPrev fields also require the BtShared.mutex mutex.
+** Fields in this structure are accessed under the BtShared.mutex
+** mutex.  The pBtree field is safe to access under the
+** BtShared->pBtree->pSqlite->mutex mutex.
 */
 struct BtCursor {
   Btree *pBtree;            /* The Btree to which this cursor belongs */
index 9cb779c6c168062077edd0d1674d1707df88f948..f0bbe7da0f8c8f0a5d38bb290dbac811d4c4b1af 100644 (file)
@@ -14,7 +14,7 @@
 ** other files are for internal use by SQLite and should not be
 ** accessed by users of the library.
 **
-** $Id: main.c,v 1.388 2007/08/20 17:37:48 shess Exp $
+** $Id: main.c,v 1.389 2007/08/20 22:48:42 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -218,8 +218,8 @@ int sqlite3_close(sqlite3 *db){
   ** structure?
   */
   sqlite3_free(db->aDb[1].pSchema);
+  sqlite3_vfs_release(db->pVfs);
   sqlite3_free(db);
-  /* sqlite3ReleaseThreadData(); */
   return SQLITE_OK;
 }
 
@@ -900,7 +900,7 @@ static int openDatabase(
   /* Allocate the sqlite data structure */
   db = sqlite3MallocZero( sizeof(sqlite3) );
   if( db==0 ) goto opendb_out;
-  db->pVfs = sqlite3_find_vfs(0);
+  db->pVfs = sqlite3_vfs_find(0);
   db->errMask = 0xff;
   db->priorNewRowid = 0;
   db->magic = SQLITE_MAGIC_BUSY;
@@ -1385,7 +1385,7 @@ int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
 */
 int sqlite3_sleep(int ms){
   sqlite3_vfs *pVfs;
-  pVfs = sqlite3_find_vfs(0);
+  pVfs = sqlite3_vfs_find(0);
 
   /* This function works in milliseconds, but the underlying OsSleep() 
   ** API uses microseconds. Hence the 1000's.
index f9cd933b5a25d76c3973fe9da7f0cbcadc6879c5..a88802dceea066284ed1f37f2c94c65b261f9a62 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains the C functions that implement a memory
 ** allocation subsystem for use by SQLite.  
 **
-** $Id: mem2.c,v 1.4 2007/08/16 19:40:17 drh Exp $
+** $Id: mem2.c,v 1.5 2007/08/20 22:48:43 drh Exp $
 */
 
 /*
@@ -132,6 +132,12 @@ static struct {
   int iFail;    /* Decrement and fail malloc when this is 1 */
   int iReset;   /* When malloc fails set iiFail to this value */
   int iFailCnt; /* Number of failures */
+
+  /* 
+  ** sqlite3MallocDisallow() increments the following counter.
+  ** sqlite3MallocAllow() decrements it.
+  */
+  int disallow; /* Do not allow memory allocation */
   
   
 } mem = {  /* This variable holds all of the local data */
@@ -254,6 +260,7 @@ void *sqlite3_malloc(unsigned int nByte){
     mem.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM);
   }
   sqlite3_mutex_enter(mem.mutex);
+  assert( mem.disallow==0 );
   if( mem.nowUsed+nByte>=mem.alarmThreshold ){
     sqlite3MemsysAlarm(nByte);
   }
@@ -367,6 +374,7 @@ void *sqlite3_realloc(void *pPrior, unsigned int nByte){
     sqlite3_free(pPrior);
     return 0;
   }
+  assert( mem.disallow==0 );
   pOldHdr = sqlite3MemsysGetHeader(pPrior);
   pNew = sqlite3_malloc(nByte);
   if( pNew ){
@@ -440,4 +448,24 @@ int sqlite3_memdebug_fail(int iFail, int iRepeat){
   return n;
 }
 
+/*
+** The following two routines are used to assert that no memory
+** allocations occur between one call and the next.  The use of
+** these routines does not change the computed results in any way.
+** These routines are like asserts.
+*/
+void sqlite3MallocDisallow(void){
+  assert( mem.mutex!=0 );
+  sqlite3_mutex_enter(mem.mutex);
+  mem.disallow++;
+  sqlite3_mutex_leave(mem.mutex);
+}
+void sqlite3MallocAllow(void){
+  assert( mem.mutex );
+  sqlite3_mutex_enter(mem.mutex);
+  assert( mem.disallow>0 );
+  mem.disallow--;
+  sqlite3_mutex_leave(mem.mutex);
+}
+
 #endif /* SQLITE_MEMDEBUG && !SQLITE_OMIT_MEMORY_ALLOCATION */
index ffee6d78504aaf77d2f2152bc0a39e469479752e..619a1afc48e29de903f1355a5ede9b78587e5b37 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains the C functions that implement mutexes for
 ** use by the SQLite core.
 **
-** $Id: mutex.c,v 1.4 2007/08/17 01:14:38 drh Exp $
+** $Id: mutex.c,v 1.5 2007/08/20 22:48:43 drh Exp $
 */
 
 /*
@@ -41,7 +41,7 @@
 ** that means that a mutex could not be allocated. 
 */
 sqlite3_mutex *sqlite3_mutex_alloc(int idNotUsed){
-  return (sqlite3_mutex*)sqlite3_mutex_alloc;
+  return (sqlite3_mutex*)8;
 }
 
 /*
@@ -64,13 +64,24 @@ void sqlite3_mutex_enter(sqlite3_mutex *pNotUsed){}
 int sqlite3_mutex_try(sqlite3_mutex *pNotUsed){ return SQLITE_OK; }
 
 /*
-** The sqlite3_mutex_exit() routine exits a mutex that was
+** The sqlite3_mutex_leave() routine exits a mutex that was
 ** previously entered by the same thread.  The behavior
 ** is undefined if the mutex is not currently entered or
 ** is not currently allocated.  SQLite will never do either.
 */
 void sqlite3_mutex_leave(sqlite3_mutex *pNotUsed){}
 
+/*
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
+** intended for use inside assert() statements.
+*/
+int sqlite3_mutex_held(sqlite3_mutex *pNotUsed){
+  return 1;
+}
+int sqlite3_mutex_notheld(sqlite3_mutex *pNotUsed){
+  return 1;
+}
+
 #if 0
 /**************** Non-recursive Pthread Mutex Implementation *****************
 **
@@ -293,6 +304,17 @@ void sqlite3_mutex_leave(sqlite3_mutex *pMutex){
     pthread_mutex_unlock(&p->auxMutex);
   }
 }
+
+/*
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
+** intended for use inside assert() statements.
+*/
+int sqlite3_mutex_held(sqlite3_mutex *pNotUsed){
+  return 1;
+}
+int sqlite3_mutex_notheld(sqlite3_mutex *pNotUsed){
+  return 1;
+}
 #endif /* non-recursive pthreads */
 
 #endif /* !defined(SQLITE_MUTEX_APPDEF) */
index 73f067bbcf8247ee404d523f3711f810ef539c3b..aeafb26da0418ce4d3f2ba8bb5005ca98f30d0e3 100644 (file)
--- a/src/os.c
+++ b/src/os.c
@@ -74,6 +74,10 @@ int sqlite3OsDeviceCharacteristics(sqlite3_file *id){
   }
 #endif
 
+/*
+** The next group of routines are convenience wrappers around the
+** VFS methods.
+*/
 int sqlite3OsOpen(
   sqlite3_vfs *pVfs, 
   const char *zPath, 
@@ -81,9 +85,6 @@ int sqlite3OsOpen(
   int flags, 
   int *pFlagsOut
 ){
-#if defined(SQLITE_TEST) && !defined(SQLITE_OMIT_DISKIO)
-  return sqlite3CrashFileOpen(pVfs, zPath, pFile, flags, pFlagsOut);
-#endif
   return pVfs->xOpen(pVfs->pAppData, zPath, pFile, flags, pFlagsOut);
 }
 int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
@@ -154,7 +155,96 @@ int sqlite3OsCloseFree(sqlite3_file *pFile){
 */
 extern sqlite3_vfs sqlite3DefaultVfs;
 
-sqlite3_vfs *sqlite3_find_vfs(const char *zVfs){
-  return &sqlite3DefaultVfs;
+/*
+** The list of all registered VFS implementations.
+*/
+static sqlite3_vfs *vfsList = &sqlite3DefaultVfs;
+
+/*
+** Locate a VFS by name.  If no name is given, simply return the
+** first VFS on the list.
+*/
+sqlite3_vfs *sqlite3_vfs_find(const char *zVfs){
+  sqlite3_mutex *mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
+  sqlite3_vfs *pVfs;
+  sqlite3_mutex_enter(mutex);
+  for(pVfs = vfsList; pVfs; pVfs=pVfs->pNext){
+    if( zVfs==0 ) break;
+    if( strcmp(zVfs, pVfs->zName)==0 ) break;
+  }
+  if( pVfs ){
+    pVfs->nRef++;
+    assert( pVfs->nRef==1 || pVfs->vfsMutex!=0 );
+    assert( pVfs->nRef>1 || pVfs->vfsMutex==0 );
+    if( pVfs->vfsMutex==0 ){
+      pVfs->vfsMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+    }
+  }
+  sqlite3_mutex_leave(mutex);
+  return pVfs;
+}
+
+/*
+** Release a VFS once it is no longer needed.
+*/
+int sqlite3_vfs_release(sqlite3_vfs *pVfs){
+  sqlite3_mutex *mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
+  sqlite3_mutex_enter(mutex);
+  assert( pVfs->nRef>0 );
+  pVfs->nRef--;
+  if( pVfs->nRef==0 ){
+    sqlite3_mutex_free(pVfs->vfsMutex);
+    pVfs->vfsMutex = 0;
+  }
+  sqlite3_mutex_leave(mutex);
+  return SQLITE_OK;
 }
 
+/*
+** Unlink a VFS from the linked list
+*/
+static void vfsUnlink(sqlite3_vfs *pVfs){
+  assert( sqlite3_mutex_held(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)) );
+  if( vfsList==pVfs ){
+    vfsList = pVfs->pNext;
+  }else{
+    sqlite3_vfs *p = vfsList;
+    while( p->pNext && p->pNext!=pVfs ){
+      p = p->pNext;
+    }
+    if( p->pNext==pVfs ){
+      p->pNext = pVfs->pNext;
+    }
+  }
+}
+
+/*
+** Register a VFS with the system.  It is harmless to register the same
+** VFS multiple times.  The new VFS becomes the default if makeDflt is
+** true.
+*/
+int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){
+  sqlite3_mutex *mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
+  sqlite3_mutex_enter(mutex);
+  vfsUnlink(pVfs);
+  if( makeDflt || vfsList==0 ){
+    pVfs->pNext = vfsList;
+    vfsList = pVfs;
+  }else{
+    pVfs->pNext = vfsList->pNext;
+    pVfs->pNext = pVfs;
+  }
+  sqlite3_mutex_leave(mutex);
+  return SQLITE_OK;
+}
+
+/*
+** Unregister a VFS so that it is no longer accessible.
+*/
+int sqlite3_vfs_unregister(sqlite3_vfs *pVfs){
+  sqlite3_mutex *mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
+  sqlite3_mutex_enter(mutex);
+  vfsUnlink(pVfs);
+  sqlite3_mutex_leave(mutex);
+  return SQLITE_OK;
+}
index b7381857faa64157b26ed95adcb6a553f3430e8e..de7f385fb054090d1b5d6454a387135094365e10 100644 (file)
 
 /*
 ** Macros used to determine whether or not to use threads.  The
-** SQLITE_UNIX_THREADS macro is defined if we are synchronizing for
+** SQLITE_OS2_THREADS macro is defined if we are synchronizing for
 ** Posix threads and SQLITE_W32_THREADS is defined if we are
 ** synchronizing using Win32 threads.
 */
 /* this mutex implementation only available with EMX */
-#if defined(THREADSAFE) && THREADSAFE
+#if SQLITE_THREADSAFE
 # include <sys/builtin.h>
 # include <sys/smutex.h>
 # define SQLITE_OS2_THREADS 1
index a67748f613a7f136e8ebf3e76d6c59ccf37b422c..0544e071c0d16ebc1860243aa010330d2c2ba92d 100644 (file)
 ** If we are to be thread-safe, include the pthreads header and define
 ** the SQLITE_UNIX_THREADS macro.
 */
-#ifndef THREADSAFE
-# define THREADSAFE 1
-#endif
-#if THREADSAFE
+#if SQLITE_THREADSAFE
 # include <pthread.h>
 # define SQLITE_UNIX_THREADS 1
 #endif
@@ -102,7 +99,7 @@ struct unixFile {
   unsigned char locktype;   /* The type of lock held on this fd */
   unsigned char isOpen;     /* True if needs to be closed */
   int dirfd;                /* File descriptor for the directory */
-#ifdef SQLITE_UNIX_THREADS
+#if SQLITE_THREADSAFE
   pthread_t tid;            /* The thread that "owns" this unixFile */
 #endif
 };
@@ -150,7 +147,7 @@ struct unixFile {
 ** The threadid macro resolves to the thread-id or to 0.  Used for
 ** testing and debugging only.
 */
-#ifdef SQLITE_UNIX_THREADS
+#if SQLITE_THREADSAFE
 #define threadid pthread_self()
 #else
 #define threadid 0
@@ -172,7 +169,7 @@ struct unixFile {
 ** recomputed because its key includes the thread-id.  See the 
 ** transferOwnership() function below for additional information
 */
-#if defined(SQLITE_UNIX_THREADS)
+#if SQLITE_THREADSAFE
 # define SET_THREADID(X)   (X)->tid = pthread_self()
 # define CHECK_THREADID(X) (threadsOverrideEachOthersLocks==0 && \
                             !pthread_equal((X)->tid, pthread_self()))
@@ -293,7 +290,7 @@ struct unixFile {
 struct lockKey {
   dev_t dev;       /* Device number */
   ino_t ino;       /* Inode number */
-#ifdef SQLITE_UNIX_THREADS
+#if SQLITE_THREADSAFE
   pthread_t tid;   /* Thread ID or zero if threads can override each other */
 #endif
 };
@@ -383,7 +380,7 @@ static void leaveMutex(){
   sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_GLOBAL));
 }
 
-#ifdef SQLITE_UNIX_THREADS
+#if SQLITE_THREADSAFE
 /*
 ** This variable records whether or not threads can override each others
 ** locks.
@@ -520,7 +517,7 @@ static void testThreadLockingBehavior(int fd_orig){
   close(fd);
   threadsOverrideEachOthersLocks =  d[0].result==0 && d[1].result==0;
 }
-#endif /* SQLITE_UNIX_THREADS */
+#endif /* SQLITE_THREADSAFE */
 
 /*
 ** Release a lockInfo structure previously allocated by findLockInfo().
@@ -648,7 +645,7 @@ static int findLockInfo(
   memset(&key1, 0, sizeof(key1));
   key1.dev = statbuf.st_dev;
   key1.ino = statbuf.st_ino;
-#ifdef SQLITE_UNIX_THREADS
+#if SQLITE_THREADSAFE
   if( threadsOverrideEachOthersLocks<0 ){
     testThreadLockingBehavior(fd);
   }
@@ -744,7 +741,7 @@ static const char *locktypeName(int locktype){
 ** If the unixFile is locked and an ownership is wrong, then return
 ** SQLITE_MISUSE.  SQLITE_OK is returned if everything works.
 */
-#ifdef SQLITE_UNIX_THREADS
+#if SQLITE_THREADSAFE
 static int transferOwnership(unixFile *pFile){
   int rc;
   pthread_t hSelf;
@@ -2717,7 +2714,6 @@ sqlite3_vfs sqlite3DefaultVfs = {
   0,                  /* nRef */
   0,                  /* vfsMutex */
   0,                  /* pNext */
-  0,                  /* pPrev */
   "unix",             /* zName */
   0,                  /* pAppData */
 
index 27ee0e508ca7e147bb7537715b7707d822c3fc6d..58aa07d801fc9d5bbe315c5018d617590149bacf 100644 (file)
@@ -15,7 +15,7 @@
 ** Random numbers are used by some of the database backends in order
 ** to generate random integer keys for tables or random filenames.
 **
-** $Id: random.c,v 1.17 2007/08/17 15:53:37 danielk1977 Exp $
+** $Id: random.c,v 1.18 2007/08/20 22:48:43 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -63,7 +63,7 @@ static int randomByte(void){
     char k[256];
     prng.j = 0;
     prng.i = 0;
-    sqlite3OsRandomness(sqlite3_find_vfs(0), 256, k);
+    sqlite3OsRandomness(sqlite3_vfs_find(0), 256, k);
     for(i=0; i<256; i++){
       prng.s[i] = i;
     }
index 55f2a50e6607955f4d7198ffb26920615a8d65e0..d70e0444e68ba011dd0f108b4c327ae124b3d834 100644 (file)
@@ -30,7 +30,7 @@
 ** the version number) and changes its name to "sqlite3.h" as
 ** part of the build process.
 **
-** @(#) $Id: sqlite.h.in,v 1.230 2007/08/18 10:59:21 danielk1977 Exp $
+** @(#) $Id: sqlite.h.in,v 1.231 2007/08/20 22:48:43 drh Exp $
 */
 #ifndef _SQLITE3_H_
 #define _SQLITE3_H_
@@ -548,7 +548,7 @@ typedef struct sqlite3_mutex sqlite3_mutex;
 ** Registered vfs modules are kept on a linked list formed by
 ** the pNext and pPrev pointers.  The [sqlite3_register_vfs()]
 ** and [sqlite3_unregister_vfs()] interfaces manage this list
-** in a thread-safe way.  The [sqlite3_find_vfs()] searches the
+** in a thread-safe way.  The [sqlite3_acquire_vfs()] searches the
 ** list.
 **
 ** The zName field holds the name of the VFS module.  The name must
@@ -636,7 +636,6 @@ struct sqlite3_vfs {
   int nRef;                /* Number of references to this structure */
   sqlite3_mutex *vfsMutex; /* A mutex for this VFS */
   sqlite3_vfs *pNext;      /* Next registered VFS */
-  sqlite3_vfs *pPrev;      /* Previous registered VFS */
   const char *zName;       /* Name of this virtual file system */
   void *pAppData;          /* Application context */
   int (*xOpen)(void *pAppData, const char *zName, sqlite3_file*,
@@ -859,6 +858,13 @@ int sqlite3_complete16(const void *sql);
 ** connection.  Setting a new busy handler clears any previous one.
 ** Note that calling [sqlite3_busy_timeout()] will also set or clear
 ** the busy handler.
+**
+** When operating in [sqlite3_enable_shared_cache | shared cache mode],
+** only a single busy handler can be defined for each database file.
+** So if two database connections share a single cache, then changing
+** the busy handler on one connection will also change the busy
+** handler in the other connection.  The busy handler is invoked
+** in the thread that was running when the SQLITE_BUSY was hit.
 */
 int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
 
@@ -3162,24 +3168,28 @@ int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset);
 ** New VFSes can be registered and existing VFSes can be unregistered.
 ** The following interfaces are provided.
 **
-** The sqlite3_find_vfs() interface returns a pointer to a VFS given its
+** The sqlite3_vfs_find() interface returns a pointer to a VFS given its
 ** name.  Names are case sensitive.  If there is no match, a NULL
 ** pointer is returned.  If zVfsName is NULL then the default 
-** VFS is returned.
+** VFS is returned.  If a valid VFS pointer is returned, its
+** vfsMutex field will have been initialized and nRef will be
+** greater than zero.  The sqlite3_vfs_release() function should
+** be used to release the VFS when it is no longer needed.
 **
-** New VFSes are registered with sqlite3_register_vfs().  Each
+** New VFSes are registered with sqlite3_vfs_register().  Each
 ** new VFS becomes the default VFS if the makeDflt flag is set.
 ** The same VFS can be registered multiple times without injury.
 ** To make an existing VFS into the default VFS, register it again
 ** with the makeDflt flag set.
 ** 
-** Unregister a VFS with the sqlite3_unregister_vfs() interface.
+** Unregister a VFS with the sqlite3_vfs_unregister() interface.
 ** If the default VFS is unregistered, another VFS is chosen as
 ** the default.  The choice for the new VFS is arbitrary.
 */
-sqlite3_vfs *sqlite3_find_vfs(const char *zVfsName);
-int sqlite3_register_vfs(sqlite3_vfs*, int makeDflt);
-int sqlite3_unregister_vfs(sqlite3_vfs*);
+sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName);
+int sqlite3_vfs_release(sqlite3_vfs*);
+int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt);
+int sqlite3_vfs_unregister(sqlite3_vfs*);
 
 /*
 ** CAPI3REF: Mutexes
@@ -3273,12 +3283,33 @@ int sqlite3_unregister_vfs(sqlite3_vfs*);
 ** previously entered by the same thread.  The behavior
 ** is undefined if the mutex is not currently entered or
 ** is not currently allocated.  SQLite will never do either.
+**
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines
+** are intended for use inside assert() statements.  They should
+** return true if the mutex in their argument is held or not held,
+** respectively, by the current thread. The implementation is
+** not required to provided working implementations of these
+** routines as their intended use is within assert() statements
+** only.  If the implementation does not provide working
+** versions of these routines, it must at least provide stubs
+** that always return true.
+**
+** If the argument to sqlite3_mutex_held() is a NULL pointer then
+** the routine should return 1.  This seems counter-intuitive since
+** clearly the mutex cannot be held if it does not exist.  But the
+** the reason the mutex does not exist is because the build is not
+** using mutexes.  And we do not want the assert() containing the
+** call to sqlite3_mutex_held() to fail, so a non-zero return is
+** the appropriate thing to do.  The sqlite3_mutex_notheld() 
+** interface should also return 1 when given a NULL pointer.
 */
 sqlite3_mutex *sqlite3_mutex_alloc(int);
 void sqlite3_mutex_free(sqlite3_mutex*);
 void sqlite3_mutex_enter(sqlite3_mutex*);
 int sqlite3_mutex_try(sqlite3_mutex*);
 void sqlite3_mutex_leave(sqlite3_mutex*);
+int sqlite3_mutex_held(sqlite3_mutex*);
+int sqlite3_mutex_notheld(sqlite3_mutex*);
 #define SQLITE_MUTEX_FAST             0
 #define SQLITE_MUTEX_RECURSIVE        1
 #define SQLITE_MUTEX_STATIC_MASTER    2
index a3899c572bcf3ba14e870f696f2ac294aa940213..63bb6a3d958bd4faa674dd5a30388d4e980f5497 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.592 2007/08/20 14:23:44 danielk1977 Exp $
+** @(#) $Id: sqliteInt.h,v 1.593 2007/08/20 22:48:43 drh Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
 # define NDEBUG 1
 #endif
 
+/*
+** The SQLITE_THREADSAFE macro must be defined as either 0 or 1.
+** Older versions of SQLite used an optional THREADSAFE macro.
+** We support that for legacy
+*/
+#if !defined(SQLITE_THREADSAFE)
+#if defined(THREADSAFE)
+# define SQLITE_THREADSAFE THREADSAFE
+#else
+# define SQLTIE_THREADSAFE 1
+#endif
+#endif
+
 /*
 ** These #defines should enable >2GB file support on Posix if the
 ** underlying operating system supports it.  If the OS lacks
@@ -399,7 +412,7 @@ struct sqlite3 {
   int magic;                    /* Magic number for detect library misuse */
   int nChange;                  /* Value returned by sqlite3_changes() */
   int nTotalChange;             /* Value returned by sqlite3_total_changes() */
-  sqlite3_mutex *pMutex;        /* Connection mutex */
+  sqlite3_mutex *mutex;         /* Connection mutex */
   struct sqlite3InitInfo {      /* Information used during initialization */
     int iDb;                    /* When back is being initialized */
     int newTnum;                /* Rootpage of table being initialized */
@@ -1818,10 +1831,17 @@ void sqlite3Parser(void*, int, Token, Parse*);
 #endif
 
 /*
-** FIX ME:  create these routines
+** The MallocDisallow() and MallocAllow() routines are like asserts.
+** Call them around a section of code that you do not expect to do
+** any memory allocation.
 */
-#define sqlite3MallocDisallow()
-#define sqlite3MallocAllow()
+#ifdef SQLITE_MEMDEBUG
+  void sqlite3MallocDisallow(void);
+  void sqlite3MallocAllow(void);
+#else
+# define sqlite3MallocDisallow()
+# define sqlite3MallocAllow()
+#endif
 
 
 #ifdef SQLITE_OMIT_VIRTUALTABLE
@@ -1850,7 +1870,6 @@ void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**);
 int sqlite3Reprepare(Vdbe*);
 void sqlite3ExprListCheckLength(Parse*, ExprList*, int, const char*);
 CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
-int sqlite3CrashFileOpen(sqlite3_vfs*, const char*, sqlite3_file*, int,int*);
 
 #if SQLITE_MAX_EXPR_DEPTH>0
   void sqlite3ExprSetHeight(Expr *);
index 012f22a71e41a3e6bae177b031fed50ce17f7b61..d46be4526eb5295201db2d247de7f835c01890c8 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test1.c,v 1.264 2007/08/17 15:53:37 danielk1977 Exp $
+** $Id: test1.c,v 1.265 2007/08/20 22:48:43 drh Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -4330,7 +4330,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
   extern int sqlite3_pager_writedb_count;
   extern int sqlite3_pager_writej_count;
   extern int sqlite3_pager_pgfree_count;
-#if OS_UNIX && defined(SQLITE_TEST) && defined(THREADSAFE) && THREADSAFE
+#if OS_UNIX && defined(SQLITE_TEST) && SQLITE_THREADSAFE
   extern int threadsOverrideEachOthersLocks;
 #endif
 #if OS_WIN
@@ -4380,7 +4380,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
   Tcl_LinkVar(interp, "unaligned_string_counter",
       (char*)&unaligned_string_counter, TCL_LINK_INT);
 #endif
-#if OS_UNIX && defined(SQLITE_TEST) && defined(THREADSAFE) && THREADSAFE
+#if OS_UNIX && defined(SQLITE_TEST) && SQLITE_THREADSAFE
   Tcl_LinkVar(interp, "threadsOverrideEachOthersLocks",
       (char*)&threadsOverrideEachOthersLocks, TCL_LINK_INT);
 #endif
index c819ee3ea0f0205cabd4c4b3f4724199e402c147..676b188baa2815737773c849122a95a599151fc7 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test2.c,v 1.48 2007/08/20 14:23:44 danielk1977 Exp $
+** $Id: test2.c,v 1.49 2007/08/20 22:48:43 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -78,7 +78,7 @@ static int pager_open(
     return TCL_ERROR;
   }
   if( Tcl_GetInt(interp, argv[2], &nPage) ) return TCL_ERROR;
-  rc = sqlite3PagerOpen(sqlite3_find_vfs(0), &pPager, argv[1], 0, 0);
+  rc = sqlite3PagerOpen(sqlite3_vfs_find(0), &pPager, argv[1], 0, 0);
   if( rc!=SQLITE_OK ){
     Tcl_AppendResult(interp, errorName(rc), 0);
     return TCL_ERROR;
@@ -540,7 +540,7 @@ static int fake_big_file(
   }
   if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR;
 
-  pVfs = sqlite3_find_vfs(0);
+  pVfs = sqlite3_vfs_find(0);
   rc = sqlite3OsOpenMalloc(pVfs, argv[2], &fd, 
       (SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB), 0
   );
index 520ad2a68308080d5bd284711efb913902ef5265..221db0fb401d6145f642240c9cbb20350330a224 100644 (file)
 *************************************************************************
 ** Code for testing the the SQLite library in a multithreaded environment.
 **
-** $Id: test4.c,v 1.18 2007/08/16 04:30:40 drh Exp $
+** $Id: test4.c,v 1.19 2007/08/20 22:48:43 drh Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
 #include "os.h"
-#if defined(OS_UNIX) && OS_UNIX==1 && defined(THREADSAFE) && THREADSAFE==1
+#if defined(OS_UNIX) && OS_UNIX==1 && SQLITE_THREADSAFE
 #include <stdlib.h>
 #include <string.h>
 #include <pthread.h>
index 66b0128468b902771d7bf5be5ddc211d6916eedd..df7418f09140f9fca39ea392841746d845692c06 100644 (file)
@@ -451,6 +451,14 @@ static const sqlite3_io_methods CrashFileVtab = {
   cfDeviceCharacteristics       /* xDeviceCharacteristics */
 };
 
+/*
+** Application data for the crash VFS
+*/
+struct crashAppData {
+  int (*xOpen)(void*,const char*,sqlite3_file*,int,int*); /* Original xOpen */
+  void *pAppData;                                      /* Original pAppData */
+};
+
 /*
 ** Open a crash-file file handle. The vfs pVfs is used to open
 ** the underlying real file.
@@ -461,7 +469,7 @@ static const sqlite3_io_methods CrashFileVtab = {
 ** sqlite3_malloc(). The assumption here is (pVfs->szOsFile) is
 ** equal or greater than sizeof(CrashFile).
 */
-int sqlite3CrashFileOpen(
+static int sqlite3CrashFileOpen(
   sqlite3_vfs *pVfs,
   const char *zName,
   sqlite3_file *pFile,
@@ -499,7 +507,8 @@ int sqlite3CrashFileOpen(
       sqlite3OsClose(pFile);
     }
   }else{
-    rc = pVfs->xOpen(pVfs->pAppData, zName, pFile, flags, pOutFlags);
+    struct crashAppData *pData = (struct crashAppData*)pVfs->pAppData;
+    rc = pData->xOpen(pData->pAppData, zName, pFile, flags, pOutFlags);
   }
   return rc;
 }
@@ -528,11 +537,26 @@ static int crashParamsObjCmd(
   int objc,
   Tcl_Obj *CONST objv[]
 ){
-  sqlite3_vfs *pVfs;
   int i;
   int iDelay;
   const char *zCrashFile;
   int nCrashFile;
+  static sqlite3_vfs crashVfs, *pOriginalVfs;
+  static struct crashAppData appData;
+
+  if( pOriginalVfs==0 ){
+    pOriginalVfs = sqlite3_vfs_find(0);
+    crashVfs = *pOriginalVfs;
+    crashVfs.xOpen = sqlite3CrashFileOpen;
+    crashVfs.vfsMutex = 0;
+    crashVfs.nRef = 0;
+    crashVfs.pAppData = &appData;
+    appData.xOpen = pOriginalVfs->xOpen;
+    appData.pAppData = pOriginalVfs->pAppData;
+    sqlite3_vfs_release(pOriginalVfs);
+    sqlite3_vfs_unregister(pOriginalVfs);
+    sqlite3_vfs_register(&crashVfs, 1);
+  }
 
   int iDc = 0;
   int iSectorSize = 0;
index 17c2a49846654ba28b38ccf6f37b38136dd681a9..f3411f90a7f1f2e09085e2bae8ddaf3301ed4b6b 100644 (file)
 ** Code for testing the client/server version of the SQLite library.
 ** Derived from test4.c.
 **
-** $Id: test7.c,v 1.5 2007/08/16 04:30:40 drh Exp $
+** $Id: test7.c,v 1.6 2007/08/20 22:48:43 drh Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
 #include "os.h"
 
 /*
-** This test only works on UNIX with a THREADSAFE build that includes
+** This test only works on UNIX with a SQLITE_THREADSAFE build that includes
 ** the SQLITE_SERVER option.
 */
-#if OS_UNIX && defined(THREADSAFE) && THREADSAFE==1 && \
+#if OS_UNIX && SQLITE_THREADSAFE && \
     defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE)
 
 #include <stdlib.h>
index 9f38f2c7c49fd4946e12d1d1f19ac7e2b061c1aa..ead84429711097e47b820f6478a0d5182ef5761b 100644 (file)
 #include "os.h"
 #include <tcl.h>
 
-/* If the THREADSAFE macro is not set, assume that it is turned off. */
-#ifndef THREADSAFE
-# define THREADSAFE 0
-#endif
-
 /*
 ** This test uses pthreads and hence only works on unix and with
 ** a threadsafe build of SQLite.  It also requires that the redefinable
@@ -86,7 +81,7 @@
 ** default.  If a required element is missing, almost all of the code
 ** in this file is commented out.
 */
-#if OS_UNIX && THREADSAFE && defined(SQLITE_ENABLE_REDEF_IO)
+#if OS_UNIX && SQLITE_THREADSAFE && defined(SQLITE_ENABLE_REDEF_IO)
 
 /*
 ** This demo uses pthreads.  If you do not have a pthreads implementation
@@ -1249,7 +1244,7 @@ static int testAsyncWait(
 }
 
 
-#endif  /* OS_UNIX and THREADSAFE and defined(SQLITE_ENABLE_REDEF_IO) */
+#endif  /* OS_UNIX and SQLITE_THREADSAFE and defined(SQLITE_ENABLE_REDEF_IO) */
 
 /*
 ** This routine registers the custom TCL commands defined in this
@@ -1257,7 +1252,7 @@ static int testAsyncWait(
 ** of this module.
 */
 int Sqlitetestasync_Init(Tcl_Interp *interp){
-#if OS_UNIX && THREADSAFE && defined(SQLITE_ENABLE_REDEF_IO)
+#if OS_UNIX && SQLITE_THREADSAFE && defined(SQLITE_ENABLE_REDEF_IO)
   Tcl_CreateObjCommand(interp,"sqlite3async_enable",testAsyncEnable,0,0);
   Tcl_CreateObjCommand(interp,"sqlite3async_halt",testAsyncHalt,0,0);
   Tcl_CreateObjCommand(interp,"sqlite3async_delay",testAsyncDelay,0,0);
@@ -1265,6 +1260,6 @@ int Sqlitetestasync_Init(Tcl_Interp *interp){
   Tcl_CreateObjCommand(interp,"sqlite3async_wait",testAsyncWait,0,0);
   Tcl_LinkVar(interp, "sqlite3async_trace",
       (char*)&sqlite3async_trace, TCL_LINK_INT);
-#endif  /* OS_UNIX and THREADSAFE and defined(SQLITE_ENABLE_REDEF_IO) */
+#endif  /* OS_UNIX and SQLITE_THREADSAFE and defined(SQLITE_ENABLE_REDEF_IO) */
   return TCL_OK;
 }
index 78ec7af4045f7c8a21b799ef3aa5ab7f880c09a7..e839910d23428680ed841d9e12074b37b2766396 100644 (file)
@@ -16,7 +16,7 @@
 ** The focus of this file is providing the TCL testing layer
 ** access to compile-time constants.
 **
-** $Id: test_config.c,v 1.8 2007/08/20 17:37:48 shess Exp $
+** $Id: test_config.c,v 1.9 2007/08/20 22:48:43 drh Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -197,12 +197,6 @@ static void set_options(Tcl_Interp *interp){
   Tcl_SetVar2(interp, "sqlite_options", "fts2", "0", TCL_GLOBAL_ONLY);
 #endif
 
-#ifdef SQLITE_ENABLE_FTS3
-  Tcl_SetVar2(interp, "sqlite_options", "fts3", "1", TCL_GLOBAL_ONLY);
-#else
-  Tcl_SetVar2(interp, "sqlite_options", "fts3", "0", TCL_GLOBAL_ONLY);
-#endif
-
 #ifdef SQLITE_OMIT_GLOBALRECOVER
   Tcl_SetVar2(interp, "sqlite_options", "globalrecover", "0", TCL_GLOBAL_ONLY);
 #else
@@ -334,7 +328,7 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
   Tcl_SetVar2(interp, "sqlite_options", "tclvar", "1", TCL_GLOBAL_ONLY);
 #endif
 
-#if defined(THREADSAFE) && THREADSAFE
+#if SQLITE_THREADSAFE
   Tcl_SetVar2(interp, "sqlite_options", "threadsafe", "1", TCL_GLOBAL_ONLY);
 #else
   Tcl_SetVar2(interp, "sqlite_options", "threadsafe", "0", TCL_GLOBAL_ONLY);
index db213cf673fed8bee8c5139089187a4105f04d9e..48cd122b8ccf46c15cd98b4140854e5c51af2ece 100644 (file)
 */
 
 /*
-** Only compile the code in this file on UNIX with a THREADSAFE build
+** Only compile the code in this file on UNIX with a SQLITE_THREADSAFE build
 ** and only if the SQLITE_SERVER macro is defined.
 */
 #if defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE)
-#if defined(OS_UNIX) && OS_UNIX && defined(THREADSAFE) && THREADSAFE
+#if defined(OS_UNIX) && OS_UNIX && SQLITE_THREADSAFE
 
 /*
 ** We require only pthreads and the public interface of SQLite.
@@ -483,5 +483,5 @@ void sqlite3_server_stop(void){
   pthread_mutex_unlock(&g.serverMutex);
 }
 
-#endif /* defined(OS_UNIX) && OS_UNIX && defined(THREADSAFE) && THREADSAFE */
+#endif /* defined(OS_UNIX) && OS_UNIX && SQLITE_THREADSAFE */
 #endif /* defined(SQLITE_SERVER) */