#include "sqliteInt.h"
#ifdef SQLITE_MUTEX_PTHREADS
#include <pthread.h>
#if defined(SQLITE_DEBUG) || defined(SQLITE_HOMEGROWN_RECURSIVE_MUTEX)
# define SQLITE_MUTEX_NREF 1
#else
# define SQLITE_MUTEX_NREF 0
#endif
struct sqlite3_mutex {
pthread_mutex_t mutex;
#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR)
int id;
#endif
#if SQLITE_MUTEX_NREF
volatile int nRef;
volatile pthread_t owner;
int trace;
#endif
};
#if SQLITE_MUTEX_NREF
# define SQLITE3_MUTEX_INITIALIZER(id) \
{PTHREAD_MUTEX_INITIALIZER,id,0,(pthread_t)0,0}
#elif defined(SQLITE_ENABLE_API_ARMOR)
# define SQLITE3_MUTEX_INITIALIZER(id) { PTHREAD_MUTEX_INITIALIZER, id }
#else
#define SQLITE3_MUTEX_INITIALIZER(id) { PTHREAD_MUTEX_INITIALIZER }
#endif
#if !defined(NDEBUG) || defined(SQLITE_DEBUG)
static int pthreadMutexHeld(sqlite3_mutex *p){
return (p->nRef!=0 && pthread_equal(p->owner, pthread_self()));
}
static int pthreadMutexNotheld(sqlite3_mutex *p){
return p->nRef==0 || pthread_equal(p->owner, pthread_self())==0;
}
#endif
void sqlite3MemoryBarrier(void){
#if defined(SQLITE_MEMORY_BARRIER)
SQLITE_MEMORY_BARRIER;
#elif defined(__GNUC__) && GCC_VERSION>=4001000
__sync_synchronize();
#endif
}
static int pthreadMutexInit(void){ return SQLITE_OK; }
static int pthreadMutexEnd(void){ return SQLITE_OK; }
static sqlite3_mutex *pthreadMutexAlloc(int iType){
static sqlite3_mutex 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)
};
sqlite3_mutex *p;
switch( iType ){
case SQLITE_MUTEX_RECURSIVE: {
p = sqlite3MallocZero( sizeof(*p) );
if( p ){
#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
pthread_mutex_init(&p->mutex, 0);
#else
pthread_mutexattr_t recursiveAttr;
pthread_mutexattr_init(&recursiveAttr);
pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&p->mutex, &recursiveAttr);
pthread_mutexattr_destroy(&recursiveAttr);
#endif
#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR)
p->id = SQLITE_MUTEX_RECURSIVE;
#endif
}
break;
}
case SQLITE_MUTEX_FAST: {
p = sqlite3MallocZero( sizeof(*p) );
if( p ){
pthread_mutex_init(&p->mutex, 0);
#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR)
p->id = SQLITE_MUTEX_FAST;
#endif
}
break;
}
default: {
#ifdef SQLITE_ENABLE_API_ARMOR
if( iType-2<0 || iType-2>=ArraySize(staticMutexes) ){
(void)SQLITE_MISUSE_BKPT;
return 0;
}
#endif
p = &staticMutexes[iType-2];
break;
}
}
#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR)
assert( p==0 || p->id==iType );
#endif
return p;
}
static void pthreadMutexFree(sqlite3_mutex *p){
assert( p->nRef==0 );
#if SQLITE_ENABLE_API_ARMOR
if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE )
#endif
{
pthread_mutex_destroy(&p->mutex);
sqlite3_free(p);
}
#ifdef SQLITE_ENABLE_API_ARMOR
else{
(void)SQLITE_MISUSE_BKPT;
}
#endif
}
static void pthreadMutexEnter(sqlite3_mutex *p){
assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) );
#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
{
pthread_t self = pthread_self();
if( p->nRef>0 && pthread_equal(p->owner, self) ){
p->nRef++;
}else{
pthread_mutex_lock(&p->mutex);
assert( p->nRef==0 );
p->owner = self;
p->nRef = 1;
}
}
#else
pthread_mutex_lock(&p->mutex);
#if SQLITE_MUTEX_NREF
assert( p->nRef>0 || p->owner==0 );
p->owner = pthread_self();
p->nRef++;
#endif
#endif
#ifdef SQLITE_DEBUG
if( p->trace ){
printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
}
#endif
}
static int pthreadMutexTry(sqlite3_mutex *p){
int rc;
assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) );
#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
{
pthread_t self = pthread_self();
if( p->nRef>0 && pthread_equal(p->owner, self) ){
p->nRef++;
rc = SQLITE_OK;
}else if( pthread_mutex_trylock(&p->mutex)==0 ){
assert( p->nRef==0 );
p->owner = self;
p->nRef = 1;
rc = SQLITE_OK;
}else{
rc = SQLITE_BUSY;
}
}
#else
if( pthread_mutex_trylock(&p->mutex)==0 ){
#if SQLITE_MUTEX_NREF
p->owner = pthread_self();
p->nRef++;
#endif
rc = SQLITE_OK;
}else{
rc = SQLITE_BUSY;
}
#endif
#ifdef SQLITE_DEBUG
if( rc==SQLITE_OK && p->trace ){
printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
}
#endif
return rc;
}
static void pthreadMutexLeave(sqlite3_mutex *p){
assert( pthreadMutexHeld(p) );
#if SQLITE_MUTEX_NREF
p->nRef--;
if( p->nRef==0 ) p->owner = 0;
#endif
assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
if( p->nRef==0 ){
pthread_mutex_unlock(&p->mutex);
}
#else
pthread_mutex_unlock(&p->mutex);
#endif
#ifdef SQLITE_DEBUG
if( p->trace ){
printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
}
#endif
}
sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
static const sqlite3_mutex_methods sMutex = {
pthreadMutexInit,
pthreadMutexEnd,
pthreadMutexAlloc,
pthreadMutexFree,
pthreadMutexEnter,
pthreadMutexTry,
pthreadMutexLeave,
#ifdef SQLITE_DEBUG
pthreadMutexHeld,
pthreadMutexNotheld
#else
0,
0
#endif
};
return &sMutex;
}
#endif