]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Always set BtShared.db when entering the BtShared mutex. Ticket #3793. (CVS 6480)
authordanielk1977 <danielk1977@noemail.net>
Fri, 10 Apr 2009 09:47:06 +0000 (09:47 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Fri, 10 Apr 2009 09:47:06 +0000 (09:47 +0000)
FossilOrigin-Name: ed6620ba589ddbb6ca86f42a7652e3b019195647

manifest
manifest.uuid
src/btmutex.c
src/btree.c
test/tkt3793.test [new file with mode: 0644]

index 75150435e8dd825e28b7ce22ae702bd0e12eaaa1..ac6831495fd8d2b7661dc598c59482b091a4640b 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Force\s8-byte\salignment\sof\ssqlite3_value\sobjects\sin\sthe\nsqlite3VdbeUnpackRecord()\sprimitive.\s\sTicket\s#3777.\s(CVS\s6479)
-D 2009-04-10T00:56:28
+C Always\sset\sBtShared.db\swhen\sentering\sthe\sBtShared\smutex.\sTicket\s#3793.\s(CVS\s6480)
+D 2009-04-10T09:47:07
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in 583e87706abc3026960ed759aff6371faf84c211
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -102,8 +102,8 @@ F src/attach.c af80fa85d391ad302c148c4e2524a2cebec64cb2
 F src/auth.c c8b2ab5c8bad4bd90ed7c294694f48269162c627
 F src/backup.c 0082d0e5a63f04e88faee0dff0a7d63d3e92a78d
 F src/bitvec.c ef370407e03440b0852d05024fb016b14a471d3d
-F src/btmutex.c 341502bc496dc0840dcb00cde65680fb0e85c3ab
-F src/btree.c 02c902db5527fc20b74a9ffdf1fc296ee3437964
+F src/btmutex.c 527b63e275d132564710dec47ddab52a0f2d7f4e
+F src/btree.c 8331febf3769cdac2e0cde463be4ed901406b783
 F src/btree.h 8007018c1753944790c39610280894ab280210b8
 F src/btreeInt.h df64030d632f8c8ac217ed52e8b6b3eacacb33a5
 F src/build.c 2882f22078db1c3f887b1aca77ff460cf9461c62
@@ -637,6 +637,7 @@ F test/tkt3761.test b95ea9c98f21cf91325f18a984887e62caceab33
 F test/tkt3762.test 2a9f3b03df44ec49ec0cfa8d5da6574c2a7853df
 F test/tkt3773.test 430b06567ce40285dfd2c4834a2a61816403efeb
 F test/tkt3791.test a6624b9a80b216a26cf473607f42f3e51898c267
+F test/tkt3793.test 3aa2efe55bc31fc9459618feea2016ea9a52b2af
 F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
 F test/trace.test 19ffbc09885c3321d56358a5738feae8587fb377
 F test/trans.test 8b79967a7e085289ec64890c6fdf9d089e1b4a5f
@@ -716,7 +717,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
-P 9a09a47495d498a3372ead0eef5e3642a3ff30c2
-R 5607ab4efa85fc4682dba26b62f8935a
-U drh
-Z d8359058aa4314f29eca77f3a23b5b1c
+P 2cc68272b1f70701268075cfa82fa64bb2a8179d
+R 0f5ed1cbc4ec520cfd83afb78d3917f3
+U danielk1977
+Z 46ae6a8835e1cba4786ac026de3d9fbb
index 3a72b4506d8fe2dd87d239695cca2143da9f6319..154252be0ef5c9262601205107f8433501e3cf83 100644 (file)
@@ -1 +1 @@
-2cc68272b1f70701268075cfa82fa64bb2a8179d
\ No newline at end of file
+ed6620ba589ddbb6ca86f42a7652e3b019195647
\ No newline at end of file
index 4d2f767995b942c3fcaf7561c3f42b5fcb0e05c8..06cbed8d9cc393515636cd456cbe948a52e610b2 100644 (file)
@@ -10,7 +10,7 @@
 **
 *************************************************************************
 **
-** $Id: btmutex.c,v 1.13 2009/03/05 04:20:32 shane Exp $
+** $Id: btmutex.c,v 1.14 2009/04/10 09:47:07 danielk1977 Exp $
 **
 ** This file contains code used to implement mutexes on Btree objects.
 ** This code really belongs in btree.c.  But btree.c is getting too
 #include "btreeInt.h"
 #if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
 
+/*
+** Obtain the BtShared mutex associated with B-Tree handle p. Also,
+** set BtShared.db to the database handle associated with p and the
+** p->locked boolean to true.
+*/
+static void lockBtreeMutex(Btree *p){
+  assert( p->locked==0 );
+  assert( sqlite3_mutex_notheld(p->pBt->mutex) );
+  assert( sqlite3_mutex_held(p->db->mutex) );
+
+  sqlite3_mutex_enter(p->pBt->mutex);
+  p->pBt->db = p->db;
+  p->locked = 1;
+}
+
+/*
+** Release the BtShared mutex associated with B-Tree handle p and
+** clear the p->locked boolean.
+*/
+static void unlockBtreeMutex(Btree *p){
+  assert( p->locked==1 );
+  assert( sqlite3_mutex_held(p->pBt->mutex) );
+  assert( sqlite3_mutex_held(p->db->mutex) );
+  assert( p->db==p->pBt->db );
+
+  sqlite3_mutex_leave(p->pBt->mutex);
+  p->locked = 0;
+}
 
 /*
 ** Enter a mutex on the given BTree object.
@@ -57,6 +85,10 @@ void sqlite3BtreeEnter(Btree *p){
   /* We should already hold a lock on the database connection */
   assert( sqlite3_mutex_held(p->db->mutex) );
 
+  /* Unless the database is sharable and unlocked, then BtShared.db
+  ** should already be set correctly. */
+  assert( (p->locked==0 && p->sharable) || p->pBt->db==p->db );
+
   if( !p->sharable ) return;
   p->wantToLock++;
   if( p->locked ) return;
@@ -66,6 +98,7 @@ void sqlite3BtreeEnter(Btree *p){
   ** procedure that follows.  Just be sure not to block.
   */
   if( sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){
+    p->pBt->db = p->db;
     p->locked = 1;
     return;
   }
@@ -80,16 +113,13 @@ void sqlite3BtreeEnter(Btree *p){
     assert( pLater->pNext==0 || pLater->pNext->pBt>pLater->pBt );
     assert( !pLater->locked || pLater->wantToLock>0 );
     if( pLater->locked ){
-      sqlite3_mutex_leave(pLater->pBt->mutex);
-      pLater->locked = 0;
+      unlockBtreeMutex(pLater);
     }
   }
-  sqlite3_mutex_enter(p->pBt->mutex);
-  p->locked = 1;
+  lockBtreeMutex(p);
   for(pLater=p->pNext; pLater; pLater=pLater->pNext){
     if( pLater->wantToLock ){
-      sqlite3_mutex_enter(pLater->pBt->mutex);
-      pLater->locked = 1;
+      lockBtreeMutex(pLater);
     }
   }
 }
@@ -102,25 +132,25 @@ void sqlite3BtreeLeave(Btree *p){
     assert( p->wantToLock>0 );
     p->wantToLock--;
     if( p->wantToLock==0 ){
-      assert( p->locked );
-      sqlite3_mutex_leave(p->pBt->mutex);
-      p->locked = 0;
+      unlockBtreeMutex(p);
     }
   }
 }
 
 #ifndef NDEBUG
 /*
-** Return true if the BtShared mutex is held on the btree.  
-**
-** This routine makes no determination one way or another if the
-** database connection mutex is held.
+** Return true if the BtShared mutex is held on the btree, or if the
+** B-Tree is not marked as sharable.
 **
 ** This routine is used only from within assert() statements.
 */
 int sqlite3BtreeHoldsMutex(Btree *p){
-  return (p->sharable==0 ||
-             (p->locked && p->wantToLock && sqlite3_mutex_held(p->pBt->mutex)));
+  assert( p->sharable==0 || p->locked==0 || p->wantToLock>0 );
+  assert( p->sharable==0 || p->locked==0 || p->db==p->pBt->db );
+  assert( p->sharable==0 || p->locked==0 || sqlite3_mutex_held(p->pBt->mutex) );
+  assert( p->sharable==0 || p->locked==0 || sqlite3_mutex_held(p->db->mutex) );
+
+  return (p->sharable==0 || p->locked);
 }
 #endif
 
