Skip to content

Commit f640f79

Browse files
dandan
authored andcommitted
Ensure that SQLITE_PROTOCOL is not returned too early when a SQLITE_ENABLE_SETLK_TIMEOUT build fails to open a transaction on a wal mode database in cases where blocking locks are not being used.
FossilOrigin-Name: a8e9af1356f5fb2ec460f932dfbe89283bb4e3cf9fa677d1acdbe77ffa11dd04
2 parents bf64cbd + 4e50f77 commit f640f79

3 files changed

Lines changed: 64 additions & 19 deletions

File tree

manifest

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
C Remove\san\sALWAYS()\sfrom\sRTREE.\s\sDbsqlfuzz\sfound\sa\sway\sto\smake\sit\sfalse.
2-
D 2024-01-07T20:27:54.639
1+
C Ensure\sthat\sSQLITE_PROTOCOL\sis\snot\sreturned\stoo\searly\swhen\sa\sSQLITE_ENABLE_SETLK_TIMEOUT\sbuild\sfails\sto\sopen\sa\stransaction\son\sa\swal\smode\sdatabase\sin\scases\swhere\sblocking\slocks\sare\snot\sbeing\sused.
2+
D 2024-01-08T13:38:15.023
33
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
44
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
55
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -819,7 +819,7 @@ F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf8
819819
F src/vdbevtab.c 2143db7db0ceed69b21422581f434baffc507a08d831565193a7a02882a1b6a7
820820
F src/vtab.c 11948e105f56e84099ca17f1f434b1944539ea84de26d0d767eadfbc670ce1ea
821821
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
822-
F src/wal.c b08f88e69b9c43572f0703355dccc5b2f08296256fc6a82a582fcae68f8f9cd1
822+
F src/wal.c bf41478461ca8a2758617691152befd9d526921e2fb59d5647638679cff98381
823823
F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452
824824
F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2
825825
F src/where.c 217fe82a26c0fb6a3c7fd01865d821e752f9c01fb72f114af3f0b77ce234d1fb
@@ -2156,8 +2156,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
21562156
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
21572157
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
21582158
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
2159-
P 7a5b42ff74882c58493dc8b710fde73d4ff251f5d42271d84be73ceaabc01698
2160-
R e24ddedf31de297bbf4b76d307641631
2161-
U drh
2162-
Z ac5f82bfe64ed7224d42f062673e1893
2159+
P 40f0a29e6dd90fcb969d7c0e49728ba0ee8f31d9e8f502b9a21469620a8ad283 b934a33671d8a0190082ad7e5e68c78fe0c558d102404eafc1de26e4e7d65b92
2160+
R faa7ffdec00af93a5c93f6a8d8d1ed79
2161+
T +closed b934a33671d8a0190082ad7e5e68c78fe0c558d102404eafc1de26e4e7d65b92
2162+
U dan
2163+
Z f6fdb69739d322cda88ea774ad465280
21632164
# Remove this line to create a well-formed Fossil manifest.

manifest.uuid

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
40f0a29e6dd90fcb969d7c0e49728ba0ee8f31d9e8f502b9a21469620a8ad283
1+
a8e9af1356f5fb2ec460f932dfbe89283bb4e3cf9fa677d1acdbe77ffa11dd04

src/wal.c

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2902,6 +2902,37 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
29022902
return rc;
29032903
}
29042904

