diff options
Diffstat (limited to 'src/test_mutex.c')
-rw-r--r-- | src/test_mutex.c | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/src/test_mutex.c b/src/test_mutex.c new file mode 100644 index 0000000..a203208 --- /dev/null +++ b/src/test_mutex.c @@ -0,0 +1,510 @@ +/* +** 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 <stdlib.h> +#include <assert.h> +#include <string.h> + +#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_main", "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_initialize() 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( eStaticType<STATIC_MUTEXES ); + pRet = &g.aStatic[eStaticType]; + } + + pRet->eType = 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->eType<MAX_MUTEXES ); + g.aCounter[p->eType]++; + 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->eType<MAX_MUTEXES ); + g.aCounter[p->eType]++; + 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<MAX_MUTEXES; ii++){ + Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(aName[ii], -1)); + Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(g.aCounter[ii])); + } + Tcl_SetObjResult(interp, pRet); + Tcl_DecrRefCount(pRet); + + return TCL_OK; +} + +/* +** clear_mutex_counters +*/ +static int SQLITE_TCLAPI test_clear_mutex_counters( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int ii; + + if( objc!=1 ){ + Tcl_WrongNumArgs(interp, 1, objv, ""); + return TCL_ERROR; + } + + for(ii=0; ii<MAX_MUTEXES; ii++){ + g.aCounter[ii] = 0; + } + return TCL_OK; +} + +/* +** Create and free a mutex. Return the mutex pointer. The pointer +** will be invalid since the mutex has already been freed. The +** return pointer just checks to see if the mutex really was allocated. +*/ +static int SQLITE_TCLAPI test_alloc_mutex( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ +#if SQLITE_THREADSAFE + sqlite3_mutex *p = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + char zBuf[100]; + sqlite3_mutex_free(p); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", p); + Tcl_AppendResult(interp, zBuf, (char*)0); +#endif + return TCL_OK; +} + +/* +** sqlite3_config OPTION +** +** OPTION can be either one of the keywords: +** +** SQLITE_CONFIG_SINGLETHREAD +** SQLITE_CONFIG_MULTITHREAD +** SQLITE_CONFIG_SERIALIZED +** +** Or OPTION can be an raw integer. +*/ +static int SQLITE_TCLAPI test_config( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + struct ConfigOption { + const char *zName; + int iValue; + } aOpt[] = { + {"singlethread", SQLITE_CONFIG_SINGLETHREAD}, + {"multithread", SQLITE_CONFIG_MULTITHREAD}, + {"serialized", SQLITE_CONFIG_SERIALIZED}, + {0, 0} + }; + int s = sizeof(struct ConfigOption); + int i; + int rc; + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, ""); + return TCL_ERROR; + } + + if( Tcl_GetIndexFromObjStruct(interp, objv[1], aOpt, s, "flag", 0, &i) ){ + if( Tcl_GetIntFromObj(interp, objv[1], &i) ){ + return TCL_ERROR; + } + }else{ + i = aOpt[i].iValue; + } + + rc = sqlite3_config(i); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); + return TCL_OK; +} + +static sqlite3 *getDbPointer(Tcl_Interp *pInterp, Tcl_Obj *pObj){ + sqlite3 *db; + Tcl_CmdInfo info; + char *zCmd = Tcl_GetString(pObj); + if( Tcl_GetCommandInfo(pInterp, zCmd, &info) ){ + db = *((sqlite3 **)info.objClientData); + }else{ + db = (sqlite3*)sqlite3TestTextToPtr(zCmd); + } + assert( db ); + return db; +} + +static sqlite3_mutex *getStaticMutexPointer( + Tcl_Interp *pInterp, + Tcl_Obj *pObj +){ + int iMutex; + if( Tcl_GetIndexFromObj(pInterp, pObj, aName, "mutex name", 0, &iMutex) ){ + return 0; + } + assert( iMutex!=SQLITE_MUTEX_FAST && iMutex!=SQLITE_MUTEX_RECURSIVE ); + return counterMutexAlloc(iMutex); +} + +static int SQLITE_TCLAPI test_enter_static_mutex( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_mutex *pMutex; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "NAME"); + return TCL_ERROR; + } + pMutex = getStaticMutexPointer(interp, objv[1]); + if( !pMutex ){ + return TCL_ERROR; + } + sqlite3_mutex_enter(pMutex); + return TCL_OK; +} + +static int SQLITE_TCLAPI test_leave_static_mutex( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_mutex *pMutex; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "NAME"); + return TCL_ERROR; + } + pMutex = getStaticMutexPointer(interp, objv[1]); + if( !pMutex ){ + return TCL_ERROR; + } + sqlite3_mutex_leave(pMutex); + return TCL_OK; +} + +static int SQLITE_TCLAPI test_enter_db_mutex( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3 *db; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB"); + return TCL_ERROR; + } + db = getDbPointer(interp, objv[1]); + if( !db ){ + return TCL_ERROR; + } + sqlite3_mutex_enter(sqlite3_db_mutex(db)); + return TCL_OK; +} + +static int SQLITE_TCLAPI test_leave_db_mutex( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3 *db; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB"); + return TCL_ERROR; + } + db = getDbPointer(interp, objv[1]); + if( !db ){ + return TCL_ERROR; + } + sqlite3_mutex_leave(sqlite3_db_mutex(db)); + return TCL_OK; +} + +int Sqlitetest_mutex_Init(Tcl_Interp *interp){ + static struct { + char *zName; + Tcl_ObjCmdProc *xProc; + } aCmd[] = { + { "sqlite3_shutdown", (Tcl_ObjCmdProc*)test_shutdown }, + { "sqlite3_initialize", (Tcl_ObjCmdProc*)test_initialize }, + { "sqlite3_config", (Tcl_ObjCmdProc*)test_config }, + + { "enter_static_mutex", (Tcl_ObjCmdProc*)test_enter_static_mutex }, + { "leave_static_mutex", (Tcl_ObjCmdProc*)test_leave_static_mutex }, + + { "enter_db_mutex", (Tcl_ObjCmdProc*)test_enter_db_mutex }, + { "leave_db_mutex", (Tcl_ObjCmdProc*)test_leave_db_mutex }, + + { "alloc_dealloc_mutex", (Tcl_ObjCmdProc*)test_alloc_mutex }, + { "install_mutex_counters", (Tcl_ObjCmdProc*)test_install_mutex_counters }, + { "read_mutex_counters", (Tcl_ObjCmdProc*)test_read_mutex_counters }, + { "clear_mutex_counters", (Tcl_ObjCmdProc*)test_clear_mutex_counters }, + }; + int i; + for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ + Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); + } + + Tcl_LinkVar(interp, "disable_mutex_init", + (char*)&g.disableInit, TCL_LINK_INT); + Tcl_LinkVar(interp, "disable_mutex_try", + (char*)&g.disableTry, TCL_LINK_INT); + return SQLITE_OK; +} |