/* ** 2008 June 18 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains test logic for the sqlite3_mutex interfaces. */ #if defined(INCLUDE_SQLITE_TCL_H) # include "sqlite_tcl.h" #else # include "tcl.h" #endif #include "sqlite3.h" #include "sqliteInt.h" #include #include #include #define MAX_MUTEXES (SQLITE_MUTEX_STATIC_VFS3+1) #define STATIC_MUTEXES (MAX_MUTEXES-(SQLITE_MUTEX_RECURSIVE+1)) /* defined in main.c */ extern const char *sqlite3ErrName(int); static const char *aName[MAX_MUTEXES+1] = { "fast", "recursive", "static_master", "static_mem", "static_open", "static_prng", "static_lru", "static_pmem", "static_app1", "static_app2", "static_app3", "static_vfs1", "static_vfs2", "static_vfs3", 0 }; /* A countable mutex */ struct sqlite3_mutex { sqlite3_mutex *pReal; int eType; }; /* State variables */ static struct test_mutex_globals { int isInstalled; /* True if installed */ int disableInit; /* True to cause sqlite3_initalize() to fail */ int disableTry; /* True to force sqlite3_mutex_try() to fail */ int isInit; /* True if initialized */ sqlite3_mutex_methods m; /* Interface to "real" mutex system */ int aCounter[MAX_MUTEXES]; /* Number of grabs of each type of mutex */ sqlite3_mutex aStatic[STATIC_MUTEXES]; /* The static mutexes */ } g = {0}; /* Return true if the countable mutex is currently held */ static int counterMutexHeld(sqlite3_mutex *p){ return g.m.xMutexHeld(p->pReal); } /* Return true if the countable mutex is not currently held */ static int counterMutexNotheld(sqlite3_mutex *p){ return g.m.xMutexNotheld(p->pReal); } /* Initialize the countable mutex interface ** Or, if g.disableInit is non-zero, then do not initialize but instead ** return the value of g.disableInit as the result code. This can be used ** to simulate an initialization failure. */ static int counterMutexInit(void){ int rc; if( g.disableInit ) return g.disableInit; rc = g.m.xMutexInit(); g.isInit = 1; return rc; } /* ** Uninitialize the mutex subsystem */ static int counterMutexEnd(void){ g.isInit = 0; return g.m.xMutexEnd(); } /* ** Allocate a countable mutex */ static sqlite3_mutex *counterMutexAlloc(int eType){ sqlite3_mutex *pReal; sqlite3_mutex *pRet = 0; assert( g.isInit ); assert( eType>=SQLITE_MUTEX_FAST ); assert( eType<=SQLITE_MUTEX_STATIC_VFS3 ); pReal = g.m.xMutexAlloc(eType); if( !pReal ) return 0; if( eType==SQLITE_MUTEX_FAST || eType==SQLITE_MUTEX_RECURSIVE ){ pRet = (sqlite3_mutex *)malloc(sizeof(sqlite3_mutex)); }else{ int eStaticType = eType - (MAX_MUTEXES - STATIC_MUTEXES); assert( eStaticType>=0 ); assert( eStaticTypeeType = eType; pRet->pReal = pReal; return pRet; } /* ** Free a countable mutex */ static void counterMutexFree(sqlite3_mutex *p){ assert( g.isInit ); g.m.xMutexFree(p->pReal); if( p->eType==SQLITE_MUTEX_FAST || p->eType==SQLITE_MUTEX_RECURSIVE ){ free(p); } } /* ** Enter a countable mutex. Block until entry is safe. */ static void counterMutexEnter(sqlite3_mutex *p){ assert( g.isInit ); assert( p->eType>=0 ); assert( p->eTypeeType]++; g.m.xMutexEnter(p->pReal); } /* ** Try to enter a mutex. Return true on success. */ static int counterMutexTry(sqlite3_mutex *p){ assert( g.isInit ); assert( p->eType>=0 ); assert( p->eTypeeType]++; if( g.disableTry ) return SQLITE_BUSY; return g.m.xMutexTry(p->pReal); } /* Leave a mutex */ static void counterMutexLeave(sqlite3_mutex *p){ assert( g.isInit ); g.m.xMutexLeave(p->pReal); } /* ** sqlite3_shutdown */ static int SQLITE_TCLAPI test_shutdown( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int rc; if( objc!=1 ){ Tcl_WrongNumArgs(interp, 1, objv, ""); return TCL_ERROR; } rc = sqlite3_shutdown(); Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); return TCL_OK; } /* ** sqlite3_initialize */ static int SQLITE_TCLAPI test_initialize( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int rc; if( objc!=1 ){ Tcl_WrongNumArgs(interp, 1, objv, ""); return TCL_ERROR; } rc = sqlite3_initialize(); Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); return TCL_OK; } /* ** install_mutex_counters BOOLEAN */ static int SQLITE_TCLAPI test_install_mutex_counters( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int rc = SQLITE_OK; int isInstall; sqlite3_mutex_methods counter_methods = { counterMutexInit, counterMutexEnd, counterMutexAlloc, counterMutexFree, counterMutexEnter, counterMutexTry, counterMutexLeave, counterMutexHeld, counterMutexNotheld }; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN"); return TCL_ERROR; } if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[1], &isInstall) ){ return TCL_ERROR; } assert(isInstall==0 || isInstall==1); assert(g.isInstalled==0 || g.isInstalled==1); if( isInstall==g.isInstalled ){ Tcl_AppendResult(interp, "mutex counters are ", 0); Tcl_AppendResult(interp, isInstall?"already installed":"not installed", 0); return TCL_ERROR; } if( isInstall ){ assert( g.m.xMutexAlloc==0 ); rc = sqlite3_config(SQLITE_CONFIG_GETMUTEX, &g.m); if( rc==SQLITE_OK ){ sqlite3_config(SQLITE_CONFIG_MUTEX, &counter_methods); } g.disableTry = 0; }else{ assert( g.m.xMutexAlloc ); rc = sqlite3_config(SQLITE_CONFIG_MUTEX, &g.m); memset(&g.m, 0, sizeof(sqlite3_mutex_methods)); } if( rc==SQLITE_OK ){ g.isInstalled = isInstall; } Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); return TCL_OK; } /* ** read_mutex_counters */ static int SQLITE_TCLAPI test_read_mutex_counters( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ Tcl_Obj *pRet; int ii; if( objc!=1 ){ Tcl_WrongNumArgs(interp, 1, objv, ""); return TCL_ERROR; } pRet = Tcl_NewObj(); Tcl_IncrRefCount(pRet); for(ii=0; ii