1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

Be more aggressive about using the busy handler. Ticket #1159. (CVS 2385)

FossilOrigin-Name: 644c6398e52481e5dda87671e1c196b26b1e4990
This commit is contained in:
drh
2005-03-14 02:01:49 +00:00
parent 556927f944
commit b8ef32c3e8
6 changed files with 107 additions and 52 deletions

View File

@@ -1,5 +1,5 @@
C Fix\stypo\sin\sdocumentation.\s(CVS\s2384) C Be\smore\saggressive\sabout\susing\sthe\sbusy\shandler.\s\sTicket\s#1159.\s(CVS\s2385)
D 2005-03-12T18:03:59 D 2005-03-14T02:01:50
F Makefile.in 5c00d0037104de2a50ac7647a5f12769795957a3 F Makefile.in 5c00d0037104de2a50ac7647a5f12769795957a3
F Makefile.linux-gcc 06be33b2a9ad4f005a5f42b22c4a19dab3cbb5c7 F Makefile.linux-gcc 06be33b2a9ad4f005a5f42b22c4a19dab3cbb5c7
F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1 F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1
@@ -30,7 +30,7 @@ F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
F src/alter.c 6dab3d91aa4bf5c24e874145a2a547070c8c1883 F src/alter.c 6dab3d91aa4bf5c24e874145a2a547070c8c1883
F src/attach.c f78f76bc6a8e5e487ca53636e21ccba2484a9a61 F src/attach.c f78f76bc6a8e5e487ca53636e21ccba2484a9a61
F src/auth.c 18c5a0befe20f3a58a41e3ddd78f372faeeefe1f F src/auth.c 18c5a0befe20f3a58a41e3ddd78f372faeeefe1f
F src/btree.c fab5b169d25fef5a274b41edeff3f83eaa70fae2 F src/btree.c 1d9b2179ccac13970c883da6ae3758cc72978bb0
F src/btree.h 2e2cc923224649337d7217df0dd32b06673ca180 F src/btree.h 2e2cc923224649337d7217df0dd32b06673ca180
F src/build.c a8792b2f866c1ccc32f4977f4ff61d787d60ddfb F src/build.c a8792b2f866c1ccc32f4977f4ff61d787d60ddfb
F src/date.c f3d1f5cd1503dabf426a198f3ebef5afbc122a7f F src/date.c f3d1f5cd1503dabf426a198f3ebef5afbc122a7f
@@ -52,7 +52,7 @@ F src/os_unix.c d4823c6b3dd86e8cbb6a8f9d2fd6c4b3e722f8ee
F src/os_unix.h 40b2fd1d02cfa45d6c3dea25316fd019cf9fcb0c F src/os_unix.h 40b2fd1d02cfa45d6c3dea25316fd019cf9fcb0c
F src/os_win.c bddeae1c3299be0fbe47077dd4e98b786a067f71 F src/os_win.c bddeae1c3299be0fbe47077dd4e98b786a067f71
F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
F src/pager.c a789b0ffe7a42fd99f54f8c938fd9a866669e79d F src/pager.c 2e795b3d85303b03daa1e18727a6aaa215a50980
F src/pager.h 70d496f372163abb6340f474288c4bb9ea962cf7 F src/pager.h 70d496f372163abb6340f474288c4bb9ea962cf7
F src/parse.y 0b6135268a7a29db35335d5b71b5a8791e02f91e F src/parse.y 0b6135268a7a29db35335d5b71b5a8791e02f91e
F src/pragma.c 4b20dbc0f4b97f412dc511853d3d0c2e0d4adedc F src/pragma.c 4b20dbc0f4b97f412dc511853d3d0c2e0d4adedc
@@ -147,7 +147,7 @@ F test/join4.test cc6cafe85e11aacacd0abcd247a46bed251308f8
F test/lastinsert.test b6a1db3e1ce2d3f0d6afe99d445084f543b6feaa F test/lastinsert.test b6a1db3e1ce2d3f0d6afe99d445084f543b6feaa
F test/laststmtchanges.test 07cbdabc52407c29e40abc25050f2434f044a6b1 F test/laststmtchanges.test 07cbdabc52407c29e40abc25050f2434f044a6b1
F test/limit.test 270b076f31c5c32f7187de5727e74da4de43e477 F test/limit.test 270b076f31c5c32f7187de5727e74da4de43e477
F test/lock.test a19aab9a963273fe61c1058e3d1b648d6a0a2425 F test/lock.test 0b95ae28471f5123d24008d1c0fead911bf3c4be
F test/lock2.test 59c3dd7d9b24d1bf7ec91b2d1541c37e97939d5f F test/lock2.test 59c3dd7d9b24d1bf7ec91b2d1541c37e97939d5f
F test/lock3.test 615111293cf32aa2ed16d01c6611737651c96fb9 F test/lock3.test 615111293cf32aa2ed16d01c6611737651c96fb9
F test/main.test febb69416071134dc38b9b1971c0c2e5b0ca3ff8 F test/main.test febb69416071134dc38b9b1971c0c2e5b0ca3ff8
@@ -239,7 +239,7 @@ F www/audit.tcl 90e09d580f79c7efec0c7d6f447b7ec5c2dce5c0
F www/autoinc.tcl b357f5ba954b046ee35392ce0f884a2fcfcdea06 F www/autoinc.tcl b357f5ba954b046ee35392ce0f884a2fcfcdea06
F www/c_interface.tcl b51b08591554c16a0c3ef718364a508ac25abc7e F www/c_interface.tcl b51b08591554c16a0c3ef718364a508ac25abc7e
F www/capi3.tcl 7a7cc225fe02eb7ab861a6019b08baa0014409e1 F www/capi3.tcl 7a7cc225fe02eb7ab861a6019b08baa0014409e1
F www/capi3ref.tcl f395ff43a56ef087407b0e5ba6604f2f8c32cc14 F www/capi3ref.tcl 59c7da9ef1f24dcb10b247cd3b7452cf76ab36f0
F www/changes.tcl dbace6eb8ecf10f5e7047d77115feb1742e56610 F www/changes.tcl dbace6eb8ecf10f5e7047d77115feb1742e56610
F www/common.tcl de758130d54d95d151ea0d17a2ae5b92e1bb01de F www/common.tcl de758130d54d95d151ea0d17a2ae5b92e1bb01de
F www/compile.tcl 65b26bdfc964b66c5f0af841718a52f9795ceb05 F www/compile.tcl 65b26bdfc964b66c5f0af841718a52f9795ceb05
@@ -274,7 +274,7 @@ F www/tclsqlite.tcl e73f8f8e5f20e8277619433f7970060ab01088fc
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0 F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
F www/whentouse.tcl 3e522a06ad41992023c80ca29a048ae2331ca5bd F www/whentouse.tcl 3e522a06ad41992023c80ca29a048ae2331ca5bd
P 33a0191638a4d6b33422f62487bfb9a0089d3cff P 78012246fc1c1fe844d192cfff69a736e388ce7a
R 2c190de0bffdef6a18303ed58cf43144 R 12c7fb7c202c4d99608b53151d0373a0
U drh U drh
Z d98e1a0c532519fa0eb91c504a344f0c Z 9df5123a27f166217f6a13afcd69b178

