1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-12-21 13:38:01 +03:00

Supports SQLITE_ENABLE_SETLK_TIMEOUT on windows. Does not work properly yet.

FossilOrigin-Name: 737ca8a9fb9dc74b28f2186d93c5101463497445d0fabba3def61fee29abf2c8
This commit is contained in:
dan
2024-11-22 21:24:08 +00:00
parent 8ff67df7ac
commit 6bd3faa092
8 changed files with 395 additions and 140 deletions

View File

@@ -292,6 +292,12 @@ SESSION = 0
RBU = 0
!ENDIF
# Set this to non-0 to enable support for blocking locks.
#
!IFNDEF SETLK_TIMEOUT
SETLK_TIMEOUT = 0
!ENDIF
# Set the source code file to be used by executables and libraries when
# they need the amalgamation.
#
@@ -448,6 +454,10 @@ EXT_FEATURE_FLAGS =
!ENDIF
!ENDIF
!IF $(SETLK_TIMEOUT)!=0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SETLK_TIMEOUT
!ENDIF
###############################################################################
############################### END OF OPTIONS ################################
###############################################################################

View File

@@ -1,11 +1,11 @@
C Fix\sanother\sissue\sin\sargument\sexpansion\son\sWindows\sfor\stclsqlite3.c\sin\ninterpreter\smode.\s\sProblem\sintroduced\sby\scheck-in\s[9b87ea219bce5689]\sand\nunfixed\sby\s[cd942dce148c9d8f].
D 2024-11-22T17:41:00.227
C Supports\sSQLITE_ENABLE_SETLK_TIMEOUT\son\swindows.\sDoes\snot\swork\sproperly\syet.
D 2024-11-22T21:24:08.721
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md e108e1e69ae8e8a59e93c455654b8ac9356a11720d3345df2a4743e9590fb20d
F Makefile.in 85f2c740cadf969abbd9a6210b8b76636dbf231b9d3efe977b060c3055fac5b9
F Makefile.linux-generic bd3e3cacd369821a6241d4ea1967395c962dfe3057e38cb0a435cee0e8b789d0
F Makefile.msc a92237976eb92c5efaa0dd2524746aec12c196e12df8d4dbff9543a4648c3312
F Makefile.msc 9a975438b8e06da44bc169b74aa9601cd48da52abd2c88e8a349c7d82b59d250
F README.md c3c0f19532ce28f6297a71870f3c7b424729f0e6d9ab889616d3587dd2332159
F VERSION 8dc0c3df15fd5ff0622f88fc483533fce990b1cbb2f5fb9fdfb4dbd71eef2889
F art/icon-243x273.gif 9750b734f82fdb3dc43127753d5e6fbf3b62c9f4e136c2fbf573b2f57ea87af5
@@ -751,15 +751,15 @@ F src/mutex.c 06bcd9c3dbf2d9b21fcd182606c00fafb9bfe0287983c8e17acd13d2c81a2fa9
F src/mutex.h a7b2293c48db5f27007c3bdb21d438873637d12658f5a0bf8ad025bb96803c4a
F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4
F src/mutex_unix.c f7ee5a2061a4c11815a2bf4fc0e2bfa6fb8d9dc89390eb613ca0cec32fc9a3d1
F src/mutex_w32.c 28f8d480387db5b2ef5248705dd4e19db0cfc12c3ba426695a7d2c45c48e6885
F src/mutex_w32.c db182bf5aac08a16fbf5916d94974f5a11556fe150142fcabe36d6454e0d93a1
F src/notify.c 57c2d1a2805d6dee32acd5d250d928ab94e02d76369ae057dee7d445fd64e878
F src/os.c 509452169d5ea739723e213b8e2481cf0e587f0e88579a912d200db5269f5f6d
F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63
F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06
F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a
F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107
F src/os_unix.c d2edbd92b07a3f778c2defa8a2e9d75acceb6267bda56948c41e8cdda65224d6
F src/os_win.c 49c7725b500f5867e8360e75eeb30f9d70b62fa1f05c8a101da627210578df32
F src/os_unix.c d4a33e8fbd1c6eb722a21b6ce1eee1213ec856170a2f256d99f3d2978f054f5a
F src/os_win.c 2ed170fb6dba67952b7f07dfee71bb854463fb2fb51b0289fce5dec0fd075b0f
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 9656ad4e8331efb8a4f94f7a0c6440b98caea073950a367ea0c728a53b8e62c9
F src/pager.h 4b1140d691860de0be1347474c51fee07d5420bd7f802d38cbab8ea4ab9f538a
@@ -1702,7 +1702,7 @@ F test/swarmvtab2.test c948cb2fdfc5b01d85e8f6d6504854202dc1a0782ab2a0ed61538f27c
F test/swarmvtab3.test 41a3ab47cb7a834d4e5336425103b617410a67bb95d335ef536f887587ece073
F test/swarmvtabfault.test 8a67a9f27c61073a47990829e92bc0c64420a807cb642b15a25f6c788210ed95
F test/symlink.test 4368af0e213dd6e726a6240a16f2bb96a5a58f83f2d5d60652f27547b28cbf06
F test/symlink2.test bf932ff7fe95c9dbb39d2a990df9098b0ea943233c97e40098e0a8d6b559a96f
F test/symlink2.test 0b7734533f198bbc46fb8ea984ffaea537c8ee949eaba8805a92ed9969573956
F test/sync.test 89539f4973c010eda5638407e71ca7fddbcd8e0594f4c9980229f804d4333092
F test/sync2.test 8f9f7d4f6d5be8ca8941a8dadcc4299e558cb6a1ff653a9469146c7a76ef2039
F test/syscall.test a067468b43b8cb2305e9f9fe414e5f40c875bb5d2cba5f00b8154396e95fcf37
@@ -1986,7 +1986,7 @@ F test/vtab_shared.test 5253bff2355a9a3f014c15337da7e177ab0ef8ad
F test/vtabdistinct.test 7688f0889358f849fd60bbfde1ded38b014b18066076d4bfbb75395804dfe072
F test/vtabdrop.test 65d4cf6722972e5499bdaf0c0d70ee3b8133944a4e4bc31862563f32a7edca12
F test/vtabrhs1.test 9b5ecbc74a689500c33a4b2b36761f9bcc22fcc4e3f9d21066ee0c9c74cf5f6c
F test/wal.test 519c550255c78f55959e9159b93ebbfad2b4e9f36f5b76284da41f572f9d27da
F test/wal.test 3628a18ed2ba1cad58978802381f89e6076d225d5c93836d3eed464f867fa288
F test/wal2.test e89ca97593b5e92849039f6b68ce1719a853ef20fa22c669ec1ac452fbc31cab
F test/wal3.test 5de023bb862fd1eb9d2ad26fa8d9c43abb5370582e5b08b2ae0d6f93661bc310
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
@@ -2199,8 +2199,11 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh 49a486c5069de041aedcbde4de178293e0463ae9918ecad7539eedf0ec77a139
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 3d6ae13805bdba4c73b7443f20073264cdd157299cb911228600e1528a136bb1
R f112fd2beffd3eb814301c3676676b3e
U drh
Z 113c5b5aab1719624bc6c7ebe0ae9b2d
P 0fe1622cec95b7ebecc127ee57a08113d3da1dadbe72c03a13d6751b3043e50f
R ee8b39ab71f10eb6a0db7f5e0c48bbd2
T *branch * win32-enable-setlk
T *sym-win32-enable-setlk *
T -sym-trunk *
U dan
Z e8f9e025559e2dfd331c377d699a5f1c
# Remove this line to create a well-formed Fossil manifest.