@@ -160,6 +190,7 @@ void sqlite3BtreeEnterAll(sqlite3 *db){
   assert( sqlite3_mutex_held(db->mutex) );
   for(i=0; i<db->nDb; i++){
     p = db->aDb[i].pBt;
+    assert( !p || (p->locked==0 && p->sharable) || p->pBt->db==p->db );
     if( p && p->sharable ){
       p->wantToLock++;
       if( !p->locked ){
@@ -168,13 +199,11 @@ void sqlite3BtreeEnterAll(sqlite3 *db){
         while( p->locked && p->pNext ) p = p->pNext;
         for(pLater = p->pNext; pLater; pLater=pLater->pNext){
           if( pLater->locked ){
-            sqlite3_mutex_leave(pLater->pBt->mutex);
-            pLater->locked = 0;
+            unlockBtreeMutex(pLater);
           }
         }
         while( p ){
-          sqlite3_mutex_enter(p->pBt->mutex);
-          p->locked++;
+          lockBtreeMutex(p);
           p = p->pNext;
         }
       }
@@ -191,9 +220,7 @@ void sqlite3BtreeLeaveAll(sqlite3 *db){
       assert( p->wantToLock>0 );
       p->wantToLock--;
       if( p->wantToLock==0 ){
-        assert( p->locked );
-        sqlite3_mutex_leave(p->pBt->mutex);
-        p->locked = 0;
+        unlockBtreeMutex(p);
       }
     }
   }
@@ -282,8 +309,7 @@ void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pArray){
 
     p->wantToLock++;
     if( !p->locked && p->sharable ){
-      sqlite3_mutex_enter(p->pBt->mutex);
-      p->locked = 1;
+      lockBtreeMutex(p);
     }
   }
 }
@@ -305,8 +331,7 @@ void sqlite3BtreeMutexArrayLeave(BtreeMutexArray *pArray){
 
     p->wantToLock--;
     if( p->wantToLock==0 && p->locked ){
-      sqlite3_mutex_leave(p->pBt->mutex);
-      p->locked = 0;
+      unlockBtreeMutex(p);
     }
   }
 }
