/ Check-in [644c6398]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Be more aggressive about using the busy handler. Ticket #1159. (CVS 2385)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 644c6398e52481e5dda87671e1c196b26b1e4990
User & Date: drh 2005-03-14 02:01:50
Context
2005-03-15
02:04
Allow the database name in a DETACH statement to be quoted. Ticket #1151. (CVS 2386) check-in: 24e88773 user: drh tags: trunk
2005-03-14
02:01
Be more aggressive about using the busy handler. Ticket #1159. (CVS 2385) check-in: 644c6398 user: drh tags: trunk
2005-03-12
18:03
Fix typo in documentation. (CVS 2384) check-in: 78012246 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/btree.c.

     5      5   ** a legal notice, here is a blessing:
     6      6   **
     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12         -** $Id: btree.c,v 1.251 2005/03/10 17:06:34 drh Exp $
           12  +** $Id: btree.c,v 1.252 2005/03/14 02:01:50 drh Exp $
    13     13   **
    14     14   ** This file implements a external (disk-based) database using BTrees.
    15     15   ** For a detailed discussion of BTrees, refer to
    16     16   **
    17     17   **     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
    18     18   **     "Sorting And Searching", pages 473-480. Addison-Wesley
    19     19   **     Publishing Company, Reading, Massachusetts.
................................................................................
   310    310     u16 pageSize;         /* Total number of bytes on a page */
   311    311     u16 psAligned;        /* pageSize rounded up to a multiple of 8 */
   312    312     u16 usableSize;       /* Number of usable bytes on each page */
   313    313     int maxLocal;         /* Maximum local payload in non-LEAFDATA tables */
   314    314     int minLocal;         /* Minimum local payload in non-LEAFDATA tables */
   315    315     int maxLeaf;          /* Maximum local payload in a LEAFDATA table */
   316    316     int minLeaf;          /* Minimum local payload in a LEAFDATA table */
          317  +  BusyHandler *pBusyHandler;   /* Callback for when there is lock contention */
   317    318   };
   318    319   typedef Btree Bt;
   319    320   
   320    321   /*
   321    322   ** Btree.inTrans may take one of the following values.
   322    323   */
   323    324   #define TRANS_NONE  0
