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 |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
644c6398e52481e5dda87671e1c196b2 |
User & Date: | drh 2005-03-14 02:01:50.000 |
Context
2005-03-15
| ||
02:04 | Allow the database name in a DETACH statement to be quoted. Ticket #1151. (CVS 2386) (check-in: 24e8877352 user: drh tags: trunk) | |
2005-03-14
| ||
02:01 | Be more aggressive about using the busy handler. Ticket #1159. (CVS 2385) (check-in: 644c6398e5 user: drh tags: trunk) | |
2005-03-12
| ||
18:03 | Fix typo in documentation. (CVS 2384) (check-in: 78012246fc user: drh tags: trunk) | |
Changes
Changes to src/btree.c.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 2004 April 6 ** ** 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. ** ************************************************************************* | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* ** 2004 April 6 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** $Id: btree.c,v 1.252 2005/03/14 02:01:50 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to ** ** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: ** "Sorting And Searching", pages 473-480. Addison-Wesley ** Publishing Company, Reading, Massachusetts. |
︙ | ︙ | |||
310 311 312 313 314 315 316 317 318 319 320 321 322 323 | u16 pageSize; /* Total number of bytes on a page */ u16 psAligned; /* pageSize rounded up to a multiple of 8 */ u16 usableSize; /* Number of usable bytes on each page */ int maxLocal; /* Maximum local payload in non-LEAFDATA tables */ int minLocal; /* Minimum local payload in non-LEAFDATA tables */ int maxLeaf; /* Maximum local payload in a LEAFDATA table */ int minLeaf; /* Minimum local payload in a LEAFDATA table */ }; typedef Btree Bt; /* ** Btree.inTrans may take one of the following values. */ #define TRANS_NONE 0 | > | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 | u16 pageSize; /* Total number of bytes on a page */ u16 psAligned; /* pageSize rounded up to a multiple of 8 */ u16 usableSize; /* Number of usable bytes on each page */ int maxLocal; /* Maximum local payload in non-LEAFDATA tables */ int minLocal; /* Minimum local payload in non-LEAFDATA tables */ int maxLeaf; /* Maximum local payload in a LEAFDATA table */ int minLeaf; /* Minimum local payload in a LEAFDATA table */ BusyHandler *pBusyHandler; /* Callback for when there is lock contention */ }; typedef Btree Bt; /* ** Btree.inTrans may take one of the following values. */ #define TRANS_NONE 0 |
︙ | ︙ | |||
1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 | return SQLITE_OK; } /* ** Change the busy handler callback function. */ int sqlite3BtreeSetBusyHandler(Btree *pBt, BusyHandler *pHandler){ sqlite3pager_set_busyhandler(pBt->pPager, pHandler); return SQLITE_OK; } /* ** Change the limit on the number of pages allowed in the cache. ** | > | 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 | return SQLITE_OK; } /* ** Change the busy handler callback function. */ int sqlite3BtreeSetBusyHandler(Btree *pBt, BusyHandler *pHandler){ pBt->pBusyHandler = pHandler; sqlite3pager_set_busyhandler(pBt->pPager, pHandler); return SQLITE_OK; } /* ** Change the limit on the number of pages allowed in the cache. ** |
︙ | ︙ | |||
1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 | page1_init_failed: releasePage(pPage1); pBt->pPage1 = 0; return rc; } /* ** If there are no outstanding cursors and we are not in the middle ** of a transaction but there is a read lock on the database, then ** this routine unrefs the first page of the database file which ** has the effect of releasing the read lock. ** ** If there are any outstanding cursors, this routine is a no-op. | > > > > > > > > > > > > > > | 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 | page1_init_failed: releasePage(pPage1); pBt->pPage1 = 0; return rc; } /* ** This routine works like lockBtree() except that it also invokes the ** busy callback if there is lock contention. */ static int lockBtreeWithRetry(Btree *pBt){ int rc = SQLITE_OK; if( pBt->inTrans==TRANS_NONE ){ rc = sqlite3BtreeBeginTrans(pBt, 0); pBt->inTrans = TRANS_NONE; } return rc; } /* ** If there are no outstanding cursors and we are not in the middle ** of a transaction but there is a read lock on the database, then ** this routine unrefs the first page of the database file which ** has the effect of releasing the read lock. ** ** If there are any outstanding cursors, this routine is a no-op. |
︙ | ︙ | |||
1539 1540 1541 1542 1543 1544 1545 | /* ** Attempt to start a new transaction. A write-transaction ** is started if the second argument is nonzero, otherwise a read- ** transaction. If the second argument is 2 or more and exclusive ** transaction is started, meaning that no other process is allowed ** to access the database. A preexisting transaction may not be | | | | | > > > > > > > > | > > > | < > > > | | | | | | | | | | | | | | | | | > > > > | 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 | /* ** Attempt to start a new transaction. A write-transaction ** is started if the second argument is nonzero, otherwise a read- ** transaction. If the second argument is 2 or more and exclusive ** transaction is started, meaning that no other process is allowed ** to access the database. A preexisting transaction may not be ** upgraded to exclusive by calling this routine a second time - the ** exclusivity flag only works for a new transaction. ** ** A write-transaction must be started before attempting any ** changes to the database. None of the following routines ** will work unless a transaction is started first: ** ** sqlite3BtreeCreateTable() ** sqlite3BtreeCreateIndex() ** sqlite3BtreeClearTable() ** sqlite3BtreeDropTable() ** sqlite3BtreeInsert() ** sqlite3BtreeDelete() ** sqlite3BtreeUpdateMeta() ** ** If an initial attempt to acquire the lock fails because of lock contention ** and the database was previously unlocked, then invoke the busy handler ** if there is one. But if there was previously a read-lock, do not ** invoke the busy handler - just return SQLITE_BUSY. SQLITE_BUSY is ** returned when there is already a read-lock in order to avoid a deadlock. ** ** Suppose there are two processes A and B. A has a read lock and B has ** a reserved lock. B tries to promote to exclusive but is blocked because ** of A's read lock. A tries to promote to reserved but is blocked by B. ** One or the other of the two processes must give way or there can be ** no progress. By returning SQLITE_BUSY and not invoking the busy callback ** when A already has a read lock, we encourage A to give up and let B ** proceed. */ int sqlite3BtreeBeginTrans(Btree *pBt, int wrflag){ int rc = SQLITE_OK; int busy = 0; BusyHandler *pH; /* If the btree is already in a write-transaction, or it ** is already in a read-transaction and a read-transaction ** is requested, this is a no-op. */ if( pBt->inTrans==TRANS_WRITE || (pBt->inTrans==TRANS_READ && !wrflag) ){ return SQLITE_OK; } /* Write transactions are not possible on a read-only database */ if( pBt->readOnly && wrflag ){ return SQLITE_READONLY; } do { if( pBt->pPage1==0 ){ rc = lockBtree(pBt); } if( rc==SQLITE_OK && wrflag ){ rc = sqlite3pager_begin(pBt->pPage1->aData, wrflag>1); if( rc==SQLITE_OK ){ rc = newDatabase(pBt); } } if( rc==SQLITE_OK ){ pBt->inTrans = (wrflag?TRANS_WRITE:TRANS_READ); if( wrflag ) pBt->inStmt = 0; }else{ unlockBtreeIfUnused(pBt); } }while( rc==SQLITE_BUSY && pBt->inTrans==TRANS_NONE && (pH = pBt->pBusyHandler)!=0 && pH->xFunc && pH->xFunc(pH->pArg, busy++) ); return rc; } #ifndef SQLITE_OMIT_AUTOVACUUM /* ** Set the pointer-map entries for all children of page pPage. Also, if |
︙ | ︙ | |||
2112 2113 2114 2115 2116 2117 2118 | return SQLITE_READONLY; } if( checkReadLocks(pBt, iTable, 0) ){ return SQLITE_LOCKED; } } if( pBt->pPage1==0 ){ | | | 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 | return SQLITE_READONLY; } if( checkReadLocks(pBt, iTable, 0) ){ return SQLITE_LOCKED; } } if( pBt->pPage1==0 ){ rc = lockBtreeWithRetry(pBt); if( rc!=SQLITE_OK ){ return rc; } } pCur = sqliteMallocRaw( sizeof(*pCur) ); if( pCur==0 ){ rc = SQLITE_NOMEM; |
︙ | ︙ | |||
5527 5528 5529 5530 5531 5532 5533 | */ char *sqlite3BtreeIntegrityCheck(Btree *pBt, int *aRoot, int nRoot){ int i; int nRef; IntegrityCk sCheck; nRef = *sqlite3pager_stats(pBt->pPager); | | | 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 | */ char *sqlite3BtreeIntegrityCheck(Btree *pBt, int *aRoot, int nRoot){ int i; int nRef; IntegrityCk sCheck; nRef = *sqlite3pager_stats(pBt->pPager); if( lockBtreeWithRetry(pBt)!=SQLITE_OK ){ return sqliteStrDup("Unable to acquire a read lock on the database"); } sCheck.pBt = pBt; sCheck.pPager = pBt->pPager; sCheck.nPage = sqlite3pager_pagecount(sCheck.pPager); if( sCheck.nPage==0 ){ unlockBtreeIfUnused(pBt); |
︙ | ︙ |
Changes to src/pager.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | ** The pager is used to access a database disk file. It implements ** atomic commit and rollback through the use of a journal file that ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ** The pager is used to access a database disk file. It implements ** atomic commit and rollback through the use of a journal file that ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** ** @(#) $Id: pager.c,v 1.193 2005/03/14 02:01:50 drh Exp $ */ #include "sqliteInt.h" #include "os.h" #include "pager.h" #include <assert.h> #include <string.h> |
︙ | ︙ | |||
1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 | assert( PAGER_SHARED==SHARED_LOCK ); assert( PAGER_RESERVED==RESERVED_LOCK ); assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK ); if( pPager->state>=locktype ){ rc = SQLITE_OK; }else{ int busy = 1; do { rc = sqlite3OsLock(&pPager->fd, locktype); }while( rc==SQLITE_BUSY && | > | | < | 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 | assert( PAGER_SHARED==SHARED_LOCK ); assert( PAGER_RESERVED==RESERVED_LOCK ); assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK ); if( pPager->state>=locktype ){ rc = SQLITE_OK; }else{ int busy = 1; BusyHandler *pH; do { rc = sqlite3OsLock(&pPager->fd, locktype); }while( rc==SQLITE_BUSY && (pH = pPager->pBusyHandler)!=0 && pH->xFunc && pH->xFunc(pH->pArg, busy++) ); if( rc==SQLITE_OK ){ pPager->state = locktype; } } return rc; } |
︙ | ︙ | |||
2629 2630 2631 2632 2633 2634 2635 | assert( pPager->state!=PAGER_UNLOCK ); if( pPager->state==PAGER_SHARED ){ assert( pPager->aInJournal==0 ); if( MEMDB ){ pPager->state = PAGER_EXCLUSIVE; pPager->origDbSize = pPager->dbSize; }else{ | < < < | < | 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 | assert( pPager->state!=PAGER_UNLOCK ); if( pPager->state==PAGER_SHARED ){ assert( pPager->aInJournal==0 ); if( MEMDB ){ pPager->state = PAGER_EXCLUSIVE; pPager->origDbSize = pPager->dbSize; }else{ rc = sqlite3OsLock(&pPager->fd, RESERVED_LOCK); if( rc==SQLITE_OK ){ pPager->state = PAGER_RESERVED; if( exFlag ){ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); } } if( rc!=SQLITE_OK ){ |
︙ | ︙ |
Changes to test/lock.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 2001 September 15 # # 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. The # focus of this script is database locks. # | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # 2001 September 15 # # 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. The # focus of this script is database locks. # # $Id: lock.test,v 1.31 2005/03/14 02:01:50 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Create an alternative connection to the database # |
︙ | ︙ | |||
165 166 167 168 169 170 171 | # A thread can read when another has a RESERVED lock. # do_test lock-2.2 { catchsql {SELECT * FROM t2} db2 } {0 {9 8}} # If the other thread (the one that does not hold the transaction with | | > | > > > > > > > > > > | > > > > > > > > > > > > > > > | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | # A thread can read when another has a RESERVED lock. # do_test lock-2.2 { catchsql {SELECT * FROM t2} db2 } {0 {9 8}} # If the other thread (the one that does not hold the transaction with # a RESERVED lock) tries to get a RESERVED lock, we do get a busy callback # as long as we were not orginally holding a READ lock. # do_test lock-2.3.1 { proc callback {count} { set ::callback_value $count break } set ::callback_value {} db2 busy callback # db2 does not hold a lock so we should get a busy callback here set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg] lappend r $msg lappend r $::callback_value } {1 {database is locked} 0} do_test lock-2.3.2 { set ::callback_value {} execsql {BEGIN; SELECT rowid FROM sqlite_master LIMIT 1} db2 # This time db2 does hold a read lock. No busy callback this time. set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg] lappend r $msg lappend r $::callback_value } {1 {database is locked} {}} catch {execsql {ROLLBACK} db2} do_test lock-2.4.1 { proc callback {count} { lappend ::callback_value $count if {$count>4} break } set ::callback_value {} db2 busy callback # We get a busy callback because db2 is not holding a lock set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg] lappend r $msg lappend r $::callback_value } {1 {database is locked} {0 1 2 3 4 5}} do_test lock-2.4.2 { proc callback {count} { lappend ::callback_value $count if {$count>4} break } set ::callback_value {} db2 busy callback execsql {BEGIN; SELECT rowid FROM sqlite_master LIMIT 1} db2 # No busy callback this time because we are holding a lock set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg] lappend r $msg lappend r $::callback_value } {1 {database is locked} {}} catch {execsql {ROLLBACK} db2} do_test lock-2.5 { proc callback {count} { lappend ::callback_value $count if {$count>4} break } set ::callback_value {} db2 busy callback |
︙ | ︙ | |||
251 252 253 254 255 256 257 | proc callback {count} { lappend ::callback_value $count if {$count>4} break } db2 busy callback set rc [catch {db2 eval {UPDATE t1 SET a=0}} msg] lappend rc $msg $::callback_value | | | 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | proc callback {count} { lappend ::callback_value $count if {$count>4} break } db2 busy callback set rc [catch {db2 eval {UPDATE t1 SET a=0}} msg] lappend rc $msg $::callback_value } {1 {database is locked} {0 1 2 3 4 5}} execsql {ROLLBACK} # When one thread is writing, other threads cannot read. Except if the # writing thread is writing to its temporary tables, the other threads # can still read. -> Not so in 3.0. One thread can read while another # holds a RESERVED lock. # |
︙ | ︙ |
Changes to www/capi3ref.tcl.
|
| | | 1 2 3 4 5 6 7 8 | set rcsid {$Id: capi3ref.tcl,v 1.20 2005/03/14 02:01:50 drh Exp $} source common.tcl header {C/C++ Interface For SQLite Version 3} puts { <h2>C/C++ Interface For SQLite Version 3</h2> } proc api {name prototype desc {notused x}} { |
︙ | ︙ | |||
161 162 163 164 165 166 167 | second argument is the number of prior calls to the busy callback for the same lock. If the busy callback returns 0, then no additional attempts are made to access the database and SQLITE_BUSY is returned. If the callback returns non-zero, then another attempt is made to open the database for reading and the cycle repeats. | | | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | second argument is the number of prior calls to the busy callback for the same lock. If the busy callback returns 0, then no additional attempts are made to access the database and SQLITE_BUSY is returned. If the callback returns non-zero, then another attempt is made to open the database for reading and the cycle repeats. The presence of a busy handler does not guarantee that it will be invoked when there is lock contention. If SQLite determines that invoking the busy handler could result in a deadlock, it will return SQLITE_BUSY instead. Consider a scenario where one process is holding a read lock that it is trying to promote to a reserved lock and a second process is holding a reserved lock that it is trying to promote to an exclusive lock. The first process cannot proceed |
︙ | ︙ |