index 61fed3dca3b64573e04bfb1fddb6101c8df23cf1..e5c25f80904b3085c0f6102d39a5e0557c3c8cf2 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.593 2009/04/10 00:56:28 drh Exp $
+** $Id: btree.c,v 1.594 2009/04/10 09:47:07 danielk1977 Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** See the header comment on "btreeInt.h" for additional information.
@@ -1470,6 +1470,7 @@ int sqlite3BtreeOpen(
     if( rc!=SQLITE_OK ){
       goto btree_open_out;
     }
+    pBt->db = db;
     sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt);
     p->pBt = pBt;
   
@@ -1647,7 +1648,6 @@ int sqlite3BtreeClose(Btree *p){
   /* Close all cursors opened via this handle.  */
   assert( sqlite3_mutex_held(p->db->mutex) );
   sqlite3BtreeEnter(p);
-  pBt->db = p->db;
   pCur = pBt->pCursor;
   while( pCur ){
     BtCursor *pTmp = pCur;
@@ -2126,7 +2126,6 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
   int rc = SQLITE_OK;
 
   sqlite3BtreeEnter(p);
-  pBt->db = p->db;
   btreeIntegrity(p);
 
   /* If the btree is already in a write-transaction, or it
@@ -2545,7 +2544,6 @@ int sqlite3BtreeIncrVacuum(Btree *p){
   BtShared *pBt = p->pBt;
 
   sqlite3BtreeEnter(p);
-  pBt->db = p->db;
   assert( pBt->inTransaction==TRANS_WRITE && p->inTrans==TRANS_WRITE );
   if( !pBt->autoVacuum ){
     rc = SQLITE_DONE;
@@ -2652,7 +2650,6 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
   if( p->inTrans==TRANS_WRITE ){
     BtShared *pBt = p->pBt;
     sqlite3BtreeEnter(p);
-    pBt->db = p->db;
 #ifndef SQLITE_OMIT_AUTOVACUUM
     if( pBt->autoVacuum ){
       rc = autoVacuumCommit(pBt);
@@ -2686,7 +2683,6 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
   BtShared *pBt = p->pBt;
 
   sqlite3BtreeEnter(p);
-  pBt->db = p->db;
   btreeIntegrity(p);
 
   /* If the handle has a write-transaction open, commit the shared-btrees 
@@ -2812,7 +2808,6 @@ int sqlite3BtreeRollback(Btree *p){
   MemPage *pPage1;
 
   sqlite3BtreeEnter(p);
-  pBt->db = p->db;
   rc = saveAllCursors(pBt, 0, 0);
 #ifndef SQLITE_OMIT_SHARED_CACHE
   if( rc!=SQLITE_OK ){
@@ -2887,7 +2882,6 @@ int sqlite3BtreeBeginStmt(Btree *p, int iStatement){
   int rc;
   BtShared *pBt = p->pBt;
   sqlite3BtreeEnter(p);
-  pBt->db = p->db;
   assert( p->inTrans==TRANS_WRITE );
   assert( pBt->readOnly==0 );
   assert( iStatement>0 );
@@ -2926,7 +2920,6 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
     assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );
     assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) );
     sqlite3BtreeEnter(p);
-    pBt->db = p->db;
     rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint);
     if( rc==SQLITE_OK ){
       rc = newDatabase(pBt);
@@ -3043,7 +3036,6 @@ int sqlite3BtreeCursor(
 ){
   int rc;
   sqlite3BtreeEnter(p);
-  p->pBt->db = p->db;
   rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
   sqlite3BtreeLeave(p);
   return rc;
@@ -3101,7 +3093,6 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
     int i;
     BtShared *pBt = pCur->pBt;
     sqlite3BtreeEnter(pBtree);
-    pBt->db = pBtree->db;
     sqlite3BtreeClearCursor(pCur);
     if( pCur->pPrev ){
       pCur->pPrev->pNext = pCur->pNext;
@@ -6517,7 +6508,6 @@ static int btreeCreateTable(Btree *p, int *piTable, int flags){
 int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
   int rc;
   sqlite3BtreeEnter(p);
-  p->pBt->db = p->db;
   rc = btreeCreateTable(p, piTable, flags);
   sqlite3BtreeLeave(p);
   return rc;
@@ -6589,7 +6579,6 @@ int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
   int rc;
   BtShared *pBt = p->pBt;
   sqlite3BtreeEnter(p);
-  pBt->db = p->db;
   assert( p->inTrans==TRANS_WRITE );
   if( (rc = checkForReadConflicts(p, iTable, 0, 1))!=SQLITE_OK ){
     /* nothing to do */
@@ -6731,7 +6720,6 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
 int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
   int rc;
   sqlite3BtreeEnter(p);
-  p->pBt->db = p->db;
   rc = btreeDropTable(p, iTable, piMoved);
   sqlite3BtreeLeave(p);
   return rc;
@@ -6755,7 +6743,6 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
   BtShared *pBt = p->pBt;
 
   sqlite3BtreeEnter(p);
-  pBt->db = p->db;
 
   /* 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
@@ -6826,7 +6813,6 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
   int rc;
   assert( idx>=1 && idx<=15 );
   sqlite3BtreeEnter(p);
-  pBt->db = p->db;
   assert( p->inTrans==TRANS_WRITE );
   assert( pBt->pPage1!=0 );
   pP1 = pBt->pPage1->aData;
@@ -7300,7 +7286,6 @@ char *sqlite3BtreeIntegrityCheck(
   char zErr[100];
 
   sqlite3BtreeEnter(p);
-  pBt->db = p->db;
   nRef = sqlite3PagerRefcount(pBt->pPager);
   if( lockBtreeWithRetry(p)!=SQLITE_OK ){
     *pnErr = 1;
diff --git a/test/tkt3793.test b/test/tkt3793.test
new file mode 100644 (file)
index 0000000..5cba0a1
--- /dev/null
@@ -0,0 +1,109 @@
+# 2009 April 10
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.
+#
+# This file implements tests to verify that ticket #3793 has been
+# fixed.  
+#
+# $Id: tkt3793.test,v 1.1 2009/04/10 09:47:07 danielk1977 Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !shared_cache||!attach {
+  finish_test
+  return
+}
+set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+
+do_test tkt3793-1.1 {
+  sqlite3 db1 test.db
+  sqlite3 db2 test.db
+  execsql {
+    BEGIN;
+    CREATE TABLE t1(a, b);
+    CREATE TABLE t2(a PRIMARY KEY, b);
+    INSERT INTO t1 VALUES(randstr(50,50), randstr(50,50));
+    INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1;
+    INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1;
+    INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1;
+    INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1;
+    INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1;
+    INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1;
+    INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1;
+    INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1;
+    INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1;
+    INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1;
+    INSERT INTO t2 SELECT * FROM t1;
+    COMMIT;
+  }
+} {}
+
+proc busyhandler {db args} { set ::busyconnection $db ; return 1 }
+db2 busy {busyhandler db2}
+db1 busy {busyhandler db1}
+
+# Establish a read-lock on the database file using connection [db].
+#
+do_test tkt3793-1.2 {
+  execsql {
+    BEGIN;
+    SELECT count(*) FROM t1;
+  }
+} {1024}
+
+# Set the size of the cache shared by [db1] and [db2] to 10. Then update
+# more than 10 pages of table t1. At this point the shared-cache will
+# hold a RESERVED lock on the database file. Even though there are now
+# more than 10 dirty pages in memory, it cannot upgrade to an EXCLUSIVE 
+# lock because of the read-lock held by [db].
+#
+do_test tkt3793-1.3 {
+  execsql {
+    PRAGMA cache_size = 10;
+    BEGIN;
+    UPDATE t1 SET b = randstr(50,50);
+  } db1
+} {}
+
+set x 0
+
+# Run one SELECT query on the shared-cache using [db1], then from within 
+# the callback run another via [db2]. Because of the large number of dirty
+# pages within the cache, each time a new page is read from the database
+# SQLite will attempt to upgrade to an EXCLUSIVE lock, and hence invoke
+# the busy-handler. The tests here verify that the correct busy-handler
+# function is invoked (the busy-handler associated with the database
+# connection that called sqlite3_step()). When bug #3793 existed, sometimes
+# the [db2] busy-handler was invoked from within the call to sqlite3_step()
+# associated with [db1]. 
+#
+# Note: Before the bug was fixed, if [db2] was opened with the "-fullmutex 1"
+# option, then this test case would cause an assert() to fail.
+#
+set ::busyconnection db1
+db1 eval {SELECT * FROM t2 ORDER BY a LIMIT 20} {
+  do_test tkt3793-2.[incr x] { set ::busyconnection } db1
+  set ::busyconnection db2
+
+  db2 eval { SELECT count(*) FROM t2 }
+  do_test tkt3793-2.[incr x] { set ::busyconnection } db2
+  set ::busyconnection db1
+}
+
+do_test tkt3793-3 {
+  db1 close
+  db2 close
+} {}
+
+sqlite3_enable_shared_cache $::enable_shared_cache
+finish_test