................................................................................
  1287   1288     return SQLITE_OK;
  1288   1289   }
  1289   1290   
  1290   1291   /*
  1291   1292   ** Change the busy handler callback function.
  1292   1293   */
  1293   1294   int sqlite3BtreeSetBusyHandler(Btree *pBt, BusyHandler *pHandler){
         1295  +  pBt->pBusyHandler = pHandler;
  1294   1296     sqlite3pager_set_busyhandler(pBt->pPager, pHandler);
  1295   1297     return SQLITE_OK;
  1296   1298   }
  1297   1299   
  1298   1300   /*
  1299   1301   ** Change the limit on the number of pages allowed in the cache.
  1300   1302   **
................................................................................
  1475   1477   
  1476   1478   page1_init_failed:
  1477   1479     releasePage(pPage1);
  1478   1480     pBt->pPage1 = 0;
  1479   1481     return rc;
  1480   1482   }
  1481   1483   
         1484  +/*
         1485  +** This routine works like lockBtree() except that it also invokes the
         1486  +** busy callback if there is lock contention.
         1487  +*/
         1488  +static int lockBtreeWithRetry(Btree *pBt){
         1489  +  int rc = SQLITE_OK;
         1490  +  if( pBt->inTrans==TRANS_NONE ){
         1491  +    rc = sqlite3BtreeBeginTrans(pBt, 0);
         1492  +    pBt->inTrans = TRANS_NONE;
         1493  +  }
         1494  +  return rc;
         1495  +}
         1496  +       
         1497  +
  1482   1498   /*
  1483   1499   ** If there are no outstanding cursors and we are not in the middle
  1484   1500   ** of a transaction but there is a read lock on the database, then
  1485   1501   ** this routine unrefs the first page of the database file which 
  1486   1502   ** has the effect of releasing the read lock.
  1487   1503   **
  1488   1504   ** If there are any outstanding cursors, this routine is a no-op.
................................................................................
  1539   1555   
  1540   1556   /*
  1541   1557   ** Attempt to start a new transaction. A write-transaction
  1542   1558   ** is started if the second argument is nonzero, otherwise a read-
  1543   1559   ** transaction.  If the second argument is 2 or more and exclusive
  1544   1560   ** transaction is started, meaning that no other process is allowed
  1545   1561   ** to access the database.  A preexisting transaction may not be
  1546         -** upgrade to exclusive by calling this routine a second time - the
         1562  +** upgraded to exclusive by calling this routine a second time - the
  1547   1563   ** exclusivity flag only works for a new transaction.
  1548   1564   **
  1549   1565   ** A write-transaction must be started before attempting any 
  1550   1566   ** changes to the database.  None of the following routines 
  1551   1567   ** will work unless a transaction is started first:
  1552   1568   **
  1553   1569   **      sqlite3BtreeCreateTable()
................................................................................
  1554   1570   **      sqlite3BtreeCreateIndex()
  1555   1571   **      sqlite3BtreeClearTable()
  1556   1572   **      sqlite3BtreeDropTable()
  1557   1573   **      sqlite3BtreeInsert()
  1558   1574   **      sqlite3BtreeDelete()
  1559   1575   **      sqlite3BtreeUpdateMeta()
  1560   1576   **
  1561         -** If wrflag is true, then nMaster specifies the maximum length of
  1562         -** a master journal file name supplied later via sqlite3BtreeSync().
  1563         -** This is so that appropriate space can be allocated in the journal file
  1564         -** when it is created..
         1577  +** If an initial attempt to acquire the lock fails because of lock contention
         1578  +** and the database was previously unlocked, then invoke the busy handler
         1579  +** if there is one.  But if there was previously a read-lock, do not
         1580  +** invoke the busy handler - just return SQLITE_BUSY.  SQLITE_BUSY is 
         1581  +** returned when there is already a read-lock in order to avoid a deadlock.
         1582  +**
         1583  +** Suppose there are two processes A and B.  A has a read lock and B has
         1584  +** a reserved lock.  B tries to promote to exclusive but is blocked because
         1585  +** of A's read lock.  A tries to promote to reserved but is blocked by B.
         1586  +** One or the other of the two processes must give way or there can be
         1587  +** no progress.  By returning SQLITE_BUSY and not invoking the busy callback
         1588  +** when A already has a read lock, we encourage A to give up and let B
         1589  +** proceed.
  1565   1590   */
  1566   1591   int sqlite3BtreeBeginTrans(Btree *pBt, int wrflag){
  1567   1592     int rc = SQLITE_OK;
         1593  +  int busy = 0;
         1594  +  BusyHandler *pH;
  1568   1595   
  1569   1596     /* If the btree is already in a write-transaction, or it
  1570   1597     ** is already in a read-transaction and a read-transaction
  1571   1598     ** is requested, this is a no-op.
  1572   1599     */
  1573         -  if( pBt->inTrans==TRANS_WRITE || 
  1574         -      (pBt->inTrans==TRANS_READ && !wrflag) ){
         1600  +  if( pBt->inTrans==TRANS_WRITE || (pBt->inTrans==TRANS_READ && !wrflag) ){
  1575   1601       return SQLITE_OK;
  1576   1602     }
         1603  +
         1604  +  /* Write transactions are not possible on a read-only database */
  1577   1605     if( pBt->readOnly && wrflag ){
  1578   1606       return SQLITE_READONLY;
  1579   1607     }
  1580   1608   
  1581         -  if( pBt->pPage1==0 ){
  1582         -    rc = lockBtree(pBt);
  1583         -  }
  1584         -
  1585         -  if( rc==SQLITE_OK && wrflag ){
  1586         -    rc = sqlite3pager_begin(pBt->pPage1->aData, wrflag>1);
         1609  +  do {
         1610  +    if( pBt->pPage1==0 ){
         1611  +      rc = lockBtree(pBt);
         1612  +    }
         1613  +  
         1614  +    if( rc==SQLITE_OK && wrflag ){
         1615  +      rc = sqlite3pager_begin(pBt->pPage1->aData, wrflag>1);
         1616  +      if( rc==SQLITE_OK ){
         1617  +        rc = newDatabase(pBt);
         1618  +      }
         1619  +    }
         1620  +  
  1587   1621       if( rc==SQLITE_OK ){
  1588         -      rc = newDatabase(pBt);
         1622  +      pBt->inTrans = (wrflag?TRANS_WRITE:TRANS_READ);
         1623  +      if( wrflag ) pBt->inStmt = 0;
         1624  +    }else{
         1625  +      unlockBtreeIfUnused(pBt);
  1589   1626       }
  1590         -  }
  1591         -
  1592         -  if( rc==SQLITE_OK ){
  1593         -    pBt->inTrans = (wrflag?TRANS_WRITE:TRANS_READ);
  1594         -    if( wrflag ) pBt->inStmt = 0;
  1595         -  }else{
  1596         -    unlockBtreeIfUnused(pBt);
  1597         -  }
         1627  +  }while( rc==SQLITE_BUSY && pBt->inTrans==TRANS_NONE &&
         1628  +      (pH = pBt->pBusyHandler)!=0 && 
         1629  +      pH->xFunc && pH->xFunc(pH->pArg, busy++)
         1630  +  );
  1598   1631     return rc;
  1599   1632   }
  1600   1633   
  1601   1634   #ifndef SQLITE_OMIT_AUTOVACUUM
  1602   1635   
  1603   1636   /*
  1604   1637   ** Set the pointer-map entries for all children of page pPage. Also, if
................................................................................
  2112   2145         return SQLITE_READONLY;
  2113   2146       }
  2114   2147       if( checkReadLocks(pBt, iTable, 0) ){
  2115   2148         return SQLITE_LOCKED;
  2116   2149       }
  2117   2150     }
  2118   2151     if( pBt->pPage1==0 ){
  2119         -    rc = lockBtree(pBt);
         2152  +    rc = lockBtreeWithRetry(pBt);
  2120   2153       if( rc!=SQLITE_OK ){
  2121   2154         return rc;
  2122   2155       }
  2123   2156     }
  2124   2157     pCur = sqliteMallocRaw( sizeof(*pCur) );
  2125   2158     if( pCur==0 ){
  2126   2159       rc = SQLITE_NOMEM;
................................................................................
  5527   5560   */
  5528   5561   char *sqlite3BtreeIntegrityCheck(Btree *pBt, int *aRoot, int nRoot){
  5529   5562     int i;
  5530   5563     int nRef;
  5531   5564     IntegrityCk sCheck;
  5532   5565   
  5533   5566     nRef = *sqlite3pager_stats(pBt->pPager);
  5534         -  if( lockBtree(pBt)!=SQLITE_OK ){
         5567  +  if( lockBtreeWithRetry(pBt)!=SQLITE_OK ){
  5535   5568       return sqliteStrDup("Unable to acquire a read lock on the database");
  5536   5569     }
  5537   5570     sCheck.pBt = pBt;
  5538   5571     sCheck.pPager = pBt->pPager;
  5539   5572     sCheck.nPage = sqlite3pager_pagecount(sCheck.pPager);
  5540   5573     if( sCheck.nPage==0 ){
  5541   5574       unlockBtreeIfUnused(pBt);

Changes to src/pager.c.

    14     14   ** The pager is used to access a database disk file.  It implements
    15     15   ** atomic commit and rollback through the use of a journal file that
    16     16   ** is separate from the database file.  The pager also implements file
    17     17   ** locking to prevent two processes from writing the same database
    18     18   ** file simultaneously, or one process from reading the database while
    19     19   ** another is writing.
    20     20   **
    21         -** @(#) $Id: pager.c,v 1.192 2005/03/10 14:11:13 drh Exp $
           21  +** @(#) $Id: pager.c,v 1.193 2005/03/14 02:01:50 drh Exp $
    22     22   */
    23     23   #include "sqliteInt.h"
    24     24   #include "os.h"
    25     25   #include "pager.h"
    26     26   #include <assert.h>
    27     27   #include <string.h>
    28     28   
................................................................................
  1820   1820     assert( PAGER_SHARED==SHARED_LOCK );
  1821   1821     assert( PAGER_RESERVED==RESERVED_LOCK );
  1822   1822     assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK );
  1823   1823     if( pPager->state>=locktype ){
  1824   1824       rc = SQLITE_OK;
  1825   1825     }else{
  1826   1826       int busy = 1;
         1827  +    BusyHandler *pH;
  1827   1828       do {
  1828   1829         rc = sqlite3OsLock(&pPager->fd, locktype);
  1829   1830       }while( rc==SQLITE_BUSY && 
  1830         -        pPager->pBusyHandler && 
  1831         -        pPager->pBusyHandler->xFunc && 
  1832         -        pPager->pBusyHandler->xFunc(pPager->pBusyHandler->pArg, busy++)
         1831  +        (pH = pPager->pBusyHandler)!=0 && 
         1832  +        pH->xFunc && pH->xFunc(pH->pArg, busy++)
  1833   1833       );
  1834   1834       if( rc==SQLITE_OK ){
  1835   1835         pPager->state = locktype;
  1836   1836       }
  1837   1837     }
  1838   1838     return rc;
  1839   1839   }
................................................................................
  2629   2629     assert( pPager->state!=PAGER_UNLOCK );
  2630   2630     if( pPager->state==PAGER_SHARED ){
  2631   2631       assert( pPager->aInJournal==0 );
  2632   2632       if( MEMDB ){
  2633   2633         pPager->state = PAGER_EXCLUSIVE;
  2634   2634         pPager->origDbSize = pPager->dbSize;
  2635   2635       }else{
  2636         -      if( SQLITE_BUSY_RESERVED_LOCK || exFlag ){
  2637         -        rc = pager_wait_on_lock(pPager, RESERVED_LOCK);
  2638         -      }else{
  2639         -        rc = sqlite3OsLock(&pPager->fd, RESERVED_LOCK);
  2640         -      }
         2636  +      rc = sqlite3OsLock(&pPager->fd, RESERVED_LOCK);
  2641   2637         if( rc==SQLITE_OK ){
  2642   2638           pPager->state = PAGER_RESERVED;
  2643   2639           if( exFlag ){
  2644   2640             rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
  2645   2641           }
  2646   2642         }
  2647   2643         if( rc!=SQLITE_OK ){

Changes to test/lock.test.

     7      7   #    May you find forgiveness for yourself and forgive others.
     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   # This file implements regression tests for SQLite library.  The
    12     12   # focus of this script is database locks.
    13     13   #
    14         -# $Id: lock.test,v 1.30 2005/01/12 12:44:04 danielk1977 Exp $
           14  +# $Id: lock.test,v 1.31 2005/03/14 02:01:50 drh Exp $
    15     15   
    16     16   
    17     17   set testdir [file dirname $argv0]
    18     18   source $testdir/tester.tcl
    19     19   
    20     20   # Create an alternative connection to the database
    21     21   #
................................................................................
   165    165   # A thread can read when another has a RESERVED lock.
   166    166   #
   167    167   do_test lock-2.2 {
   168    168     catchsql {SELECT * FROM t2} db2
   169    169   } {0 {9 8}}
   170    170   
   171    171   # If the other thread (the one that does not hold the transaction with
   172         -# a RESERVED lock) tries to get a RESERVED lock, we do not get a busy callback.
          172  +# a RESERVED lock) tries to get a RESERVED lock, we do get a busy callback
          173  +# as long as we were not orginally holding a READ lock.
   173    174   #
   174         -do_test lock-2.3 {
          175  +do_test lock-2.3.1 {
   175    176     proc callback {count} {
   176    177       set ::callback_value $count
   177    178       break
   178    179     }
   179    180     set ::callback_value {}
   180    181     db2 busy callback
          182  +  # db2 does not hold a lock so we should get a busy callback here
          183  +  set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg]
          184  +  lappend r $msg
          185  +  lappend r $::callback_value
          186  +} {1 {database is locked} 0}
          187  +do_test lock-2.3.2 {
          188  +  set ::callback_value {}
          189  +  execsql {BEGIN; SELECT rowid FROM sqlite_master LIMIT 1} db2
          190  +  # This time db2 does hold a read lock.  No busy callback this time.
   181    191     set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg]
   182    192     lappend r $msg
   183    193     lappend r $::callback_value
   184    194   } {1 {database is locked} {}}
   185         -do_test lock-2.4 {
          195  +catch {execsql {ROLLBACK} db2}
          196  +do_test lock-2.4.1 {
          197  +  proc callback {count} {
          198  +    lappend ::callback_value $count
          199  +    if {$count>4} break
          200  +  }
          201  +  set ::callback_value {}
          202  +  db2 busy callback
          203  +  # We get a busy callback because db2 is not holding a lock
          204  +  set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg]
          205  +  lappend r $msg
          206  +  lappend r $::callback_value
          207  +} {1 {database is locked} {0 1 2 3 4 5}}
          208  +do_test lock-2.4.2 {
   186    209     proc callback {count} {
   187    210       lappend ::callback_value $count
   188    211       if {$count>4} break
   189    212     }
   190    213     set ::callback_value {}
   191    214     db2 busy callback
          215  +  execsql {BEGIN; SELECT rowid FROM sqlite_master LIMIT 1} db2
          216  +  # No busy callback this time because we are holding a lock
   192    217     set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg]
   193    218     lappend r $msg
   194    219     lappend r $::callback_value
   195    220   } {1 {database is locked} {}}
          221  +catch {execsql {ROLLBACK} db2}
   196    222   do_test lock-2.5 {
   197    223     proc callback {count} {
   198    224       lappend ::callback_value $count
   199    225       if {$count>4} break
   200    226     }
   201    227     set ::callback_value {}
   202    228     db2 busy callback
................................................................................
   251    277     proc callback {count} {
   252    278       lappend ::callback_value $count
   253    279       if {$count>4} break
   254    280     }
   255    281     db2 busy callback
   256    282     set rc [catch {db2 eval {UPDATE t1 SET a=0}} msg]
   257    283     lappend rc $msg $::callback_value
   258         -} {1 {database is locked} {}}
          284  +} {1 {database is locked} {0 1 2 3 4 5}}
   259    285   execsql {ROLLBACK}
   260    286   
   261    287   # When one thread is writing, other threads cannot read.  Except if the
   262    288   # writing thread is writing to its temporary tables, the other threads
   263    289   # can still read.  -> Not so in 3.0.  One thread can read while another
   264    290   # holds a RESERVED lock.
   265    291   #

Changes to www/capi3ref.tcl.

     1         -set rcsid {$Id: capi3ref.tcl,v 1.19 2005/03/12 18:03:59 drh Exp $}
            1  +set rcsid {$Id: capi3ref.tcl,v 1.20 2005/03/14 02:01:50 drh Exp $}
     2      2   source common.tcl
     3      3   header {C/C++ Interface For SQLite Version 3}
     4      4   puts {
     5      5   <h2>C/C++ Interface For SQLite Version 3</h2>
     6      6   }
     7      7   
     8      8   proc api {name prototype desc {notused x}} {
................................................................................
   161    161    second argument is the number of prior calls to the busy callback
   162    162    for the same lock.  If the
   163    163    busy callback returns 0, then no additional attempts are made to
   164    164    access the database and SQLITE_BUSY is returned.
   165    165    If the callback returns non-zero, then another attempt is made to open the
   166    166    database for reading and the cycle repeats.
   167    167   
   168         - That a busy handler is registered does not guarantee that
          168  + The presence of a busy handler does not guarantee that
   169    169    it will be invoked when there is lock contention.
   170    170    If SQLite determines that invoking the busy handler could result in
   171    171    a deadlock, it will return SQLITE_BUSY instead.
   172    172    Consider a scenario where one process is holding a read lock that
   173    173    it is trying to promote to a reserved lock and
   174    174    a second process is holding a reserved lock that it is trying
   175    175    to promote to an exclusive lock.  The first process cannot proceed