Index: src/global.c ================================================================== --- src/global.c +++ src/global.c @@ -172,10 +172,11 @@ 0x7ffffffe, /* mxStrlen */ 0, /* neverCorrupt */ 128, /* szLookaside */ 500, /* nLookaside */ {0,0,0,0,0,0,0,0}, /* m */ + (void*)0, /* pMutex */ {0,0,0,0,0,0,0,0,0}, /* mutex */ {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */ (void*)0, /* pHeap */ 0, /* nHeap */ 0, 0, /* mnHeap, mxHeap */ Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -169,11 +169,17 @@ ** malloc subsystem - this implies that the allocation of a static ** mutex must not require support from the malloc subsystem. */ MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); ) sqlite3_mutex_enter(pMaster); - sqlite3GlobalConfig.isMutexInit = 1; + if( sqlite3GlobalConfig.isInit ){ + assert( sqlite3GlobalConfig.isMutexInit ); + assert( sqlite3GlobalConfig.isMallocInit ); + sqlite3_mutex_leave(pMaster); + return SQLITE_OK; + } + sqlite3GlobalConfig.isMutexInit = 1; /* possibly redundant */ if( !sqlite3GlobalConfig.isMallocInit ){ rc = sqlite3MallocInit(); } if( rc==SQLITE_OK ){ sqlite3GlobalConfig.isMallocInit = 1; @@ -327,10 +333,21 @@ if( sqlite3GlobalConfig.isMutexInit ){ sqlite3MutexEnd(); sqlite3GlobalConfig.isMutexInit = 0; } + /* + ** Force the state of the mutex subsystem to be completely reset now, even + ** if the configured xMutexEnd(), if any, failed. This is not thread-safe. + ** This is necessary even if the xMutexInit() was never called, due to the + ** possiblity of this state being changed via SQLITE_CONFIG_MUTEX. After + ** this point, the application must enable any custom mutex implementation + ** again via SQLITE_CONFIG_MUTEX, if necessary. + */ + sqlite3GlobalConfig.pMutex = 0; + memset(&sqlite3GlobalConfig.mutex, 0, sizeof(sqlite3_mutex_methods)); + return SQLITE_OK; } /* ** This API allows applications to modify the global configuration of @@ -382,12 +399,19 @@ break; } #endif #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-63666-48755 */ case SQLITE_CONFIG_MUTEX: { - /* Specify an alternative mutex implementation */ - sqlite3GlobalConfig.mutex = *va_arg(ap, sqlite3_mutex_methods*); + /* Atomically compare-and-swap the mutex implementation pointer to + * help prevent a race condition with sqlite3MutexInit(). */ + if( sqlite3CompareAndSwap((void * volatile *)&sqlite3GlobalConfig.pMutex, + 0, &sqlite3GlobalConfig.mutex)==0 ){ + /* Specify an alternative mutex implementation */ + sqlite3GlobalConfig.mutex = *va_arg(ap, sqlite3_mutex_methods*); + }else{ + rc = SQLITE_ERROR; + } break; } #endif #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-14450-37597 */ case SQLITE_CONFIG_GETMUTEX: { Index: src/mutex.c ================================================================== --- src/mutex.c +++ src/mutex.c @@ -24,42 +24,84 @@ static SQLITE_WSD int mutexIsInit = 0; #endif /* SQLITE_DEBUG && !defined(SQLITE_MUTEX_OMIT) */ #ifndef SQLITE_MUTEX_OMIT +/* +** This structure is for use by mutexIsInvalid() only. It represents an +** invalid mutex implementation (i.e. one where all the function pointers +** are null). +*/ +static const sqlite3_mutex_methods mutexNullMethods = { + 0, /* xMutexInit */ + 0, /* xMutexEnd */ + 0, /* xMutexAlloc */ + 0, /* xMutexFree */ + 0, /* xMutexEnter */ + 0, /* xMutexTry */ + 0, /* xMutexLeave */ + 0, /* xMutexHeld */ + 0 /* xMutexNotheld */ +}; + +/* +** Returns non-zero if the currently configured mutex implemention is +** invalid (i.e. all of its function pointers are null). +*/ +static int mutexIsInvalid(void){ + return memcmp(&sqlite3GlobalConfig.mutex, &mutexNullMethods, + sizeof(sqlite3_mutex_methods))==0; +} + +/* +** Copies a mutex implementation. Both arguments must point to valid +** memory. +*/ +static void mutexCopy( + sqlite3_mutex_methods *pTo, + sqlite3_mutex_methods const *pFrom +){ + pTo->xMutexInit = pFrom->xMutexInit; + pTo->xMutexEnd = pFrom->xMutexEnd; + pTo->xMutexFree = pFrom->xMutexFree; + pTo->xMutexEnter = pFrom->xMutexEnter; + pTo->xMutexTry = pFrom->xMutexTry; + pTo->xMutexLeave = pFrom->xMutexLeave; + pTo->xMutexHeld = pFrom->xMutexHeld; + pTo->xMutexNotheld = pFrom->xMutexNotheld; + pTo->xMutexAlloc = pFrom->xMutexAlloc; +} + /* ** Initialize the mutex system. */ int sqlite3MutexInit(void){ - int rc = SQLITE_OK; - if( !sqlite3GlobalConfig.mutex.xMutexAlloc ){ - /* If the xMutexAlloc method has not been set, then the user did not - ** install a mutex implementation via sqlite3_config() prior to - ** sqlite3_initialize() being called. This block copies pointers to - ** the default implementation into the sqlite3GlobalConfig structure. + static int initPending = 0; + int rc; + if( sqlite3CompareAndSwap((void * volatile *)&sqlite3GlobalConfig.pMutex, + 0, &sqlite3GlobalConfig.mutex)==0 || mutexIsInvalid() ){ + /* If the mutex implementation pointer has not been set, then the user + ** did not install a mutex implementation via sqlite3_config() prior to + ** sqlite3_initialize() being called. This block copies the pointers + ** for the default implementation into the sqlite3GlobalConfig structure. */ sqlite3_mutex_methods const *pFrom; - sqlite3_mutex_methods *pTo = &sqlite3GlobalConfig.mutex; if( sqlite3GlobalConfig.bCoreMutex ){ pFrom = sqlite3DefaultMutex(); }else{ pFrom = sqlite3NoopMutex(); } - pTo->xMutexInit = pFrom->xMutexInit; - pTo->xMutexEnd = pFrom->xMutexEnd; - pTo->xMutexFree = pFrom->xMutexFree; - pTo->xMutexEnter = pFrom->xMutexEnter; - pTo->xMutexTry = pFrom->xMutexTry; - pTo->xMutexLeave = pFrom->xMutexLeave; - pTo->xMutexHeld = pFrom->xMutexHeld; - pTo->xMutexNotheld = pFrom->xMutexNotheld; + mutexCopy(&sqlite3GlobalConfig.mutex, pFrom); sqlite3MemoryBarrier(); - pTo->xMutexAlloc = pFrom->xMutexAlloc; } - assert( sqlite3GlobalConfig.mutex.xMutexInit ); - rc = sqlite3GlobalConfig.mutex.xMutexInit(); + if( !initPending ){ + assert( sqlite3GlobalConfig.mutex.xMutexInit ); + initPending = 1; + rc = sqlite3GlobalConfig.mutex.xMutexInit(); + initPending = 0; + } #ifdef SQLITE_DEBUG GLOBAL(int, mutexIsInit) = 1; #endif Index: src/mutex_noop.c ================================================================== --- src/mutex_noop.c +++ src/mutex_noop.c @@ -26,10 +26,32 @@ ** called correctly. */ #include "sqliteInt.h" #ifndef SQLITE_MUTEX_OMIT + +/* +** Try to provide an atomic compare-and-swap operation on a void pointer, +** needed for initialization only. +*/ +void *sqlite3NoopCompareAndSwap( + void *volatile *pCurVal, + void *cmpVal, + void *swapVal +){ + /* + ** This platform may not have a way to perform an atomic compare-and-swap + ** operation; therefore, use the fallback algorithm. + ** + ** WARNING: This code is almost certainly not thread-safe. + */ + void *oldVal = *pCurVal; + if( oldVal==cmpVal ){ + *pCurVal = swapVal; + } + return oldVal; +} #ifndef SQLITE_DEBUG /* ** Stub routines for all mutex methods. ** Index: src/mutex_unix.c ================================================================== --- src/mutex_unix.c +++ src/mutex_unix.c @@ -88,10 +88,38 @@ SQLITE_MEMORY_BARRIER; #elif defined(__GNUC__) && GCC_VERSION>=4001000 __sync_synchronize(); #endif } + +/* +** Try to provide an atomic compare-and-swap operation on a void pointer, +** needed for initialization only. +*/ +void *sqlite3CompareAndSwap( + void *volatile *pCurVal, + void *cmpVal, + void *swapVal +){ +#if defined(SQLITE_COMPARE_AND_SWAP) + return SQLITE_COMPARE_AND_SWAP(pCurVal, cmpVal, swapVal); +#elif defined(__GNUC__) && GCC_VERSION>=4001000 && SQLITE_PTRSIZE>4 + return (void *)__sync_val_compare_and_swap_8( + (u64 volatile *)pCurVal, (u64)cmpVal, (u64)swapVal); +#elif defined(__GNUC__) && GCC_VERSION>=4001000 + return (void *)__sync_val_compare_and_swap_4( + (u32 volatile *)pCurVal, (u32)cmpVal, (u32)swapVal); +#else + /* + ** This platform may not have a way to perform an atomic compare-and-swap + ** operation; therefore, use the fallback algorithm. + ** + ** WARNING: This code is almost certainly not thread-safe. + */ + return sqlite3NoopCompareAndSwap(pCurVal, cmpVal, swapVal); +#endif +} /* ** Initialize and deinitialize the mutex subsystem. */ static int pthreadMutexInit(void){ return SQLITE_OK; } Index: src/mutex_w32.c ================================================================== --- src/mutex_w32.c +++ src/mutex_w32.c @@ -87,10 +87,32 @@ __sync_synchronize(); #else MemoryBarrier(); #endif } + +/* +** Try to provide an atomic compare-and-swap operation on a void pointer, +** needed for initialization only. +*/ +void *sqlite3CompareAndSwap( + void * volatile *pCurVal, + void *cmpVal, + void *swapVal +){ +#if defined(SQLITE_COMPARE_AND_SWAP) + return SQLITE_COMPARE_AND_SWAP(pCurVal, cmpVal, swapVal); +#elif SQLITE_PTRSIZE>4 + return (void *)InterlockedCompareExchange64( + (LONGLONG SQLITE_WIN32_VOLATILE *)pCurVal, (LONGLONG)swapVal, + (LONGLONG)cmpVal); +#else + return (void *)InterlockedCompareExchange( + (LONG SQLITE_WIN32_VOLATILE *)pCurVal, (LONG)swapVal, + (LONG)cmpVal); +#endif +} /* ** Initialize and deinitialize the mutex subsystem. */ static sqlite3_mutex winMutex_staticMutexes[] = { Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -2936,10 +2936,11 @@ int mxStrlen; /* Maximum string length */ int neverCorrupt; /* Database is always well-formed */ int szLookaside; /* Default lookaside buffer size */ int nLookaside; /* Default lookaside buffer count */ sqlite3_mem_methods m; /* Low-level memory allocation interface */ + sqlite3_mutex_methods *pMutex; /* Address of mutex member or zero. */ sqlite3_mutex_methods mutex; /* Low-level mutex interface */ sqlite3_pcache_methods2 pcache2; /* Low-level page-cache interface */ void *pHeap; /* Heap storage space */ int nHeap; /* Size of pHeap[] */ int mnReq, mxReq; /* Min and max heap requests sizes */ @@ -3187,19 +3188,29 @@ #ifndef SQLITE_MUTEX_OMIT sqlite3_mutex_methods const *sqlite3DefaultMutex(void); sqlite3_mutex_methods const *sqlite3NoopMutex(void); + void *sqlite3NoopCompareAndSwap(void * volatile *, void *, void *); sqlite3_mutex *sqlite3MutexAlloc(int); int sqlite3MutexInit(void); int sqlite3MutexEnd(void); #endif #if !defined(SQLITE_MUTEX_OMIT) && !defined(SQLITE_MUTEX_NOOP) void sqlite3MemoryBarrier(void); #else # define sqlite3MemoryBarrier() #endif +#if !defined(SQLITE_MUTEX_OMIT) +# if !defined(SQLITE_MUTEX_NOOP) + void *sqlite3CompareAndSwap(void * volatile *, void *, void *); +# else +# define sqlite3CompareAndSwap sqlite3NoopCompareAndSwap +# endif +#else +# define sqlite3CompareAndSwap(x,y,z) +#endif sqlite3_int64 sqlite3StatusValue(int); void sqlite3StatusUp(int, int); void sqlite3StatusDown(int, int); void sqlite3StatusSet(int, int);