1
0
mirror of https://github.com/sqlite/sqlite.git synced 2026-01-06 08:01:16 +03:00

On windows, when opening a UNC path, fall back to using a single file handle shared between all connections for locking.

FossilOrigin-Name: 5c0202d96c3a20a2cbcd38eba5e62371606894a0cbc2da4f60e10a1b5fa7bd04
This commit is contained in:
dan
2025-08-12 17:55:34 +00:00
parent 9081b89720
commit b1406a6550
6 changed files with 172 additions and 62 deletions

View File

@@ -1,5 +1,5 @@
C Replace\ssome\s32-bit\sarithmetic\sin\sfts3_write.c\swith\s64-bit\sto\savoid\sthe\spossibility\sof\sinteger\soverflow.
D 2025-08-11T10:54:39.636
C On\swindows,\swhen\sopening\sa\sUNC\spath,\sfall\sback\sto\susing\sa\ssingle\sfile\shandle\sshared\sbetween\sall\sconnections\sfor\slocking.
D 2025-08-12T17:55:34.071
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -727,7 +727,7 @@ F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e
F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a
F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107
F src/os_unix.c 690107e26cc4e9809eeb9826c0efdbff4a42b9cc59d0f0b855ca3e6021e1ae73
F src/os_win.c 7ac69df49d2ff0432b9c96fd2d9a17a100cced6860479e584cd3337e18d09334
F src/os_win.c f81a7cffdfe8c593a840895b3f64290714f0186b06302d2c397012252d830374
F src/os_win.h 4c247cdb6d407c75186c94a1e84d5a22cbae4adcec93fcae8d2bc1f956fd1f19
F src/pager.c 23c0f17deb892da6b32fef1f465507df7ab5cd01d774288cb43695658a649259
F src/pager.h 6137149346e6c8a3ddc1eeb40aee46381e9bc8b0fcc6dda8a1efde993c2275b8
@@ -752,7 +752,7 @@ F src/status.c 0e72e4f6be6ccfde2488eb63210297e75f569f3ce9920f6c3d77590ec6ce5ffd
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/tclsqlite.c 3c604c49e6cf4211960a9ddb9505280fd22cde32175f40884c641c0f5a286036
F src/tclsqlite.h 65e2c761446e1c9fa0342b7d2612a703483643c8b6a316d12a65b745a4727395
F src/test1.c 13cc07851f989141b29f7ca3c6c90f6d18f90081ab423c66716c8cb29d277d1f
F src/test1.c c55dee15ea54c6e012df823cddbec068291adfa46c6e837a8fc2d3729dd14d7e
F src/test2.c 62f0830958f9075692c29c6de51b495ae8969e1bef85f239ffcd9ba5fb44a5ff
F src/test3.c 432646f581d8af1bb495e58fc98234380250954f5d5535e507fc785eccc3987a
F src/test4.c 0ac87fc13cdb334ab3a71823f99b6c32a6bebe5d603cd6a71d84c823d43a25a0
@@ -1493,7 +1493,7 @@ F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/pendingrace.test e99efc5ab3584da3dfc8cd6a0ec4e5a42214820574f5ea24ee93f1d84655f463
F test/percentile.test 52ba89d6ee6b65f770972b67dace358bab7cdbd532803d3db157845268e789cd
F test/permutations.test 5260363b43b3fad4becb49b58d0b53e6024e1c18dc169b154d0b98db5630bbf2
F test/permutations.test e6de4f5777f7785737ac3d1d964b8656e5477a134665b2fe8a91884ab9b685b3
F test/pg_common.tcl 3b27542224db1e713ae387459b5d117c836a5f6e328846922993b6d2b7640d9f
F test/pragma.test 7d07b7bb76e273215d6a20c4f83c3062cc28976c737ccb70a686025801e86c8f
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
@@ -1693,7 +1693,7 @@ F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
F test/tester.tcl 463ae33b8bf75ac77451df19bd65e7c415c2e9891227c7c9e657d0a2d8e1074a
F test/testrunner.tcl b42f7736968cafc9e69bb5d0b87fc81b375fb4c3f44e19b472cccd91d41a416a x
F test/testrunner_data.tcl 02dd645b647d907c959fbf232b7ff7d869c2ae430d5117443fc1e16a0d32243a
F test/testrunner_data.tcl c507a9afa911c03446ed90442ffd4a98aca02882c3d51bd1177c24795674def8
F test/testrunner_estwork.tcl 7927a84327259a32854926f68a75292e33a61e7e052fdbfcb01f18696c99c724
F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899
F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502
@@ -2169,8 +2169,11 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 8c9db6237154d1c153916ed821f576f91b353bf988182127d2a619506707d6bd
R 46dd406e90e99834a1b4765a422bd8ef
P 6711110b1c7589311f012deee4d4dd5b771fa44ad328b471c9ef583960795199
R 9c8bd9825359e182db79f50fb2b7a597
T *branch * win-unc-fix
T *sym-win-unc-fix *
T -sym-trunk *
U dan
Z a860898d6545bf8b50fdb8dafd2d9aa9
Z 86752bc54d7f0a99414d23fa683cb261
# Remove this line to create a well-formed Fossil manifest.