2905+
/*
2906+
** The final argument passed to walTryBeginRead() is of type (int*). The
2907+
** caller should invoke walTryBeginRead as follows:
2908+
**
2909+
** int cnt = 0;
2910+
** do {
2911+
** rc = walTryBeginRead(..., &cnt);
2912+
** }while( rc==WAL_RETRY );
2913+
**
2914+
** The final value of "cnt" is of no use to the caller. It is used by
2915+
** the implementation of walTryBeginRead() as follows:
2916+
**
2917+
** + Each time walTryBeginRead() is called, it is incremented. Once
2918+
** it reaches WAL_RETRY_PROTOCOL_LIMIT - indicating that walTryBeginRead()
2919+
** has many times been invoked and failed with WAL_RETRY - walTryBeginRead()
2920+
** returns SQLITE_PROTOCOL.
2921+
**
2922+
** + If SQLITE_ENABLE_SETLK_TIMEOUT is defined and walTryBeginRead() failed
2923+
** because a blocking lock timed out (SQLITE_BUSY_TIMEOUT from the OS
2924+
** layer), the WAL_RETRY_BLOCKED_MASK bit is set in "cnt". In this case
2925+
** the next invocation of walTryBeginRead() may omit an expected call to
2926+
** sqlite3OsSleep(). There has already been a delay when the previous call
2927+
** waited on a lock.
2928+
*/
2929+
#define WAL_RETRY_PROTOCOL_LIMIT 100
2930+
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
2931+
# define WAL_RETRY_BLOCKED_MASK 0x10000000
2932+
#else
2933+
# define WAL_RETRY_BLOCKED_MASK 0
2934+
#endif
2935+
29052936
/*
29062937
** Attempt to start a read transaction. This might fail due to a race or
29072938
** other transient condition. When that happens, it returns WAL_RETRY to
@@ -2952,7 +2983,7 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
29522983
** so it takes care to hold an exclusive lock on the corresponding
29532984
** WAL_READ_LOCK() while changing values.
29542985
*/
2955-
static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
2986+
static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
29562987
volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */
29572988
u32 mxReadMark; /* Largest aReadMark[] value */
29582989
int mxI; /* Index of largest aReadMark[] value */
@@ -2985,27 +3016,34 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
29853016
** so that on the 100th (and last) RETRY we delay for 323 milliseconds.
29863017
** The total delay time before giving up is less than 10 seconds.
29873018
*/
2988-
if( cnt>5 ){
3019+
(*pCnt)++;
3020+
if( *pCnt>5 ){
29893021
int nDelay = 1; /* Pause time in microseconds */
2990-
if( cnt>100 ){
3022+
int cnt = (*pCnt & ~WAL_RETRY_BLOCKED_MASK);
3023+
if( cnt>WAL_RETRY_PROTOCOL_LIMIT ){
29913024
VVA_ONLY( pWal->lockError = 1; )
29923025
return SQLITE_PROTOCOL;
29933026
}
2994-
if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
3027+
if( *pCnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
29953028
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
29963029
/* In SQLITE_ENABLE_SETLK_TIMEOUT builds, configure the file-descriptor
29973030
** to block for locks for approximately nDelay us. This affects three
29983031
** locks: (a) the shared lock taken on the DMS slot in os_unix.c (if
29993032
** using os_unix.c), (b) the WRITER lock taken in walIndexReadHdr() if the
3000-
** first attempted read fails, and (c) the shared lock taken on the DMS
3001-
** slot in os_unix.c. All three of these locks are attempted from within
3002-
** the call to walIndexReadHdr() below. */
3033+
** first attempted read fails, and (c) the shared lock taken on the
3034+
** read-mark.
3035+
**
3036+
** If the previous call failed due to an SQLITE_BUSY_TIMEOUT error,
3037+
** then sleep for the minimum of 1us. The previous call already provided
3038+
** an extra delay while it was blocking on the lock.
3039+
*/
30033040
nBlockTmout = (nDelay+998) / 1000;
30043041
if( !useWal && walEnableBlockingMs(pWal, nBlockTmout) ){
3005-
nDelay = 1;
3042+
if( *pCnt & WAL_RETRY_BLOCKED_MASK ) nDelay = 1;
30063043
}
30073044
#endif
30083045
sqlite3OsSleep(pWal->pVfs, nDelay);
3046+
*pCnt &= ~WAL_RETRY_BLOCKED_MASK;
30093047
}
30103048

30113049
if( !useWal ){
@@ -3015,7 +3053,10 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
30153053
}
30163054
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
30173055
walDisableBlocking(pWal);
3018-
if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY;
3056+
if( rc==SQLITE_BUSY_TIMEOUT ){
3057+
rc = SQLITE_BUSY;
3058+
*pCnt |= WAL_RETRY_BLOCKED_MASK;
3059+
}
30193060
#endif
30203061
if( rc==SQLITE_BUSY ){
30213062
/* If there is not a recovery running in another thread or process
@@ -3135,6 +3176,9 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
31353176
rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
31363177
walDisableBlocking(pWal);
31373178
if( rc ){
3179+
if( rc==SQLITE_BUSY_TIMEOUT ){
3180+
*pCnt |= WAL_RETRY_BLOCKED_MASK;
3181+
}
31383182
assert( (rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT );
31393183
return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc;
31403184
}
@@ -3324,7 +3368,7 @@ static int walBeginReadTransaction(Wal *pWal, int *pChanged){
33243368
#endif
33253369

33263370
do{
3327-
rc = walTryBeginRead(pWal, pChanged, 0, ++cnt);
3371+
rc = walTryBeginRead(pWal, pChanged, 0, &cnt);
33283372
}while( rc==WAL_RETRY );
33293373
testcase( (rc&0xff)==SQLITE_BUSY );
33303374
testcase( (rc&0xff)==SQLITE_IOERR );
@@ -3809,7 +3853,7 @@ static int walRestartLog(Wal *pWal){
38093853
cnt = 0;
38103854
do{
38113855
int notUsed;
3812-
rc = walTryBeginRead(pWal, &notUsed, 1, ++cnt);
3856+
rc = walTryBeginRead(pWal, &notUsed, 1, &cnt);
38133857
}while( rc==WAL_RETRY );
38143858
assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
38153859
testcase( (rc&0xff)==SQLITE_IOERR );

0 commit comments

Comments
 (0)