drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 1 | /* |
| 2 | ** 2007 August 14 |
| 3 | ** |
| 4 | ** The author disclaims copyright to this source code. In place of |
| 5 | ** a legal notice, here is a blessing: |
| 6 | ** |
| 7 | ** May you do good and not evil. |
| 8 | ** May you find forgiveness for yourself and forgive others. |
| 9 | ** May you share freely, never taking more than you give. |
| 10 | ** |
| 11 | ************************************************************************* |
| 12 | ** This file contains the C functions that implement mutexes for win32 |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 13 | */ |
| 14 | #include "sqliteInt.h" |
| 15 | |
| 16 | /* |
| 17 | ** The code in this file is only used if we are compiling multithreaded |
| 18 | ** on a win32 system. |
| 19 | */ |
drh | c7ce76a | 2007-08-30 14:10:30 +0000 | [diff] [blame] | 20 | #ifdef SQLITE_MUTEX_W32 |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 21 | |
| 22 | /* |
| 23 | ** Each recursive mutex is an instance of the following structure. |
| 24 | */ |
| 25 | struct sqlite3_mutex { |
| 26 | CRITICAL_SECTION mutex; /* Mutex controlling the lock */ |
| 27 | int id; /* Mutex type */ |
| 28 | int nRef; /* Number of enterances */ |
| 29 | DWORD owner; /* Thread holding this mutex */ |
shaneh | 1f4222f | 2010-02-13 02:31:09 +0000 | [diff] [blame] | 30 | #ifdef SQLITE_DEBUG |
| 31 | int trace; /* True to trace changes */ |
| 32 | #endif |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 33 | }; |
shaneh | 1f4222f | 2010-02-13 02:31:09 +0000 | [diff] [blame] | 34 | #define SQLITE_W32_MUTEX_INITIALIZER { 0 } |
| 35 | #ifdef SQLITE_DEBUG |
| 36 | #define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, 0L, (DWORD)0, 0 } |
| 37 | #else |
| 38 | #define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, 0L, (DWORD)0 } |
| 39 | #endif |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 40 | |
| 41 | /* |
drh | df6a81c | 2007-09-05 14:30:42 +0000 | [diff] [blame] | 42 | ** Return true (non-zero) if we are running under WinNT, Win2K, WinXP, |
| 43 | ** or WinCE. Return false (zero) for Win95, Win98, or WinME. |
| 44 | ** |
| 45 | ** Here is an interesting observation: Win95, Win98, and WinME lack |
| 46 | ** the LockFileEx() API. But we can still statically link against that |
| 47 | ** API as long as we don't call it win running Win95/98/ME. A call to |
| 48 | ** this routine is used to determine if the host is Win95/98/ME or |
| 49 | ** WinNT/2K/XP so that we will know whether or not we can safely call |
| 50 | ** the LockFileEx() API. |
shane | b211183 | 2008-11-10 20:01:40 +0000 | [diff] [blame] | 51 | ** |
| 52 | ** mutexIsNT() is only used for the TryEnterCriticalSection() API call, |
| 53 | ** which is only available if your application was compiled with |
| 54 | ** _WIN32_WINNT defined to a value >= 0x0400. Currently, the only |
| 55 | ** call to TryEnterCriticalSection() is #ifdef'ed out, so #ifdef |
| 56 | ** this out as well. |
drh | df6a81c | 2007-09-05 14:30:42 +0000 | [diff] [blame] | 57 | */ |
shane | b211183 | 2008-11-10 20:01:40 +0000 | [diff] [blame] | 58 | #if 0 |
danielk1977 | 29bafea | 2008-06-26 10:41:19 +0000 | [diff] [blame] | 59 | #if SQLITE_OS_WINCE |
drh | df6a81c | 2007-09-05 14:30:42 +0000 | [diff] [blame] | 60 | # define mutexIsNT() (1) |
| 61 | #else |
| 62 | static int mutexIsNT(void){ |
| 63 | static int osType = 0; |
| 64 | if( osType==0 ){ |
| 65 | OSVERSIONINFO sInfo; |
| 66 | sInfo.dwOSVersionInfoSize = sizeof(sInfo); |
| 67 | GetVersionEx(&sInfo); |
| 68 | osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1; |
| 69 | } |
| 70 | return osType==2; |
| 71 | } |
danielk1977 | 29bafea | 2008-06-26 10:41:19 +0000 | [diff] [blame] | 72 | #endif /* SQLITE_OS_WINCE */ |
shane | b211183 | 2008-11-10 20:01:40 +0000 | [diff] [blame] | 73 | #endif |
drh | a418980 | 2008-06-19 16:07:07 +0000 | [diff] [blame] | 74 | |
| 75 | #ifdef SQLITE_DEBUG |
danielk1977 | 6d2ab0e | 2008-06-17 17:21:18 +0000 | [diff] [blame] | 76 | /* |
| 77 | ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are |
| 78 | ** intended for use only inside assert() statements. |
| 79 | */ |
| 80 | static int winMutexHeld(sqlite3_mutex *p){ |
| 81 | return p->nRef!=0 && p->owner==GetCurrentThreadId(); |
| 82 | } |
shaneh | 1f4222f | 2010-02-13 02:31:09 +0000 | [diff] [blame] | 83 | static int winMutexNotheld2(sqlite3_mutex *p, DWORD tid){ |
| 84 | return p->nRef==0 || p->owner!=tid; |
| 85 | } |
danielk1977 | 6d2ab0e | 2008-06-17 17:21:18 +0000 | [diff] [blame] | 86 | static int winMutexNotheld(sqlite3_mutex *p){ |
shaneh | 1f4222f | 2010-02-13 02:31:09 +0000 | [diff] [blame] | 87 | DWORD tid = GetCurrentThreadId(); |
| 88 | return winMutexNotheld2(p, tid); |
danielk1977 | 6d2ab0e | 2008-06-17 17:21:18 +0000 | [diff] [blame] | 89 | } |
drh | a418980 | 2008-06-19 16:07:07 +0000 | [diff] [blame] | 90 | #endif |
danielk1977 | 6d2ab0e | 2008-06-17 17:21:18 +0000 | [diff] [blame] | 91 | |
drh | df6a81c | 2007-09-05 14:30:42 +0000 | [diff] [blame] | 92 | |
| 93 | /* |
drh | 40257ff | 2008-06-13 18:24:27 +0000 | [diff] [blame] | 94 | ** Initialize and deinitialize the mutex subsystem. |
| 95 | */ |
shaneh | 1f4222f | 2010-02-13 02:31:09 +0000 | [diff] [blame] | 96 | static sqlite3_mutex winMutex_staticMutexes[6] = { |
| 97 | SQLITE3_MUTEX_INITIALIZER, |
| 98 | SQLITE3_MUTEX_INITIALIZER, |
| 99 | SQLITE3_MUTEX_INITIALIZER, |
| 100 | SQLITE3_MUTEX_INITIALIZER, |
| 101 | SQLITE3_MUTEX_INITIALIZER, |
| 102 | SQLITE3_MUTEX_INITIALIZER |
| 103 | }; |
shane | 61b82d6 | 2009-06-01 17:06:07 +0000 | [diff] [blame] | 104 | static int winMutex_isInit = 0; |
| 105 | /* As winMutexInit() and winMutexEnd() are called as part |
| 106 | ** of the sqlite3_initialize and sqlite3_shutdown() |
| 107 | ** processing, the "interlocked" magic is probably not |
| 108 | ** strictly necessary. |
| 109 | */ |
| 110 | static long winMutex_lock = 0; |
| 111 | |
| 112 | static int winMutexInit(void){ |
| 113 | /* The first to increment to 1 does actual initialization */ |
shane | 1987c8d | 2009-08-10 03:23:21 +0000 | [diff] [blame] | 114 | if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){ |
shane | 61b82d6 | 2009-06-01 17:06:07 +0000 | [diff] [blame] | 115 | int i; |
drh | 9ac0650 | 2009-08-17 13:42:29 +0000 | [diff] [blame] | 116 | for(i=0; i<ArraySize(winMutex_staticMutexes); i++){ |
shane | 61b82d6 | 2009-06-01 17:06:07 +0000 | [diff] [blame] | 117 | InitializeCriticalSection(&winMutex_staticMutexes[i].mutex); |
| 118 | } |
| 119 | winMutex_isInit = 1; |
| 120 | }else{ |
shane | 1987c8d | 2009-08-10 03:23:21 +0000 | [diff] [blame] | 121 | /* Someone else is in the process of initing the static mutexes */ |
shane | 61b82d6 | 2009-06-01 17:06:07 +0000 | [diff] [blame] | 122 | while( !winMutex_isInit ){ |
| 123 | Sleep(1); |
| 124 | } |
| 125 | } |
| 126 | return SQLITE_OK; |
| 127 | } |
| 128 | |
| 129 | static int winMutexEnd(void){ |
| 130 | /* The first to decrement to 0 does actual shutdown |
| 131 | ** (which should be the last to shutdown.) */ |
shane | 1987c8d | 2009-08-10 03:23:21 +0000 | [diff] [blame] | 132 | if( InterlockedCompareExchange(&winMutex_lock, 0, 1)==1 ){ |
shane | 61b82d6 | 2009-06-01 17:06:07 +0000 | [diff] [blame] | 133 | if( winMutex_isInit==1 ){ |
| 134 | int i; |
drh | 9ac0650 | 2009-08-17 13:42:29 +0000 | [diff] [blame] | 135 | for(i=0; i<ArraySize(winMutex_staticMutexes); i++){ |
shane | 61b82d6 | 2009-06-01 17:06:07 +0000 | [diff] [blame] | 136 | DeleteCriticalSection(&winMutex_staticMutexes[i].mutex); |
| 137 | } |
| 138 | winMutex_isInit = 0; |
| 139 | } |
| 140 | } |
| 141 | return SQLITE_OK; |
| 142 | } |
drh | 40257ff | 2008-06-13 18:24:27 +0000 | [diff] [blame] | 143 | |
| 144 | /* |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 145 | ** The sqlite3_mutex_alloc() routine allocates a new |
| 146 | ** mutex and returns a pointer to it. If it returns NULL |
| 147 | ** that means that a mutex could not be allocated. SQLite |
| 148 | ** will unwind its stack and return an error. The argument |
| 149 | ** to sqlite3_mutex_alloc() is one of these integer constants: |
| 150 | ** |
| 151 | ** <ul> |
shane | 7c7c311 | 2009-08-17 15:31:23 +0000 | [diff] [blame] | 152 | ** <li> SQLITE_MUTEX_FAST |
| 153 | ** <li> SQLITE_MUTEX_RECURSIVE |
| 154 | ** <li> SQLITE_MUTEX_STATIC_MASTER |
| 155 | ** <li> SQLITE_MUTEX_STATIC_MEM |
| 156 | ** <li> SQLITE_MUTEX_STATIC_MEM2 |
| 157 | ** <li> SQLITE_MUTEX_STATIC_PRNG |
| 158 | ** <li> SQLITE_MUTEX_STATIC_LRU |
| 159 | ** <li> SQLITE_MUTEX_STATIC_LRU2 |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 160 | ** </ul> |
| 161 | ** |
| 162 | ** The first two constants cause sqlite3_mutex_alloc() to create |
| 163 | ** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE |
| 164 | ** is used but not necessarily so when SQLITE_MUTEX_FAST is used. |
| 165 | ** The mutex implementation does not need to make a distinction |
| 166 | ** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does |
| 167 | ** not want to. But SQLite will only request a recursive mutex in |
| 168 | ** cases where it really needs one. If a faster non-recursive mutex |
| 169 | ** implementation is available on the host platform, the mutex subsystem |
| 170 | ** might return such a mutex in response to SQLITE_MUTEX_FAST. |
| 171 | ** |
| 172 | ** The other allowed parameters to sqlite3_mutex_alloc() each return |
shane | 7c7c311 | 2009-08-17 15:31:23 +0000 | [diff] [blame] | 173 | ** a pointer to a static preexisting mutex. Six static mutexes are |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 174 | ** used by the current version of SQLite. Future versions of SQLite |
| 175 | ** may add additional static mutexes. Static mutexes are for internal |
| 176 | ** use by SQLite only. Applications that use SQLite mutexes should |
| 177 | ** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or |
| 178 | ** SQLITE_MUTEX_RECURSIVE. |
| 179 | ** |
| 180 | ** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST |
| 181 | ** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() |
| 182 | ** returns a different mutex on every call. But for the static |
| 183 | ** mutex types, the same mutex is returned on every call that has |
| 184 | ** the same type number. |
| 185 | */ |
danielk1977 | 6d2ab0e | 2008-06-17 17:21:18 +0000 | [diff] [blame] | 186 | static sqlite3_mutex *winMutexAlloc(int iType){ |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 187 | sqlite3_mutex *p; |
| 188 | |
| 189 | switch( iType ){ |
| 190 | case SQLITE_MUTEX_FAST: |
| 191 | case SQLITE_MUTEX_RECURSIVE: { |
| 192 | p = sqlite3MallocZero( sizeof(*p) ); |
shane | 61b82d6 | 2009-06-01 17:06:07 +0000 | [diff] [blame] | 193 | if( p ){ |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 194 | p->id = iType; |
| 195 | InitializeCriticalSection(&p->mutex); |
| 196 | } |
| 197 | break; |
| 198 | } |
| 199 | default: { |
shane | ddfefca | 2009-06-01 17:10:22 +0000 | [diff] [blame] | 200 | assert( winMutex_isInit==1 ); |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 201 | assert( iType-2 >= 0 ); |
drh | 9ac0650 | 2009-08-17 13:42:29 +0000 | [diff] [blame] | 202 | assert( iType-2 < ArraySize(winMutex_staticMutexes) ); |
shane | 61b82d6 | 2009-06-01 17:06:07 +0000 | [diff] [blame] | 203 | p = &winMutex_staticMutexes[iType-2]; |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 204 | p->id = iType; |
| 205 | break; |
| 206 | } |
| 207 | } |
| 208 | return p; |
| 209 | } |
| 210 | |
| 211 | |
| 212 | /* |
| 213 | ** This routine deallocates a previously |
| 214 | ** allocated mutex. SQLite is careful to deallocate every |
| 215 | ** mutex that it allocates. |
| 216 | */ |
danielk1977 | 6d2ab0e | 2008-06-17 17:21:18 +0000 | [diff] [blame] | 217 | static void winMutexFree(sqlite3_mutex *p){ |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 218 | assert( p ); |
| 219 | assert( p->nRef==0 ); |
| 220 | assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ); |
| 221 | DeleteCriticalSection(&p->mutex); |
| 222 | sqlite3_free(p); |
| 223 | } |
| 224 | |
| 225 | /* |
| 226 | ** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt |
| 227 | ** to enter a mutex. If another thread is already within the mutex, |
| 228 | ** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return |
| 229 | ** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK |
| 230 | ** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can |
| 231 | ** be entered multiple times by the same thread. In such cases the, |
| 232 | ** mutex must be exited an equal number of times before another thread |
| 233 | ** can enter. If the same thread tries to enter any other kind of mutex |
| 234 | ** more than once, the behavior is undefined. |
| 235 | */ |
danielk1977 | 6d2ab0e | 2008-06-17 17:21:18 +0000 | [diff] [blame] | 236 | static void winMutexEnter(sqlite3_mutex *p){ |
shaneh | 1f4222f | 2010-02-13 02:31:09 +0000 | [diff] [blame] | 237 | DWORD tid = GetCurrentThreadId(); |
| 238 | assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 239 | EnterCriticalSection(&p->mutex); |
shaneh | 1f4222f | 2010-02-13 02:31:09 +0000 | [diff] [blame] | 240 | p->owner = tid; |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 241 | p->nRef++; |
shaneh | 1f4222f | 2010-02-13 02:31:09 +0000 | [diff] [blame] | 242 | #ifdef SQLITE_DEBUG |
| 243 | if( p->trace ){ |
| 244 | printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); |
| 245 | } |
| 246 | #endif |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 247 | } |
danielk1977 | 6d2ab0e | 2008-06-17 17:21:18 +0000 | [diff] [blame] | 248 | static int winMutexTry(sqlite3_mutex *p){ |
shaneh | 1da207e | 2010-03-09 14:41:12 +0000 | [diff] [blame] | 249 | #ifndef NDEBUG |
shaneh | 1f4222f | 2010-02-13 02:31:09 +0000 | [diff] [blame] | 250 | DWORD tid = GetCurrentThreadId(); |
shaneh | 1da207e | 2010-03-09 14:41:12 +0000 | [diff] [blame] | 251 | #endif |
drh | 8650506 | 2007-10-05 15:08:01 +0000 | [diff] [blame] | 252 | int rc = SQLITE_BUSY; |
shaneh | 1f4222f | 2010-02-13 02:31:09 +0000 | [diff] [blame] | 253 | assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); |
drh | 8650506 | 2007-10-05 15:08:01 +0000 | [diff] [blame] | 254 | /* |
| 255 | ** The sqlite3_mutex_try() routine is very rarely used, and when it |
| 256 | ** is used it is merely an optimization. So it is OK for it to always |
| 257 | ** fail. |
| 258 | ** |
| 259 | ** The TryEnterCriticalSection() interface is only available on WinNT. |
| 260 | ** And some windows compilers complain if you try to use it without |
| 261 | ** first doing some #defines that prevent SQLite from building on Win98. |
| 262 | ** For that reason, we will omit this optimization for now. See |
| 263 | ** ticket #2685. |
| 264 | */ |
| 265 | #if 0 |
drh | df6a81c | 2007-09-05 14:30:42 +0000 | [diff] [blame] | 266 | if( mutexIsNT() && TryEnterCriticalSection(&p->mutex) ){ |
shaneh | 1f4222f | 2010-02-13 02:31:09 +0000 | [diff] [blame] | 267 | p->owner = tid; |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 268 | p->nRef++; |
| 269 | rc = SQLITE_OK; |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 270 | } |
shane | 1a38964 | 2009-01-30 16:09:22 +0000 | [diff] [blame] | 271 | #else |
| 272 | UNUSED_PARAMETER(p); |
drh | 8650506 | 2007-10-05 15:08:01 +0000 | [diff] [blame] | 273 | #endif |
shaneh | 1f4222f | 2010-02-13 02:31:09 +0000 | [diff] [blame] | 274 | #ifdef SQLITE_DEBUG |
| 275 | if( rc==SQLITE_OK && p->trace ){ |
| 276 | printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); |
| 277 | } |
| 278 | #endif |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 279 | return rc; |
| 280 | } |
| 281 | |
| 282 | /* |
| 283 | ** The sqlite3_mutex_leave() routine exits a mutex that was |
| 284 | ** previously entered by the same thread. The behavior |
| 285 | ** is undefined if the mutex is not currently entered or |
| 286 | ** is not currently allocated. SQLite will never do either. |
| 287 | */ |
danielk1977 | 6d2ab0e | 2008-06-17 17:21:18 +0000 | [diff] [blame] | 288 | static void winMutexLeave(sqlite3_mutex *p){ |
shaneh | 1da207e | 2010-03-09 14:41:12 +0000 | [diff] [blame] | 289 | #ifndef NDEBUG |
| 290 | DWORD tid = GetCurrentThreadId(); |
| 291 | #endif |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 292 | assert( p->nRef>0 ); |
shaneh | 1f4222f | 2010-02-13 02:31:09 +0000 | [diff] [blame] | 293 | assert( p->owner==tid ); |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 294 | p->nRef--; |
| 295 | assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); |
| 296 | LeaveCriticalSection(&p->mutex); |
shaneh | 1f4222f | 2010-02-13 02:31:09 +0000 | [diff] [blame] | 297 | #ifdef SQLITE_DEBUG |
| 298 | if( p->trace ){ |
| 299 | printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); |
| 300 | } |
| 301 | #endif |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 302 | } |
| 303 | |
danielk1977 | 6d2ab0e | 2008-06-17 17:21:18 +0000 | [diff] [blame] | 304 | sqlite3_mutex_methods *sqlite3DefaultMutex(void){ |
| 305 | static sqlite3_mutex_methods sMutex = { |
| 306 | winMutexInit, |
danielk1977 | 4a9d1f6 | 2008-06-19 08:51:23 +0000 | [diff] [blame] | 307 | winMutexEnd, |
danielk1977 | 6d2ab0e | 2008-06-17 17:21:18 +0000 | [diff] [blame] | 308 | winMutexAlloc, |
| 309 | winMutexFree, |
| 310 | winMutexEnter, |
| 311 | winMutexTry, |
| 312 | winMutexLeave, |
drh | a418980 | 2008-06-19 16:07:07 +0000 | [diff] [blame] | 313 | #ifdef SQLITE_DEBUG |
danielk1977 | 6d2ab0e | 2008-06-17 17:21:18 +0000 | [diff] [blame] | 314 | winMutexHeld, |
| 315 | winMutexNotheld |
drh | 1875f7a | 2008-12-08 18:19:17 +0000 | [diff] [blame] | 316 | #else |
| 317 | 0, |
| 318 | 0 |
drh | a418980 | 2008-06-19 16:07:07 +0000 | [diff] [blame] | 319 | #endif |
danielk1977 | 6d2ab0e | 2008-06-17 17:21:18 +0000 | [diff] [blame] | 320 | }; |
| 321 | |
| 322 | return &sMutex; |
drh | 437b901 | 2007-08-28 16:34:42 +0000 | [diff] [blame] | 323 | } |
drh | c7ce76a | 2007-08-30 14:10:30 +0000 | [diff] [blame] | 324 | #endif /* SQLITE_MUTEX_W32 */ |