View File

@@ -1 +1 @@
78012246fc1c1fe844d192cfff69a736e388ce7a 644c6398e52481e5dda87671e1c196b26b1e4990

View File

@@ -9,7 +9,7 @@
** May you share freely, never taking more than you give. ** May you share freely, never taking more than you give.
** **
************************************************************************* *************************************************************************
** $Id: btree.c,v 1.251 2005/03/10 17:06:34 drh Exp $ ** $Id: btree.c,v 1.252 2005/03/14 02:01:50 drh Exp $
** **
** This file implements a external (disk-based) database using BTrees. ** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to ** For a detailed discussion of BTrees, refer to
@@ -314,6 +314,7 @@ struct Btree {
int minLocal; /* Minimum 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 maxLeaf; /* Maximum local payload in a LEAFDATA table */
int minLeaf; /* Minimum 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; typedef Btree Bt;
@@ -1291,6 +1292,7 @@ int sqlite3BtreeClose(Btree *pBt){
** Change the busy handler callback function. ** Change the busy handler callback function.
*/ */
int sqlite3BtreeSetBusyHandler(Btree *pBt, BusyHandler *pHandler){ int sqlite3BtreeSetBusyHandler(Btree *pBt, BusyHandler *pHandler){
pBt->pBusyHandler = pHandler;
sqlite3pager_set_busyhandler(pBt->pPager, pHandler); sqlite3pager_set_busyhandler(pBt->pPager, pHandler);
return SQLITE_OK; return SQLITE_OK;
} }
@@ -1479,6 +1481,20 @@ page1_init_failed:
return rc; 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 ** 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 ** of a transaction but there is a read lock on the database, then
@@ -1543,7 +1559,7 @@ static int newDatabase(Btree *pBt){
** transaction. If the second argument is 2 or more and exclusive ** transaction. If the second argument is 2 or more and exclusive
** transaction is started, meaning that no other process is allowed ** transaction is started, meaning that no other process is allowed
** to access the database. A preexisting transaction may not be ** to access the database. A preexisting transaction may not be
** upgrade to exclusive by calling this routine a second time - the ** upgraded to exclusive by calling this routine a second time - the
** exclusivity flag only works for a new transaction. ** exclusivity flag only works for a new transaction.
** **
** A write-transaction must be started before attempting any ** A write-transaction must be started before attempting any
@@ -1558,43 +1574,60 @@ static int newDatabase(Btree *pBt){
** sqlite3BtreeDelete() ** sqlite3BtreeDelete()
** sqlite3BtreeUpdateMeta() ** sqlite3BtreeUpdateMeta()
** **
** If wrflag is true, then nMaster specifies the maximum length of ** If an initial attempt to acquire the lock fails because of lock contention
** a master journal file name supplied later via sqlite3BtreeSync(). ** and the database was previously unlocked, then invoke the busy handler
** This is so that appropriate space can be allocated in the journal file ** if there is one. But if there was previously a read-lock, do not
** when it is created.. ** 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 sqlite3BtreeBeginTrans(Btree *pBt, int wrflag){
int rc = SQLITE_OK; int rc = SQLITE_OK;
int busy = 0;
BusyHandler *pH;
/* If the btree is already in a write-transaction, or it /* If the btree is already in a write-transaction, or it
** is already in a read-transaction and a read-transaction ** is already in a read-transaction and a read-transaction
** is requested, this is a no-op. ** is requested, this is a no-op.
*/ */
if( pBt->inTrans==TRANS_WRITE || if( pBt->inTrans==TRANS_WRITE || (pBt->inTrans==TRANS_READ && !wrflag) ){
(pBt->inTrans==TRANS_READ && !wrflag) ){
return SQLITE_OK; return SQLITE_OK;
} }
/* Write transactions are not possible on a read-only database */
if( pBt->readOnly && wrflag ){ if( pBt->readOnly && wrflag ){
return SQLITE_READONLY; return SQLITE_READONLY;
} }
if( pBt->pPage1==0 ){ do {
rc = lockBtree(pBt); 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 && wrflag ){
if( rc==SQLITE_OK ){ rc = sqlite3pager_begin(pBt->pPage1->aData, wrflag>1);
pBt->inTrans = (wrflag?TRANS_WRITE:TRANS_READ); if( rc==SQLITE_OK ){
if( wrflag ) pBt->inStmt = 0; rc = newDatabase(pBt);
}else{ }
unlockBtreeIfUnused(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; return rc;
} }
@@ -2116,7 +2149,7 @@ int sqlite3BtreeCursor(
} }
} }
if( pBt->pPage1==0 ){ if( pBt->pPage1==0 ){
rc = lockBtree(pBt); rc = lockBtreeWithRetry(pBt);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
return rc; return rc;
} }
@@ -5531,7 +5564,7 @@ char *sqlite3BtreeIntegrityCheck(Btree *pBt, int *aRoot, int nRoot){
IntegrityCk sCheck; IntegrityCk sCheck;
nRef = *sqlite3pager_stats(pBt->pPager); nRef = *sqlite3pager_stats(pBt->pPager);
if( lockBtree(pBt)!=SQLITE_OK ){ if( lockBtreeWithRetry(pBt)!=SQLITE_OK ){
return sqliteStrDup("Unable to acquire a read lock on the database"); return sqliteStrDup("Unable to acquire a read lock on the database");
} }
sCheck.pBt = pBt; sCheck.pBt = pBt;

View File

@@ -18,7 +18,7 @@
** file simultaneously, or one process from reading the database while ** file simultaneously, or one process from reading the database while
** another is writing. ** another is writing.
** **
** @(#) $Id: pager.c,v 1.192 2005/03/10 14:11:13 drh Exp $ ** @(#) $Id: pager.c,v 1.193 2005/03/14 02:01:50 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include "os.h" #include "os.h"
@@ -1824,12 +1824,12 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){
rc = SQLITE_OK; rc = SQLITE_OK;
}else{ }else{
int busy = 1; int busy = 1;
BusyHandler *pH;
do { do {
rc = sqlite3OsLock(&pPager->fd, locktype); rc = sqlite3OsLock(&pPager->fd, locktype);
}while( rc==SQLITE_BUSY && }while( rc==SQLITE_BUSY &&
pPager->pBusyHandler && (pH = pPager->pBusyHandler)!=0 &&
pPager->pBusyHandler->xFunc && pH->xFunc && pH->xFunc(pH->pArg, busy++)
pPager->pBusyHandler->xFunc(pPager->pBusyHandler->pArg, busy++)
); );
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
pPager->state = locktype; pPager->state = locktype;
@@ -2633,11 +2633,7 @@ int sqlite3pager_begin(void *pData, int exFlag){
pPager->state = PAGER_EXCLUSIVE; pPager->state = PAGER_EXCLUSIVE;
pPager->origDbSize = pPager->dbSize; pPager->origDbSize = pPager->dbSize;
}else{ }else{
if( SQLITE_BUSY_RESERVED_LOCK || exFlag ){ rc = sqlite3OsLock(&pPager->fd, RESERVED_LOCK);
rc = pager_wait_on_lock(pPager, RESERVED_LOCK);
}else{
rc = sqlite3OsLock(&pPager->fd, RESERVED_LOCK);
}
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
pPager->state = PAGER_RESERVED; pPager->state = PAGER_RESERVED;
if( exFlag ){ if( exFlag ){

View File

@@ -11,7 +11,7 @@
# This file implements regression tests for SQLite library. The # This file implements regression tests for SQLite library. The
# focus of this script is database locks. # focus of this script is database locks.
# #
# $Id: lock.test,v 1.30 2005/01/12 12:44:04 danielk1977 Exp $ # $Id: lock.test,v 1.31 2005/03/14 02:01:50 drh Exp $
set testdir [file dirname $argv0] set testdir [file dirname $argv0]
@@ -169,30 +169,56 @@ do_test lock-2.2 {
} {0 {9 8}} } {0 {9 8}}
# If the other thread (the one that does not hold the transaction with # If the other thread (the one that does not hold the transaction with
# a RESERVED lock) tries to get a RESERVED lock, we do not get a busy callback. # 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 { do_test lock-2.3.1 {
proc callback {count} { proc callback {count} {
set ::callback_value $count set ::callback_value $count
break break
} }
set ::callback_value {} set ::callback_value {}
db2 busy callback 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] set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg]
lappend r $msg lappend r $msg
lappend r $::callback_value lappend r $::callback_value
} {1 {database is locked} {}} } {1 {database is locked} {}}
do_test lock-2.4 { catch {execsql {ROLLBACK} db2}
do_test lock-2.4.1 {
proc callback {count} { proc callback {count} {
lappend ::callback_value $count lappend ::callback_value $count
if {$count>4} break if {$count>4} break
} }
set ::callback_value {} set ::callback_value {}
db2 busy callback 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] set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg]
lappend r $msg lappend r $msg
lappend r $::callback_value lappend r $::callback_value
} {1 {database is locked} {}} } {1 {database is locked} {}}
catch {execsql {ROLLBACK} db2}
do_test lock-2.5 { do_test lock-2.5 {
proc callback {count} { proc callback {count} {
lappend ::callback_value $count lappend ::callback_value $count
@@ -255,7 +281,7 @@ do_test lock-4.3 {
db2 busy callback db2 busy callback
set rc [catch {db2 eval {UPDATE t1 SET a=0}} msg] set rc [catch {db2 eval {UPDATE t1 SET a=0}} msg]
lappend rc $msg $::callback_value lappend rc $msg $::callback_value
} {1 {database is locked} {}} } {1 {database is locked} {0 1 2 3 4 5}}
execsql {ROLLBACK} execsql {ROLLBACK}
# When one thread is writing, other threads cannot read. Except if the # When one thread is writing, other threads cannot read. Except if the

View File

@@ -1,4 +1,4 @@
set rcsid {$Id: capi3ref.tcl,v 1.19 2005/03/12 18:03:59 drh Exp $} set rcsid {$Id: capi3ref.tcl,v 1.20 2005/03/14 02:01:50 drh Exp $}
source common.tcl source common.tcl
header {C/C++ Interface For SQLite Version 3} header {C/C++ Interface For SQLite Version 3}
puts { puts {
@@ -165,7 +165,7 @@ api {} {
If the callback returns non-zero, then another attempt is made to open the If the callback returns non-zero, then another attempt is made to open the
database for reading and the cycle repeats. database for reading and the cycle repeats.
That a busy handler is registered does not guarantee that The presence of a busy handler does not guarantee that
it will be invoked when there is lock contention. it will be invoked when there is lock contention.
If SQLite determines that invoking the busy handler could result in If SQLite determines that invoking the busy handler could result in
a deadlock, it will return SQLITE_BUSY instead. a deadlock, it will return SQLITE_BUSY instead.