View File

@@ -1 +1 @@
6711110b1c7589311f012deee4d4dd5b771fa44ad328b471c9ef583960795199
5c0202d96c3a20a2cbcd38eba5e62371606894a0cbc2da4f60e10a1b5fa7bd04

View File

@@ -4123,29 +4123,35 @@ static int winShmMutexHeld(void) {
** log-summary is opened only once per process.
**
** winShmMutexHeld() must be true when creating or destroying
** this object or while reading or writing the following fields:
** this object, or while editing the global linked list that starts
** at winShmNodeList.
**
** nRef
** pNext
** When reading or writing the linked list starting at winShmNode.pWinShmList,
** pShmNode->mutex must be held.
**
** The following fields are read-only after the object is created:
** The following fields are constant after the object is created:
**
** zFilename
** hSharedShm
** mutex
** bUseSharedLockHandle
**
** Either winShmNode.mutex must be held or winShmNode.nRef==0 and
** Either winShmNode.mutex must be held or winShmNode.pWinShmList==0 and
** winShmMutexHeld() is true when reading or writing any other field
** in this structure.
**
** File-handle hSharedShm is used to (a) take the DMS lock, (b) truncate
** the *-shm file if the DMS-locking protocol demands it, and (c) map
** regions of the *-shm file into memory using MapViewOfFile() or
** similar. Other locks are taken by individual clients using the
** winShm.hShm handles.
** File-handle hSharedShm is always used to (a) take the DMS lock, (b)
** truncate the *-shm file if the DMS-locking protocol demands it, and
** (c) map regions of the *-shm file into memory using MapViewOfFile()
** or similar. If bUseSharedLockHandle is true, then other locks are also
** taken on hSharedShm. Or, if bUseSharedLockHandle is false, then other
** locks are taken using each connection's winShm.hShm handles.
*/
struct winShmNode {
sqlite3_mutex *mutex; /* Mutex to access this object */
char *zFilename; /* Name of the file */
HANDLE hSharedShm; /* File handle open on zFilename */
int bUseSharedLockHandle; /* True to use hSharedShm for everything */
int isUnlocked; /* DMS lock has not yet been obtained */
int isReadonly; /* True if read-only */
@@ -4158,7 +4164,8 @@ struct winShmNode {
} *aRegion;
DWORD lastErrno; /* The Windows errno from the last I/O error */
int nRef; /* Number of winShm objects pointing to this */
winShm *pWinShmList; /* List of winShm objects with ptrs to this */
winShmNode *pNext; /* Next in list of all winShmNode objects */
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
u8 nextShmId; /* Next available winShm.id value */
@@ -4186,6 +4193,7 @@ struct winShm {
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
u8 id; /* Id of this connection with its winShmNode */
#endif
winShm *pWinShmNext; /* Next winShm object on same winShmNode */
};
/*
@@ -4199,7 +4207,7 @@ static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*);
static int winDelete(sqlite3_vfs *,const char*,int);
/*
** Purge the winShmNodeList list of all entries with winShmNode.nRef==0.
** Purge the winShmNodeList list of all entries with winShmNode.pWinShmList==0.
**
** This is not a VFS shared-memory method; it is a utility function called
** by VFS shared-memory methods.
@@ -4212,7 +4220,7 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
osGetCurrentProcessId(), deleteFlag));
pp = &winShmNodeList;
while( (p = *pp)!=0 ){
if( p->nRef==0 ){
if( p->pWinShmList==0 ){
int i;
if( p->mutex ){ sqlite3_mutex_free(p->mutex); }
for(i=0; i<p->nRegion; i++){
@@ -4376,6 +4384,60 @@ static int winHandleOpen(
return rc;
}
/*
** Close pDbFd's connection to shared-memory. Delete the underlying
** *-shm file if deleteFlag is true.
*/
static int winCloseSharedMemory(winFile *pDbFd, int deleteFlag){
winShm *p; /* The connection to be closed */
winShm **pp; /* Iterator for pShmNode->pWinShmList */
winShmNode *pShmNode; /* The underlying shared-memory file */
p = pDbFd->pShm;
if( p==0 ) return SQLITE_OK;
if( p->hShm!=INVALID_HANDLE_VALUE ){
osCloseHandle(p->hShm);
}
winShmEnterMutex();
pShmNode = p->pShmNode;
/* Remove this connection from the winShmNode.pWinShmList list */
sqlite3_mutex_enter(pShmNode->mutex);
for(pp=&pShmNode->pWinShmList; *pp!=p; pp=&(*pp)->pWinShmNext){}
*pp = p->pWinShmNext;
sqlite3_mutex_leave(pShmNode->mutex);
winShmPurge(pDbFd->pVfs, deleteFlag);
winShmLeaveMutex();
/* Free the connection p */
sqlite3_free(p);
pDbFd->pShm = 0;
return SQLITE_OK;
}
/*
** testfixture builds may set this global variable to true via a
** Tcl interface. This forces the VFS to use the locking normally
** only used for UNC paths for all files.
*/
#ifdef SQLITE_TEST
int sqlite3_win_test_unc_locking = 0;
#else
# define sqlite3_win_test_unc_locking 0
#endif
/*
** Return true if the string passed as the only argument is likely
** to be a UNC path. In other words, if it starts with "\\".
*/
static int winIsUNCPath(const char *zFile){
if( zFile[0]=='\\' && zFile[1]=='\\' ){
return 1;
}
return sqlite3_win_test_unc_locking;
}
/*
** Open the shared-memory area associated with database file pDbFd.
@@ -4402,15 +4464,10 @@ static int winOpenSharedMemory(winFile *pDbFd){
pNew->zFilename = (char*)&pNew[1];
pNew->hSharedShm = INVALID_HANDLE_VALUE;
pNew->isUnlocked = 1;
pNew->bUseSharedLockHandle = winIsUNCPath(pDbFd->zPath);
sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
/* Open a file-handle on the *-shm file for this connection. This file-handle
** is only used for locking. The mapping of the *-shm file is created using
** the shared file handle in winShmNode.hSharedShm. */
p->bReadonly = sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0);
rc = winHandleOpen(pNew->zFilename, &p->bReadonly, &p->hShm);
/* Look to see if there is an existing winShmNode that can be used.
** If no matching winShmNode currently exists, then create a new one. */
winShmEnterMutex();
@@ -4431,7 +4488,7 @@ static int winOpenSharedMemory(winFile *pDbFd){
/* Open a file-handle to use for mappings, and for the DMS lock. */
if( rc==SQLITE_OK ){
HANDLE h = INVALID_HANDLE_VALUE;
pShmNode->isReadonly = p->bReadonly;
pShmNode->isReadonly = sqlite3_uri_boolean(pDbFd->zPath,"readonly_shm",0);
rc = winHandleOpen(pNew->zFilename, &pShmNode->isReadonly, &h);
pShmNode->hSharedShm = h;
}
@@ -4453,20 +4510,35 @@ static int winOpenSharedMemory(winFile *pDbFd){
/* If no error has occurred, link the winShm object to the winShmNode and
** the winShm to pDbFd. */
if( rc==SQLITE_OK ){
sqlite3_mutex_enter(pShmNode->mutex);
p->pShmNode = pShmNode;
pShmNode->nRef++;
p->pWinShmNext = pShmNode->pWinShmList;
pShmNode->pWinShmList = p;
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
p->id = pShmNode->nextShmId++;
#endif
pDbFd->pShm = p;
sqlite3_mutex_leave(pShmNode->mutex);
}else if( p ){
winHandleClose(p->hShm);
sqlite3_free(p);
}
assert( rc!=SQLITE_OK || pShmNode->isUnlocked==0 || pShmNode->nRegion==0 );
winShmLeaveMutex();
sqlite3_free(pNew);
/* Open a file-handle on the *-shm file for this connection. This file-handle
** is only used for locking. The mapping of the *-shm file is created using
** the shared file handle in winShmNode.hSharedShm. */
if( rc==SQLITE_OK && pShmNode->bUseSharedLockHandle==0 ){
p->bReadonly = sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0);
rc = winHandleOpen(pShmNode->zFilename, &p->bReadonly, &p->hShm);
if( rc!=SQLITE_OK ){
assert( p->hShm==INVALID_HANDLE_VALUE );
winCloseSharedMemory(pDbFd, 0);
}
}
return rc;
}
@@ -4478,33 +4550,7 @@ static int winShmUnmap(
sqlite3_file *fd, /* Database holding shared memory */
int deleteFlag /* Delete after closing if true */
){
winFile *pDbFd; /* Database holding shared-memory */
winShm *p; /* The connection to be closed */
winShmNode *pShmNode; /* The underlying shared-memory file */
pDbFd = (winFile*)fd;
p = pDbFd->pShm;
if( p==0 ) return SQLITE_OK;
if( p->hShm!=INVALID_HANDLE_VALUE ){
osCloseHandle(p->hShm);
}
pShmNode = p->pShmNode;
winShmEnterMutex();
/* If pShmNode->nRef has reached 0, then close the underlying
** shared-memory file, too. */
assert( pShmNode->nRef>0 );
pShmNode->nRef--;
if( pShmNode->nRef==0 ){
winShmPurge(pDbFd->pVfs, deleteFlag);
}
winShmLeaveMutex();
/* Free the connection p */
sqlite3_free(p);
pDbFd->pShm = 0;
return SQLITE_OK;
return winCloseSharedMemory((winFile*)fd, deleteFlag);
}
/*
@@ -4573,6 +4619,7 @@ static int winShmLock(
|| (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask))
|| (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
){
HANDLE h = p->hShm;
if( flags & SQLITE_SHM_UNLOCK ){
/* Case (a) - unlock. */
@@ -4581,7 +4628,27 @@ static int winShmLock(
assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );
rc = winHandleUnlock(p->hShm, ofst+WIN_SHM_BASE, n);
assert( !(flags & SQLITE_SHM_SHARED) || n==1 );
if( pShmNode->bUseSharedLockHandle ){
h = pShmNode->hSharedShm;
if( flags & SQLITE_SHM_SHARED ){
winShm *pShm;
sqlite3_mutex_enter(pShmNode->mutex);
for(pShm=pShmNode->pWinShmList; pShm; pShm=pShm->pWinShmNext){
if( pShm!=p && (pShm->sharedMask & mask) ){
/* Another connection within this process is also holding this
** SHARED lock. So do not actually release the OS lock. */
h = INVALID_HANDLE_VALUE;
break;
}
}
sqlite3_mutex_leave(pShmNode->mutex);
}
}
if( h!=INVALID_HANDLE_VALUE ){
rc = winHandleUnlock(h, ofst+WIN_SHM_BASE, n);
}
/* If successful, also clear the bits in sharedMask/exclMask */
if( rc==SQLITE_OK ){
@@ -4591,7 +4658,32 @@ static int winShmLock(
}else{
int bExcl = ((flags & SQLITE_SHM_EXCLUSIVE) ? 1 : 0);
DWORD nMs = winFileBusyTimeout(pDbFd);
rc = winHandleLockTimeout(p->hShm, ofst+WIN_SHM_BASE, n, bExcl, nMs);
if( pShmNode->bUseSharedLockHandle ){
winShm *pShm;
h = pShmNode->hSharedShm;
sqlite3_mutex_enter(pShmNode->mutex);
for(pShm=pShmNode->pWinShmList; pShm; pShm=pShm->pWinShmNext){
if( bExcl ){
if( (pShm->sharedMask|pShm->exclMask) & mask ){
rc = SQLITE_BUSY;
h = INVALID_HANDLE_VALUE;
}
}else{
if( pShm->sharedMask & mask ){
h = INVALID_HANDLE_VALUE;
}else if( pShm->exclMask & mask ){
rc = SQLITE_BUSY;
h = INVALID_HANDLE_VALUE;
}
}
}
sqlite3_mutex_leave(pShmNode->mutex);
}
if( h!=INVALID_HANDLE_VALUE ){
rc = winHandleLockTimeout(h, ofst+WIN_SHM_BASE, n, bExcl, nMs);
}
if( rc==SQLITE_OK ){
if( bExcl ){
p->exclMask = (p->exclMask | mask);

View File

@@ -9158,6 +9158,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
extern int sqlite3_pager_readdb_count;
extern int sqlite3_pager_writedb_count;
extern int sqlite3_pager_writej_count;
extern int sqlite3_win_test_unc_locking;
#if SQLITE_OS_WIN
extern LONG volatile sqlite3_os_type;
#endif
@@ -9262,5 +9263,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
Tcl_LinkVar(interp, "sqlite_fts3_enable_parentheses",
(char*)&sqlite3_fts3_enable_parentheses, TCL_LINK_INT);
#endif
Tcl_LinkVar(interp, "sqlite3_win_test_unc_locking",
(char*)&sqlite3_win_test_unc_locking, TCL_LINK_INT);
return TCL_OK;
}

View File

@@ -1119,6 +1119,18 @@ test_suite "maindbname" -prefix "" -description {
dbconfig_maindbname_icecube $::dbhandle
}
test_suite "win_unc_locking" -prefix "" -description {
Run the "wal*" tests with UNC style single fd locking.
} -files [
test_set \
{*}[glob [file join $testdir wal*]] \
-exclude *fault* *malloc* *crash* *slow* walsetlk.test
] -initialize {
set sqlite3_win_test_unc_locking 1
} -shutdown {
set sqlite3_win_test_unc_locking 0
}
# End of tests
#############################################################################

View File

@@ -38,7 +38,7 @@ namespace eval trd {
set tcltest(win.Windows-Win32Heap) veryquick
set tcltest(win.Windows-Sanitize) veryquick
set tcltest(win.Windows-WinRT) veryquick
set tcltest(win.Default) full
set tcltest(win.Default) {full win_unc_locking}
# Extra [make xyz] tests that should be run for various builds.
#