View File

@@ -1 +1 @@
0fe1622cec95b7ebecc127ee57a08113d3da1dadbe72c03a13d6751b3043e50f
737ca8a9fb9dc74b28f2186d93c5101463497445d0fabba3def61fee29abf2c8

View File

@@ -314,22 +314,12 @@ static int winMutexTry(sqlite3_mutex *p){
/*
** The sqlite3_mutex_try() routine is very rarely used, and when it
** is used it is merely an optimization. So it is OK for it to always
** fail.
**
** The TryEnterCriticalSection() interface is only available on WinNT.
** And some windows compilers complain if you try to use it without
** first doing some #defines that prevent SQLite from building on Win98.
** For that reason, we will omit this optimization for now. See
** ticket #2685.
** fail on some platforms. But - it is required for ENABLE_SETLK_TIMEOUT
** builds.
*/
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0400
assert( winMutex_isInit==1 );
assert( winMutex_isNt>=-1 && winMutex_isNt<=1 );
if( winMutex_isNt<0 ){
winMutex_isNt = sqlite3_win32_is_nt();
}
assert( winMutex_isNt==0 || winMutex_isNt==1 );
if( winMutex_isNt && TryEnterCriticalSection(&p->mutex) ){
if( sqlite3_win32_is_nt() && TryEnterCriticalSection(&p->mutex) ){
#ifdef SQLITE_DEBUG
p->owner = tid;
p->nRef++;

View File

@@ -4290,7 +4290,6 @@ static int unixGetpagesize(void){
**
** nRef
**
** The following fields are read-only after the object is created:
**
** hShm
** zFilename

View File

@@ -287,6 +287,9 @@ struct winFile {
sqlite3_int64 mmapSize; /* Size of mapped region */
sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
#endif
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
unsigned iBusyTimeout; /* Wait this many millisec on locks */
#endif
};
/*
@@ -1453,6 +1456,9 @@ int sqlite3_win32_is_nt(void){
}
return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2;
#elif SQLITE_TEST
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
return 1;
#endif
return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2;
#else
/*
@@ -2540,6 +2546,73 @@ static BOOL winLockFile(
#endif
}
/*
** Lock a region of nByte bytes starting at offset offset of file phFile.
** Take an EXCLUSIVE lock if parameter bExclusive is true, or a SHARED lock
** otherwise. If nMs is greater than zero and the lock cannot be obtained
** immediately, block for that many ms before giving up.
**
** If parameter pMutex is not NULL, then
**
** This function returns SQLITE_OK if the lock is obtained successfully. If
** some other process holds the lock, SQLITE_BUSY is returned if nMs==0, or
** SQLITE_BUSY_TIMEOUT otherwise. Or, if an error occurs, SQLITE_IOERR.
*/
static int winLockFileTimeout(
LPHANDLE phFile,
sqlite3_mutex *pMutex,
DWORD offset,
DWORD nByte,
int bExcl,
int nMs
){
DWORD flags = LOCKFILE_FAIL_IMMEDIATELY | (bExcl?LOCKFILE_EXCLUSIVE_LOCK:0);
int rc = SQLITE_OK;
BOOL ret;
#if !defined(SQLITE_ENABLE_SETLK_TIMEOUT)
ret = winLockFile(phFile, flags, offset, 0, nByte, 0);
#else
if( !osIsNT() ){
ret = winLockFile(phFile, flags, offset, 0, nByte, 0);
}else{
OVERLAPPED ovlp;
memset(&ovlp, 0, sizeof(OVERLAPPED));
ovlp.Offset = offset;
if( nMs>0 ){
ovlp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if( ovlp.hEvent==NULL ){
return SQLITE_IOERR;
}
}
ret = osLockFileEx(*phFile, flags, 0, nByte, 0, &ovlp);
if( nMs>0 ){
if( !ret ){
DWORD res = WaitForSingleObject(ovlp.hEvent, (DWORD)nMs);
if( res==WAIT_OBJECT_0 ){
/* Successfully obtained the lock. */
ret = TRUE;
}else if( res==WAIT_TIMEOUT ){
/* Timeout */
rc = SQLITE_BUSY_TIMEOUT;
}else{
/* Some other error has occurred */
rc = SQLITE_IOERR;
}
}
CloseHandle(ovlp.hEvent);
}
}
#endif /* defined(SQLITE_ENABLE_SETLK_TIMEOUT) */
if( rc==SQLITE_OK && !ret ){
rc = SQLITE_BUSY;
}
return rc;
}
/*
** Unlock a file region.
*/
@@ -3640,6 +3713,22 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
return rc;
}
#endif
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
case SQLITE_FCNTL_LOCK_TIMEOUT: {
int iOld = pFile->iBusyTimeout;
#if SQLITE_ENABLE_SETLK_TIMEOUT==1
pFile->iBusyTimeout = *(int*)pArg;
#elif SQLITE_ENABLE_SETLK_TIMEOUT==2
pFile->iBusyTimeout = !!(*(int*)pArg);
#else
# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2"
#endif
*(int*)pArg = iOld;
return SQLITE_OK;
}
#endif
}
OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h));
return SQLITE_NOTFOUND;
@@ -3720,13 +3809,31 @@ static int winShmMutexHeld(void) {
**
** The following fields are read-only after the object is created:
**
** fid
** hFile
** zFilename
**
** Either winShmNode.mutex must be held or winShmNode.nRef==0 and
** winShmMutexHeld() is true when reading or writing any other field
** in this structure.
**
** aMutex[SQLITE_SHM_NLOCK]:
** Normally, when SQLITE_ENABLE_SETLK_TIMEOUT is not defined, mutex
** winShmNode.mutex is used to serialize calls to the xShmLock()
** method.
**
** For SQLITE_ENABLE_SETLK_TIMEOUT builds, xShmLock() only takes the
** mutexes in the aMutex[] array that correspond to locks being taken
** or released. This means that:
**
** * Modifying the winShmNode.pFirst list requires holding *all*
** the locks in the aMutex[] array.
**
** * Reads and writes to winShm.sharedMask and winShm.exclMask must
** use AtomicLoad() and AtomicStore(). This is because it may be
** read by other threads while it is being modified.
**
** TODO: winShmNode.mutex is held for the space of time when LockFileEx()
** is called on winShmNode.hFile.
*/
struct winShmNode {
sqlite3_mutex *mutex; /* Mutex to access this object */
@@ -3747,11 +3854,38 @@ struct winShmNode {
int nRef; /* Number of winShm objects pointing to this */
winShm *pFirst; /* All winShm objects pointing to this */
winShmNode *pNext; /* Next in list of all winShmNode objects */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
sqlite3_mutex *aMutex[SQLITE_SHM_NLOCK];
#endif
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
u8 nextShmId; /* Next available winShm.id value */
#endif
};
/*
** Enter/leave the mutex required to modify the winShmNode.pFirst list.
*/
static void winShmListMutexEnter(winShmNode *pShmNode){
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
int ii;
for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
sqlite3_mutex_enter(pShmNode->aMutex[ii]);
}
#else
sqlite3_mutex_enter(pShmNode->mutex);
#endif
}
static void winShmListMutexLeave(winShmNode *pShmNode){
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
int ii;
for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
sqlite3_mutex_leave(pShmNode->aMutex[ii]);
}
#else
sqlite3_mutex_leave(pShmNode->mutex);
#endif
}
/*
** A global array of all winShmNode objects.
**
@@ -3796,39 +3930,48 @@ struct winShm {
#define WINSHM_RDLCK 2
#define WINSHM_WRLCK 3
static int winShmSystemLock(
winShmNode *pFile, /* Apply locks to this open shared-memory segment */
winFile *pDbFd, /* Apply locks to this open shared-memory segment */
int lockType, /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */
int ofst, /* Offset to first byte to be locked/unlocked */
int nByte /* Number of bytes to lock or unlock */
){
winShmNode *pShmNode = pDbFd->pShm->pShmNode;
int rc = 0; /* Result code form Lock/UnlockFileEx() */
/* Access to the winShmNode object is serialized by the caller */
assert( pFile->nRef==0 || sqlite3_mutex_held(pFile->mutex) );
/* assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->mutex) ); */
OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n",
pFile->hFile.h, lockType, ofst, nByte));
pShmNode->hFile.h, lockType, ofst, nByte));
/* Release/Acquire the system-level lock */
if( lockType==WINSHM_UNLCK ){
rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0);
int ret = winUnlockFile(&pShmNode->hFile.h, ofst, 0, nByte, 0);
if( ret==0 ){
pShmNode->lastErrno = osGetLastError();
rc = SQLITE_ERROR;
}
}else{
/* Initialize the locking parameters */
#if SQLITE_ENABLE_SETLK_TIMEOUT
rc = winLockFileTimeout(&pShmNode->hFile.h, pShmNode->mutex, ofst, nByte,
(lockType==WINSHM_WRLCK), pDbFd->iBusyTimeout);
#else
DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
if( lockType == WINSHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0);
}
rc = winLockFile(&pShmNode->hFile.h, dwFlags, ofst, 0, nByte, 0);
if( rc!=0 ){
rc = SQLITE_OK;
}else{
pFile->lastErrno = osGetLastError();
pShmNode->lastErrno = osGetLastError();
rc = SQLITE_BUSY;
}
#endif
}
OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n",
pFile->hFile.h, (lockType == WINSHM_UNLCK) ? "winUnlockFile" :
"winLockFile", pFile->lastErrno, sqlite3ErrName(rc)));
pShmNode->hFile.h, (lockType == WINSHM_UNLCK) ? "winUnlockFile" :
"winLockFile", pShmNode->lastErrno, sqlite3ErrName(rc)));
return rc;
}
@@ -3854,6 +3997,12 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
if( p->nRef==0 ){
int i;
if( p->mutex ){ sqlite3_mutex_free(p->mutex); }
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
/* Free the contents of the winShmNode.aMutex[] array */
for(i=0; i<SQLITE_SHM_NLOCK; i++){
sqlite3_mutex_free(p->aMutex[i]);
}
#endif
for(i=0; i<p->nRegion; i++){
BOOL bRc = osUnmapViewOfFile(p->aRegion[i].pMap);
OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n",
@@ -3886,34 +4035,35 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
}
/*
** The DMS lock has not yet been taken on shm file pShmNode. Attempt to
** take it now. Return SQLITE_OK if successful, or an SQLite error
** code otherwise.
** The DMS lock has not yet been taken on the shm file attached to
** pDbFd. Attempt to take it now. Return SQLITE_OK if successful, or an
** SQLite error code otherwise.
**
** If the DMS cannot be locked because this is a readonly_shm=1
** connection and no other process already holds a lock, return
** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1.
*/
static int winLockSharedMemory(winShmNode *pShmNode){
int rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1);
static int winLockSharedMemory(winFile *pDbFd){
winShmNode *pShmNode = pDbFd->pShm->pShmNode;
int rc = winShmSystemLock(pDbFd, WINSHM_WRLCK, WIN_SHM_DMS, 1);
if( rc==SQLITE_OK ){
if( pShmNode->isReadonly ){
pShmNode->isUnlocked = 1;
winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
winShmSystemLock(pDbFd, WINSHM_UNLCK, WIN_SHM_DMS, 1);
return SQLITE_READONLY_CANTINIT;
}else if( winTruncate((sqlite3_file*)&pShmNode->hFile, 0) ){
winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
winShmSystemLock(pDbFd, WINSHM_UNLCK, WIN_SHM_DMS, 1);
return winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
"winLockSharedMemory", pShmNode->zFilename);
}
}
if( rc==SQLITE_OK ){
winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
winShmSystemLock(pDbFd, WINSHM_UNLCK, WIN_SHM_DMS, 1);
}
return winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1);
return winShmSystemLock(pDbFd, WINSHM_RDLCK, WIN_SHM_DMS, 1);
}
/*
@@ -3975,6 +4125,20 @@ static int winOpenSharedMemory(winFile *pDbFd){
rc = SQLITE_IOERR_NOMEM_BKPT;
goto shm_open_err;
}
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
/* If SETLK_TIMEOUT is defined, also allocate the array of mutexes
** stored in pShmNode->aMutex[]. */
{
int ii;
for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
pShmNode->aMutex[ii] = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
if( pShmNode->aMutex[ii]==0 ){
rc = SQLITE_IOERR_NOMEM_BKPT;
goto shm_open_err;
}
}
}
#endif
}
if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
@@ -3992,7 +4156,10 @@ static int winOpenSharedMemory(winFile *pDbFd){
}
if( outFlags==SQLITE_OPEN_READONLY ) pShmNode->isReadonly = 1;
rc = winLockSharedMemory(pShmNode);
p->pShmNode = pShmNode;
pDbFd->pShm = p;
rc = winLockSharedMemory(pDbFd);
pDbFd->pShm = 0;
if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err;
}
@@ -4012,15 +4179,16 @@ static int winOpenSharedMemory(winFile *pDbFd){
** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex
** mutex.
*/
sqlite3_mutex_enter(pShmNode->mutex);
winShmListMutexEnter(pShmNode);
p->pNext = pShmNode->pFirst;
pShmNode->pFirst = p;
sqlite3_mutex_leave(pShmNode->mutex);
winShmListMutexLeave(pShmNode);
return rc;
/* Jump here on any error */
shm_open_err:
winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
winUnlockFile(&pShmNode->hFile.h, WIN_SHM_DMS, 0, 1, 0);
winShmPurge(pDbFd->pVfs, 0); /* This call frees pShmNode if required */
sqlite3_free(p);
sqlite3_free(pNew);
@@ -4048,14 +4216,14 @@ static int winShmUnmap(
/* Remove connection p from the set of connections associated
** with pShmNode */
sqlite3_mutex_enter(pShmNode->mutex);
winShmListMutexEnter(pShmNode);
for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){}
*pp = p->pNext;
winShmListMutexLeave(pShmNode);
/* Free the connection p */
sqlite3_free(p);
pDbFd->pShm = 0;
sqlite3_mutex_leave(pShmNode->mutex);
/* If pShmNode->nRef has reached 0, then close the underlying
** shared-memory file, too */
@@ -4084,7 +4252,7 @@ static int winShmLock(
winShm *pX; /* For looping over all siblings */
winShmNode *pShmNode;
int rc = SQLITE_OK; /* Result code */
u16 mask; /* Mask of locks to take or release */
u16 mask = (u16)((1U<<(ofst+n)) - (1U<<ofst)); /* Mask of locks to take/untake */
if( p==0 ) return SQLITE_IOERR_SHMLOCK;
pShmNode = p->pShmNode;
@@ -4098,84 +4266,168 @@ static int winShmLock(
|| flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
mask = (u16)((1U<<(ofst+n)) - (1U<<ofst));
assert( n>1 || mask==(1<<ofst) );
/* Check that, if this to be a blocking lock, no locks that occur later
** in the following list than the lock being obtained are already held:
**
** 1. Checkpointer lock (ofst==1).
** 2. Write lock (ofst==0).
** 3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
**
** In other words, if this is a blocking lock, none of the locks that
** occur later in the above list than the lock being obtained may be
** held.
**
** It is not permitted to block on the RECOVER lock.
*/
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
{
u16 lockMask = (p->exclMask|p->sharedMask);
assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
(ofst!=2) /* not RECOVER */
&& (ofst!=1 || lockMask==0 || lockMask==2)
&& (ofst!=0 || lockMask<3)
&& (ofst<3 || lockMask<(1<<ofst))
));
}
#endif
/* Check if there is any work to do. There are three cases:
**
** a) An unlock operation where there are locks to unlock,
** b) An shared lock where the requested lock is not already held
** c) An exclusive lock where the requested lock is not already held
**
** The SQLite core never requests an exclusive lock that it already holds.
** This is assert()ed below.
*/
assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)
|| 0==(p->exclMask & mask)
);
if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask))
|| (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask))
|| (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
){
/* Take the required mutexes. In SETLK_TIMEOUT mode (blocking locks), if
** this is an attempt on an exclusive lock use sqlite3_mutex_try(). If any
** other thread is holding this mutex, then it is either holding or about
** to hold a lock exclusive to the one being requested, and we may
** therefore return SQLITE_BUSY to the caller.
**
** Doing this prevents some deadlock scenarios. For example, thread 1 may
** be a checkpointer blocked waiting on the WRITER lock. And thread 2
** may be a normal SQL client upgrading to a write transaction. In this
** case thread 2 does a non-blocking request for the WRITER lock. But -
** if it were to use sqlite3_mutex_enter() then it would effectively
** become a (doomed) blocking request, as thread 2 would block until thread
** 1 obtained WRITER and released the mutex. Since thread 2 already holds
** a lock on a read-locking slot at this point, this breaks the
** anti-deadlock rules (see above). */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
int iMutex;
for(iMutex=ofst; iMutex<ofst+n; iMutex++){
if( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ){
rc = sqlite3_mutex_try(pShmNode->aMutex[iMutex]);
if( rc!=SQLITE_OK ) break;
}else{
sqlite3_mutex_enter(pShmNode->aMutex[iMutex]);
}
}
#else
sqlite3_mutex_enter(pShmNode->mutex);
#endif
if( rc==SQLITE_OK ){
if( flags & SQLITE_SHM_UNLOCK ){
/* Case (a) - unlock. */
u16 allMask = 0; /* Mask of locks held by siblings */
/* See if any siblings hold this same lock */
assert( (p->exclMask & p->sharedMask)==0 );
assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );
/* If this is a shared lock, check if any other connection in this
** process is holding the same shared lock. If one or more are, then
** do not unlock the system-level lock held on the file-handle. */
if( flags & SQLITE_SHM_SHARED ){
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
if( pX==p ) continue;
assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 );
allMask |= pX->sharedMask;
allMask |= AtomicLoad(&pX->sharedMask);
}
}
/* Unlock the system-level locks */
if( (mask & allMask)==0 ){
rc = winShmSystemLock(pShmNode, WINSHM_UNLCK, ofst+WIN_SHM_BASE, n);
}else{
rc = SQLITE_OK;
rc = winShmSystemLock(pDbFd, WINSHM_UNLCK, ofst+WIN_SHM_BASE, n);
}
/* Undo the local locks */
if( rc==SQLITE_OK ){
p->exclMask &= ~mask;
p->sharedMask &= ~mask;
AtomicStore(&p->exclMask, p->exclMask & ~mask);
AtomicStore(&p->sharedMask, p->sharedMask & ~mask);
}
}else if( flags & SQLITE_SHM_SHARED ){
u16 allShared = 0; /* Union of locks held by connections other than "p" */
/* Case (b) - a shared lock. */
int bLocked = 0; /* True if process already holds shared lock */
assert( n==1 );
/* Find out which shared locks are already held by sibling connections.
** If any sibling already holds an exclusive lock, go ahead and return
** SQLITE_BUSY.
*/
/* See what locks are held by other connections within this process. If
** any are holding an EXCLUSIVE lock, then this call will return
** SQLITE_BUSY. Or, if any are holding a SHARED lock, then there is no
** need to obtain a new system-level lock. */
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
if( (pX->exclMask & mask)!=0 ){
rc = SQLITE_BUSY;
break;
}
allShared |= pX->sharedMask;
}
/* Get shared locks at the system level, if necessary */
if( rc==SQLITE_OK ){
if( (allShared & mask)==0 ){
rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, ofst+WIN_SHM_BASE, n);
}else{
rc = SQLITE_OK;
u16 mExcl = AtomicLoad(&pX->exclMask);
u16 mShared = AtomicLoad(&pX->sharedMask);
if( mExcl & mask ) rc = SQLITE_BUSY;
if( mShared & mask ){
bLocked = 1;
}
}
/* Get the local shared locks */
if( rc==SQLITE_OK && bLocked==0 ){
rc = winShmSystemLock(pDbFd, WINSHM_RDLCK, ofst+WIN_SHM_BASE, n);
}
/* Get the local shared lock */
if( rc==SQLITE_OK ){
p->sharedMask |= mask;
AtomicStore(&p->sharedMask, p->sharedMask | mask);
}
}else{
/* Make sure no sibling connections hold locks that will block this
** lock. If any do, return SQLITE_BUSY right away.
*/
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){
rc = SQLITE_BUSY;
break;
}
}
/* Get the exclusive locks at the system level. Then if successful
** also mark the local connection as being locked.
*/
if( rc==SQLITE_OK ){
rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, ofst+WIN_SHM_BASE, n);
if( rc==SQLITE_OK ){
/* Case (c) - an exclusive lock. */
assert( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) );
assert( (p->sharedMask & mask)==0 );
p->exclMask |= mask;
assert( (p->exclMask & mask)==0 );
/* Make sure no sibling connections hold locks that will block this
** lock. If any do, return SQLITE_BUSY right away. */
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
u16 mExcl = AtomicLoad(&pX->exclMask);
u16 mShared = AtomicLoad(&pX->sharedMask);
if( (mExcl|mShared) & mask ) rc = SQLITE_BUSY;
}
/* Get the exclusive locks at the system level. If successful,
** also update the in-memory values. */
if( rc==SQLITE_OK ){
rc = winShmSystemLock(pDbFd, WINSHM_WRLCK, ofst+WIN_SHM_BASE, n);
if( rc==SQLITE_OK ){
AtomicStore(&p->exclMask, p->exclMask | mask);
}
}
}
}
/* Release the mutex(es) taken at the top of this block */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
for(iMutex--; iMutex>=ofst; iMutex--){
sqlite3_mutex_leave(pShmNode->aMutex[iMutex]);
}
#else
sqlite3_mutex_leave(pShmNode->mutex);
OSTRACE(("SHM-LOCK pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n",
osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask,
#endif
}
OSTRACE((
"SHM-LOCK(%d,%d,%d) pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n",
ofst, n, flags, osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask,
sqlite3ErrName(rc)));
return rc;
}
@@ -4238,7 +4490,7 @@ static int winShmMap(
sqlite3_mutex_enter(pShmNode->mutex);
if( pShmNode->isUnlocked ){
rc = winLockSharedMemory(pShmNode);
rc = winLockSharedMemory(pDbFd);
if( rc!=SQLITE_OK ) goto shmpage_out;
pShmNode->isUnlocked = 0;
}

View File

@@ -37,10 +37,11 @@ proc canCreateWin32Symlink {} {
set link [file join $::testdir lnk[pid].sym]
if {[file exists $link]} { return 0 }
set target [info nameofexecutable]
if {[catch {createWin32Symlink $link $target}] == 0} {
if {[catch {createWin32Symlink $link $target} msg] == 0} {
deleteWin32Symlink $link
return 1
}
puts $msg
return 0
}

View File

@@ -506,7 +506,7 @@ do_multiclient_test tn {
do_test wal-10.$tn.6 {
sql3 {SELECT * FROM t1}
} {1 2 3 4 5 6}
do_test wal-10.$tn.7 {
do_test wal-10.$tn.7a {
sql2 COMMIT
} {}
@@ -521,7 +521,7 @@ do_multiclient_test tn {
# to the database (as it is not locked and [db] is reading the latest
# snapshot).
#
do_test wal-10.$tn.7 {
do_test wal-10.$tn.7b {
sql2 { BEGIN; INSERT INTO t1 VALUES(7, 8) ; }
catchsql { INSERT INTO t1 VALUES(9, 10) }
} {1 {database is locked}}