From: dan Date: Mon, 15 May 2017 19:32:58 +0000 (+0000) Subject: Avoid writer starvation by adding a RESERVED state to page locks. X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=60dd40a62c0ba354db3abea229ec20d4720eed39;p=thirdparty%2Fsqlite.git Avoid writer starvation by adding a RESERVED state to page locks. FossilOrigin-Name: 9b7f80246f2b9921483ab23457865e783ee70b93f67bcecc0c16516447a05875 --- diff --git a/manifest b/manifest index f38012546a..f0ca521671 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Avoid\srunning\srecovery\swhile\sthere\sis\sanother\sread/write\sclient. -D 2017-05-13T19:07:10.813 +C Avoid\swriter\sstarvation\sby\sadding\sa\sRESERVED\sstate\sto\spage\slocks. +D 2017-05-15T19:32:58.633 F Makefile.in 1cc758ce3374a32425e4d130c2fe7b026b20de5b8843243de75f087c0a2661fb F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 6a8c838220f7c00820e1fc0ac1bccaaa8e5676067e1dbfa1bafa7a4ffecf8ae6 @@ -403,7 +403,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 F src/resolve.c 3e518b962d932a997fae373366880fc028c75706 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/select.c 4f0adefaa5e9417459b07757e0f6060cac97930a86f0fba9797bab233ced66c0 -F src/server.c a1732bcde85f799278c70cd4ae26adf91a7352bad5e58a7a2e5a8092d07c65fd +F src/server.c c7ba48720cd7520cc409dc926fb85d79fd70183ca5b6424c891d4e47cd184e3f F src/server.h e1ce2da1e4d21f335904539e3f98a7c24e015e1201b4bb16d61f0044b8bd2884 F src/shell.c e5950029da103c5d378e71d548759459b9a7fc76177a71562c22082c705745ab F src/sqlite.h.in 8d126e4cfbd1f4bc6f4043aacd77f78b45613e7d630185d49a5d099394247483 @@ -482,7 +482,7 @@ F src/vdbesort.c e72fe02a2121386ba767ede8942e9450878b8fc873abf3d1b6824485f092570 F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834 F src/vtab.c 35b9bdc2b41de32a417141d12097bcc4e29a77ed7cdb8f836d1d2305d946b61b F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 -F src/wal.c bbf37fd26ca1da580b31581b0d172a202bd0931c45f93ae8b09c15ead60232da +F src/wal.c 8f71654244baf38b95a277e79677d5444737c326182ba81476bc101c001f2e07 F src/wal.h 739d92494eb18b6d8f3e353e66c10eb8f94534bafd336ece9f3f60235317ea08 F src/walker.c b71a992b413b3a022572eccf29ef4b4890223791 F src/where.c c6352f15be5031907c68bcbde96cad1a6da20e9f4051d10168a59235de9a8566 @@ -1584,7 +1584,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P cbf44ed9758d577e1450b53e645b73c9ca1ee29d2354ce6375c234a41a063400 -R d5d776736d26c48307deb362071ad1ff +P a38858a24c6edaa05966b7e158e603bcbde8b05c6233e3bb005cfb32bc91ea06 +R b8e235bc4c011a85aed7a341ea53efc6 U dan -Z 76d66a6c513b28fb6f7860f0dae11a81 +Z 6c36f74987eb2c83b0e8e4c18223c9e3 diff --git a/manifest.uuid b/manifest.uuid index a46e98bb53..5993503087 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a38858a24c6edaa05966b7e158e603bcbde8b05c6233e3bb005cfb32bc91ea06 \ No newline at end of file +9b7f80246f2b9921483ab23457865e783ee70b93f67bcecc0c16516447a05875 \ No newline at end of file diff --git a/src/server.c b/src/server.c index 440f717f9b..f455490fd7 100644 --- a/src/server.c +++ b/src/server.c @@ -24,14 +24,32 @@ ** ** N*4 bytes - Page locking slots. N is HMA_PAGELOCK_SLOTS. ** -** Page lock slot format: +** Page-locking slot format: ** -** Least significant HMA_CLIENT_SLOTS used for read-locks. If bit 0 is set, -** client 0 holds a read-lock. +** Each page-locking slot provides SHARED/RESERVED/EXCLUSIVE locks on a +** single page. A RESERVED lock is similar to a RESERVED in SQLite's +** rollback mode - existing SHARED locks may continue but new SHARED locks +** may not be established. As in rollback mode, EXCLUSIVE and RESERVED +** locks are mutually exclusive. ** -** If (v) is the value of the locking slot and (v>>HMA_CLIENT_SLOTS) is -** not zero, then the write-lock holder is client ((v>>HMA_CLIENT_SLOTS)-1). +** Each 32-bit locking slot is divided into two sections - a bitmask for +** read-locks and a single integer field for the write lock. The bitmask +** occupies the least-significant 27 bits of the slot. The integer field +** occupies the remaining 5 bits (so that it can store values from 0-31). ** +** Each client has a unique integer client id. Currently these range from +** 0-15 (maximum of 16 concurrent connections). The page-locking slot format +** allows this to be increased to 0-26 (maximum of 26 connections). To +** take a SHARED lock, the corresponding bit is set in the locking slot +** bitmask: +** +** slot = slot | (1 << iClient); +** +** To take an EXCLUSIVE or RESERVED lock, the integer part of the locking +** slot is set to the client-id of the locker plus one (a value of zero +** indicates that no connection holds a RESERVED or EXCLUSIVE lock): +** +** slot = slot | ((iClient+1) << 27) */ #ifdef SQLITE_SERVER_EDITION @@ -46,6 +64,7 @@ #include "sys/mman.h" #include "sys/types.h" #include "sys/stat.h" +#include "errno.h" typedef struct ServerHMA ServerHMA; @@ -112,6 +131,9 @@ static int posixLock(int fd, int iSlot, int eLock, int bBlock){ l.l_len = 1; res = fcntl(fd, (bBlock ? F_SETLKW : F_SETLK), &l); + if( res && bBlock && errno==EDEADLK ){ + return SQLITE_BUSY_DEADLOCK; + } return (res==0 ? SQLITE_OK : SQLITE_BUSY); } @@ -366,7 +388,7 @@ static int serverOvercomeLock( int rc = SQLITE_OK; int iBlock = ((int)(v>>HMA_CLIENT_SLOTS))-1; - if( iBlock<0 ){ + if( iBlock<0 || iBlock==p->iClient ){ for(iBlock=0; iBlockiClient && (v & (1<> HMA_CLIENT_SLOTS)) - 1; +} + /* ** Lock page pgno for reading (bWrite==0) or writing (bWrite==1). ** @@ -456,6 +487,8 @@ int sqlite3ServerReleaseWriteLocks(Server *p){ */ int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){ int rc = SQLITE_OK; + int bReserved = 0; + u32 *pSlot = serverPageLockSlot(p, pgno); /* Grow the aLock[] array, if required */ if( p->nLock==p->nAlloc ){ @@ -470,7 +503,6 @@ int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){ } } if( rc==SQLITE_OK ){ - u32 *pSlot = serverPageLockSlot(p, pgno); u32 v = *pSlot; /* Check if the required lock is already held. If so, exit this function @@ -482,13 +514,28 @@ int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){ }else{ if( v & (1<iClient) ) goto server_lock_out; } - p->aLock[p->nLock++] = pgno; + while( 1 ){ u32 n; + int w; + u32 mask = (bWrite ? (((1<iClient)) : 0); - while( (bWrite && (v & ~(1 << p->iClient))) || (v >> HMA_CLIENT_SLOTS) ){ + while( ((w = serverWriteLocker(v))>=0 && w!=p->iClient) || (v & mask) ){ int bRetry = 0; + + if( w<0 && bWrite && bBlock ){ + /* Attempt a RESERVED lock before anything else */ + n = v | ((p->iClient+1) << HMA_CLIENT_SLOTS); + assert( serverWriteLocker(n)==p->iClient ); + if( __sync_val_compare_and_swap(pSlot, v, n)!=v ){ + v = *pSlot; + continue; + } + v = n; + bReserved = 1; + } + rc = serverOvercomeLock(p, bWrite, bBlock, v, &bRetry); if( rc!=SQLITE_OK ) goto server_lock_out; if( bRetry==0 ){ @@ -497,6 +544,7 @@ int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){ rc = SQLITE_BUSY_DEADLOCK; goto server_lock_out; } + v = *pSlot; } @@ -510,6 +558,17 @@ int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){ } server_lock_out: + if( rc!=SQLITE_OK && bReserved ){ + u32 n; + u32 v; + do{ + v = *pSlot; + assert( serverWriteLocker(v)==p->iClient ); + n = v & ((1<pServer, 0, 1) ); } assert( walIsServer(pWal)==0 || sqlite3ServerHasLock(pWal->pServer, 0, 1) ); @@ -3397,6 +3398,8 @@ int sqlite3WalCheckpoint( */ if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ if( walIsServer(pWal) ){ + rc = sqlite3ServerBegin(pWal->pServer); + if( rc!=SQLITE_OK ) goto ckpt_out; if( eMode>=SQLITE_CHECKPOINT_RESTART ){ /* Exclusive lock on page 1. This is exclusive access to the db. */ rc = sqlite3ServerLock(pWal->pServer, 1, 1, 1); @@ -3451,6 +3454,7 @@ int sqlite3WalCheckpoint( } /* Release the locks. */ + ckpt_out: sqlite3WalEndWriteTransaction(pWal); walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); pWal->ckptLock = 0;