From e1d4680fdd279e614c976a607af61de14af737bf Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 28 Apr 2026 20:01:03 +0000 Subject: [PATCH] Updates and simplifications to the mutex implementation on Windows. Uses Slim Reader/Writer Locks for performance, for non-reentrant mutexes. Requires WindowsVista or later. FossilOrigin-Name: a75aa5400f94531b8ffd00be4580b0578d8d81ba9681fd06b1d9d96a6180994c --- manifest | 15 +++-- manifest.tags | 4 +- manifest.uuid | 2 +- src/mutex_w32.c | 145 +++++++++++++++++++----------------------------- 4 files changed, 70 insertions(+), 96 deletions(-) diff --git a/manifest b/manifest index a04e53a9b5..eb22c21d1b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Attempt\sto\salign\sinstances\sof\sthe\ssqlite3_mutex\sobject\sat\s128-byte\sboundaries\nto\sprevent\sfalse-sharing\sin\smulti-core\smachines.\sSee\sthe\sdiscussion\sat\sand\naround\s[forum:/forumpost/2026-03-25T23:15:03Z|forum\spost\s2026-03-25T23:15:03Z]. -D 2026-04-28T15:12:40.363 +C Updates\sand\ssimplifications\sto\sthe\smutex\simplementation\son\sWindows.\s\sUses\nSlim\sReader/Writer\sLocks\sfor\sperformance,\sfor\snon-reentrant\smutexes.\nRequires\sWindowsVista\sor\slater. +D 2026-04-28T20:01:03.205 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -713,7 +713,7 @@ F src/mutex.c 00b8cee206a67fd764d001f3a148494331d8d0b3b9c3974ecd69ff29bb444462 F src/mutex.h a7b2293c48db5f27007c3bdb21d438873637d12658f5a0bf8ad025bb96803c4a F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4 F src/mutex_unix.c 9dce885059f9d2bb226e6e03bb2b41af4b89d40c323f908b58f5d05ff0565884 -F src/mutex_w32.c cd49a5772f1bfd174e22a0c783ba514a871a3a7089f90d7337019c32fecca94b +F src/mutex_w32.c 6eb3fc28172d267c4a6a92ef09f557ac8bcebc01ac5638a2bb528c492011dff6 F src/notify.c 57c2d1a2805d6dee32acd5d250d928ab94e02d76369ae057dee7d445fd64e878 F src/os.c 509452169d5ea739723e213b8e2481cf0e587f0e88579a912d200db5269f5f6d F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 @@ -2203,8 +2203,11 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P d64a1dbe0fb2d9286806d833a3146b21d5bf1636a650d5a64cf163c7f2356e98 -R e7f5576deb3afe7b12491989f716c8e2 +P 1786fcd5b4ee6cd9b4780f3687dfaec5b90ef0476e0da266a94e069b98e70514 +R 86e1ad059a85bf06401a6419341a6e82 +T *branch * win-mutex-revamp +T *sym-win-mutex-revamp * +T -sym-trunk * U drh -Z 8f2b29d8666e7ca0a228448a291e99d6 +Z 2cf5e98ef789e1ee6f49efcdd7196e10 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.tags b/manifest.tags index bec971799f..3424048c52 100644 --- a/manifest.tags +++ b/manifest.tags @@ -1,2 +1,2 @@ -branch trunk -tag trunk +branch win-mutex-revamp +tag win-mutex-revamp diff --git a/manifest.uuid b/manifest.uuid index 33b63b5768..c25a81020a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1786fcd5b4ee6cd9b4780f3687dfaec5b90ef0476e0da266a94e069b98e70514 +a75aa5400f94531b8ffd00be4580b0578d8d81ba9681fd06b1d9d96a6180994c diff --git a/src/mutex_w32.c b/src/mutex_w32.c index a888e24100..3fef1f8be4 100644 --- a/src/mutex_w32.c +++ b/src/mutex_w32.c @@ -37,6 +37,8 @@ # define ALIGN128 #endif +#pragma warning(push) +#pragma warning(disable: 4324) /* ** Each SQLite mutex is an instance of the following structure. ** @@ -47,8 +49,11 @@ ** that 64 works just as well most of the time, but the internet says ** that 128-byte alignment works better. */ -struct ALIGN128 sqlite3_mutex { - CRITICAL_SECTION mutex; /* Mutex controlling the lock */ +struct ALIGN128 sqlite3_mutex { + union { + CRITICAL_SECTION cs; /* The CRITICAL_SECTION mutex. id==1 */ + SRWLOCK srwl; /* The Slim reader/writer lock. id!=1 */ + } u; int id; /* Mutex type */ #ifdef SQLITE_DEBUG volatile int nRef; /* Number of entrances */ @@ -56,20 +61,7 @@ struct ALIGN128 sqlite3_mutex { volatile LONG trace; /* True to trace changes */ #endif }; - -/* -** These are the initializer values used when declaring a "static" mutex -** on Win32. It should be noted that all mutexes require initialization -** on the Win32 platform. -*/ -#define SQLITE_W32_MUTEX_INITIALIZER { 0 } - -#ifdef SQLITE_DEBUG -#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id, \ - 0L, (DWORD)0, 0 } -#else -#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id } -#endif +#pragma warning(pop) #ifdef SQLITE_DEBUG /* @@ -80,13 +72,8 @@ static int winMutexHeld(sqlite3_mutex *p){ return p->nRef!=0 && p->owner==GetCurrentThreadId(); } -static int winMutexNotheld2(sqlite3_mutex *p, DWORD tid){ - return p->nRef==0 || p->owner!=tid; -} - static int winMutexNotheld(sqlite3_mutex *p){ - DWORD tid = GetCurrentThreadId(); - return winMutexNotheld2(p, tid); + return p->nRef==0 || p->owner!=GetCurrentThreadId(); } #endif @@ -110,23 +97,8 @@ void sqlite3MemoryBarrier(void){ /* ** Initialize and deinitialize the mutex subsystem. */ -static sqlite3_mutex winMutex_staticMutexes[] = { - SQLITE3_MUTEX_INITIALIZER(2), - SQLITE3_MUTEX_INITIALIZER(3), - SQLITE3_MUTEX_INITIALIZER(4), - SQLITE3_MUTEX_INITIALIZER(5), - SQLITE3_MUTEX_INITIALIZER(6), - SQLITE3_MUTEX_INITIALIZER(7), - SQLITE3_MUTEX_INITIALIZER(8), - SQLITE3_MUTEX_INITIALIZER(9), - SQLITE3_MUTEX_INITIALIZER(10), - SQLITE3_MUTEX_INITIALIZER(11), - SQLITE3_MUTEX_INITIALIZER(12), - SQLITE3_MUTEX_INITIALIZER(13) -}; - +static sqlite3_mutex winMutex_staticMutexes[12]; static int winMutex_isInit = 0; -static int winMutex_isNt = -1; /* <0 means "need to query" */ /* As the winMutexInit() and winMutexEnd() functions are called as part ** of the sqlite3_initialize() and sqlite3_shutdown() processing, the @@ -142,7 +114,14 @@ static int winMutexInit(void){ if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){ int i; for(i=0; iid = i+2; + InitializeSRWLock(&p->u.srwl); +#ifdef SQLITE_DEBUG + p->nRef = 0; + p->owner = 0; + p->trace = 0; +#endif } winMutex_isInit = 1; }else{ @@ -160,10 +139,6 @@ static int winMutexEnd(void){ ** (which should be the last to shutdown.) */ if( InterlockedCompareExchange(&winMutex_lock, 0, 1)==1 ){ if( winMutex_isInit==1 ){ - int i; - for(i=0; itrace = 1; #endif #endif - InitializeCriticalSection(&p->mutex); + if( iType==SQLITE_MUTEX_RECURSIVE ){ + InitializeCriticalSection(&p->u.cs); + }else{ + InitializeSRWLock(&p->u.srwl); + } } break; } @@ -265,8 +244,10 @@ static sqlite3_mutex *winMutexAlloc(int iType){ static void winMutexFree(sqlite3_mutex *p){ assert( p ); assert( p->nRef==0 && p->owner==0 ); - if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ){ - DeleteCriticalSection(&p->mutex); + if( p->id==SQLITE_MUTEX_FAST ){ + sqlite3_free(p); + }else if( p->id==SQLITE_MUTEX_RECURSIVE ){ + DeleteCriticalSection(&p->u.cs); sqlite3_free(p); }else{ #ifdef SQLITE_ENABLE_API_ARMOR @@ -287,67 +268,56 @@ static void winMutexFree(sqlite3_mutex *p){ ** more than once, the behavior is undefined. */ static void winMutexEnter(sqlite3_mutex *p){ -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) - DWORD tid = GetCurrentThreadId(); -#endif -#ifdef SQLITE_DEBUG - assert( p ); - assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); -#else assert( p ); +#ifdef SQLITE_DEBUG + assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld(p) ); #endif assert( winMutex_isInit==1 ); - EnterCriticalSection(&p->mutex); + if( p->id==SQLITE_MUTEX_RECURSIVE ){ + EnterCriticalSection(&p->u.cs); + }else{ + AcquireSRWLockExclusive(&p->u.srwl); + } #ifdef SQLITE_DEBUG assert( p->nRef>0 || p->owner==0 ); - p->owner = tid; + p->owner = GetCurrentThreadId(); p->nRef++; if( p->trace ){ OSTRACE(("ENTER-MUTEX tid=%lu, mutex(%d)=%p (%d), nRef=%d\n", - tid, p->id, p, p->trace, p->nRef)); + GetCurrentThreadId(), p->id, p, p->trace, p->nRef)); } #endif } static int winMutexTry(sqlite3_mutex *p){ -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) - DWORD tid = GetCurrentThreadId(); -#endif int rc = SQLITE_BUSY; assert( p ); - assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); + assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld(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 + ** The sqlite3_mutex_try() routine is seldom 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. */ -#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(); + if( p->id==SQLITE_MUTEX_RECURSIVE ){ + rc = TryEnterCriticalSection(&p->u.cs); + }else{ + rc = TryAcquireSRWLockExclusive(&p->u.srwl); } - assert( winMutex_isNt==0 || winMutex_isNt==1 ); - if( winMutex_isNt && TryEnterCriticalSection(&p->mutex) ){ + if( rc ){ #ifdef SQLITE_DEBUG - p->owner = tid; + p->owner = GetCurrentThreadId(); p->nRef++; #endif rc = SQLITE_OK; + }else{ + rc = SQLITE_BUSY; } -#else - UNUSED_PARAMETER(p); -#endif #ifdef SQLITE_DEBUG if( p->trace ){ - OSTRACE(("TRY-MUTEX tid=%lu, mutex(%d)=%p (%d), owner=%lu, nRef=%d, rc=%s\n", - tid, p->id, p, p->trace, p->owner, p->nRef, sqlite3ErrName(rc))); + OSTRACE(( + "TRY-MUTEX tid=%lu, mutex(%d)=%p (%d), owner=%lu, nRef=%d, rc=%s\n", + GetCurrentThreadId(), p->id, p, p->trace, p->owner, p->nRef, + sqlite3ErrName(rc))); } #endif return rc; @@ -360,23 +330,24 @@ static int winMutexTry(sqlite3_mutex *p){ ** is not currently allocated. SQLite will never do either. */ static void winMutexLeave(sqlite3_mutex *p){ -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) - DWORD tid = GetCurrentThreadId(); -#endif assert( p ); #ifdef SQLITE_DEBUG assert( p->nRef>0 ); - assert( p->owner==tid ); + assert( p->owner==GetCurrentThreadId() ); p->nRef--; if( p->nRef==0 ) p->owner = 0; assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); #endif assert( winMutex_isInit==1 ); - LeaveCriticalSection(&p->mutex); + if( p->id==SQLITE_MUTEX_RECURSIVE ){ + LeaveCriticalSection(&p->u.cs); + }else{ + ReleaseSRWLockExclusive(&p->u.srwl); + } #ifdef SQLITE_DEBUG if( p->trace ){ OSTRACE(("LEAVE-MUTEX tid=%lu, mutex(%d)=%p (%d), nRef=%d\n", - tid, p->id, p, p->trace, p->nRef)); + GetCurrentThreadId(), p->id, p, p->trace, p->nRef)); } #endif } -- 2.47.3