sl@0: /* sl@0: ** 2008 June 18 sl@0: ** sl@0: ** The author disclaims copyright to this source code. In place of sl@0: ** a legal notice, here is a blessing: sl@0: ** sl@0: ** May you do good and not evil. sl@0: ** May you find forgiveness for yourself and forgive others. sl@0: ** May you share freely, never taking more than you give. sl@0: ** sl@0: ************************************************************************* sl@0: ** sl@0: ** $Id: test_mutex.c,v 1.11 2008/07/19 13:43:24 danielk1977 Exp $ sl@0: */ sl@0: sl@0: #include "tcl.h" sl@0: #include "sqlite3.h" sl@0: #include "sqliteInt.h" sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: /* defined in test1.c */ sl@0: const char *sqlite3TestErrorName(int); sl@0: sl@0: /* A countable mutex */ sl@0: struct sqlite3_mutex { sl@0: sqlite3_mutex *pReal; sl@0: int eType; sl@0: }; sl@0: sl@0: /* State variables */ sl@0: static struct test_mutex_globals { sl@0: int isInstalled; /* True if installed */ sl@0: int disableInit; /* True to cause sqlite3_initalize() to fail */ sl@0: int disableTry; /* True to force sqlite3_mutex_try() to fail */ sl@0: int isInit; /* True if initialized */ sl@0: sqlite3_mutex_methods m; /* Interface to "real" mutex system */ sl@0: int aCounter[8]; /* Number of grabs of each type of mutex */ sl@0: sqlite3_mutex aStatic[6]; /* The six static mutexes */ sl@0: } g; sl@0: sl@0: /* Return true if the countable mutex is currently held */ sl@0: static int counterMutexHeld(sqlite3_mutex *p){ sl@0: return g.m.xMutexHeld(p->pReal); sl@0: } sl@0: sl@0: /* Return true if the countable mutex is not currently held */ sl@0: static int counterMutexNotheld(sqlite3_mutex *p){ sl@0: return g.m.xMutexNotheld(p->pReal); sl@0: } sl@0: sl@0: /* Initialize the countable mutex interface sl@0: ** Or, if g.disableInit is non-zero, then do not initialize but instead sl@0: ** return the value of g.disableInit as the result code. This can be used sl@0: ** to simulate an initialization failure. sl@0: */ sl@0: static int counterMutexInit(void){ sl@0: int rc; sl@0: if( g.disableInit ) return g.disableInit; sl@0: rc = g.m.xMutexInit(); sl@0: g.isInit = 1; sl@0: return rc; sl@0: } sl@0: sl@0: /* sl@0: ** Uninitialize the mutex subsystem sl@0: */ sl@0: static int counterMutexEnd(void){ sl@0: g.isInit = 0; sl@0: return g.m.xMutexEnd(); sl@0: } sl@0: sl@0: /* sl@0: ** Allocate a countable mutex sl@0: */ sl@0: static sqlite3_mutex *counterMutexAlloc(int eType){ sl@0: sqlite3_mutex *pReal; sl@0: sqlite3_mutex *pRet = 0; sl@0: sl@0: assert( g.isInit ); sl@0: assert(eType<8 && eType>=0); sl@0: sl@0: pReal = g.m.xMutexAlloc(eType); sl@0: if( !pReal ) return 0; sl@0: sl@0: if( eType==SQLITE_MUTEX_FAST || eType==SQLITE_MUTEX_RECURSIVE ){ sl@0: pRet = (sqlite3_mutex *)malloc(sizeof(sqlite3_mutex)); sl@0: }else{ sl@0: pRet = &g.aStatic[eType-2]; sl@0: } sl@0: sl@0: pRet->eType = eType; sl@0: pRet->pReal = pReal; sl@0: return pRet; sl@0: } sl@0: sl@0: /* sl@0: ** Free a countable mutex sl@0: */ sl@0: static void counterMutexFree(sqlite3_mutex *p){ sl@0: assert( g.isInit ); sl@0: g.m.xMutexFree(p->pReal); sl@0: if( p->eType==SQLITE_MUTEX_FAST || p->eType==SQLITE_MUTEX_RECURSIVE ){ sl@0: free(p); sl@0: } sl@0: } sl@0: sl@0: /* sl@0: ** Enter a countable mutex. Block until entry is safe. sl@0: */ sl@0: static void counterMutexEnter(sqlite3_mutex *p){ sl@0: assert( g.isInit ); sl@0: g.aCounter[p->eType]++; sl@0: g.m.xMutexEnter(p->pReal); sl@0: } sl@0: sl@0: /* sl@0: ** Try to enter a mutex. Return true on success. sl@0: */ sl@0: static int counterMutexTry(sqlite3_mutex *p){ sl@0: assert( g.isInit ); sl@0: g.aCounter[p->eType]++; sl@0: if( g.disableTry ) return SQLITE_BUSY; sl@0: return g.m.xMutexTry(p->pReal); sl@0: } sl@0: sl@0: /* Leave a mutex sl@0: */ sl@0: static void counterMutexLeave(sqlite3_mutex *p){ sl@0: assert( g.isInit ); sl@0: g.m.xMutexLeave(p->pReal); sl@0: } sl@0: sl@0: /* sl@0: ** sqlite3_shutdown sl@0: */ sl@0: static int test_shutdown( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: int rc; sl@0: sl@0: if( objc!=1 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, ""); sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: rc = sqlite3_shutdown(); sl@0: Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** sqlite3_initialize sl@0: */ sl@0: static int test_initialize( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: int rc; sl@0: sl@0: if( objc!=1 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, ""); sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: rc = sqlite3_initialize(); sl@0: Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** install_mutex_counters BOOLEAN sl@0: */ sl@0: static int test_install_mutex_counters( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: int rc = SQLITE_OK; sl@0: int isInstall; sl@0: sl@0: sqlite3_mutex_methods counter_methods = { sl@0: counterMutexInit, sl@0: counterMutexEnd, sl@0: counterMutexAlloc, sl@0: counterMutexFree, sl@0: counterMutexEnter, sl@0: counterMutexTry, sl@0: counterMutexLeave, sl@0: counterMutexHeld, sl@0: counterMutexNotheld sl@0: }; sl@0: sl@0: if( objc!=2 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN"); sl@0: return TCL_ERROR; sl@0: } sl@0: if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[1], &isInstall) ){ sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: assert(isInstall==0 || isInstall==1); sl@0: assert(g.isInstalled==0 || g.isInstalled==1); sl@0: if( isInstall==g.isInstalled ){ sl@0: Tcl_AppendResult(interp, "mutex counters are ", 0); sl@0: Tcl_AppendResult(interp, isInstall?"already installed":"not installed", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: if( isInstall ){ sl@0: assert( g.m.xMutexAlloc==0 ); sl@0: rc = sqlite3_config(SQLITE_CONFIG_GETMUTEX, &g.m); sl@0: if( rc==SQLITE_OK ){ sl@0: sqlite3_config(SQLITE_CONFIG_MUTEX, &counter_methods); sl@0: } sl@0: g.disableTry = 0; sl@0: }else{ sl@0: assert( g.m.xMutexAlloc ); sl@0: rc = sqlite3_config(SQLITE_CONFIG_MUTEX, &g.m); sl@0: memset(&g.m, 0, sizeof(sqlite3_mutex_methods)); sl@0: } sl@0: sl@0: if( rc==SQLITE_OK ){ sl@0: g.isInstalled = isInstall; sl@0: } sl@0: sl@0: Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** read_mutex_counters sl@0: */ sl@0: static int test_read_mutex_counters( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: Tcl_Obj *pRet; sl@0: int ii; sl@0: char *aName[8] = { sl@0: "fast", "recursive", "static_master", "static_mem", sl@0: "static_mem2", "static_prng", "static_lru", "static_lru2" sl@0: }; sl@0: sl@0: if( objc!=1 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, ""); sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: pRet = Tcl_NewObj(); sl@0: Tcl_IncrRefCount(pRet); sl@0: for(ii=0; ii<8; ii++){ sl@0: Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(aName[ii], -1)); sl@0: Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(g.aCounter[ii])); sl@0: } sl@0: Tcl_SetObjResult(interp, pRet); sl@0: Tcl_DecrRefCount(pRet); sl@0: sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** clear_mutex_counters sl@0: */ sl@0: static int test_clear_mutex_counters( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: int ii; sl@0: sl@0: if( objc!=1 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, ""); sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: for(ii=0; ii<8; ii++){ sl@0: g.aCounter[ii] = 0; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Create and free a mutex. Return the mutex pointer. The pointer sl@0: ** will be invalid since the mutex has already been freed. The sl@0: ** return pointer just checks to see if the mutex really was allocated. sl@0: */ sl@0: static int test_alloc_mutex( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: #if SQLITE_THREADSAFE sl@0: sqlite3_mutex *p = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); sl@0: char zBuf[100]; sl@0: sqlite3_mutex_free(p); sl@0: sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", p); sl@0: Tcl_AppendResult(interp, zBuf, (char*)0); sl@0: #endif sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** sqlite3_config OPTION sl@0: ** sl@0: ** OPTION can be either one of the keywords: sl@0: ** sl@0: ** SQLITE_CONFIG_SINGLETHREAD sl@0: ** SQLITE_CONFIG_MULTITHREAD sl@0: ** SQLITE_CONFIG_SERIALIZED sl@0: ** sl@0: ** Or OPTION can be an raw integer. sl@0: */ sl@0: static int test_config( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: struct ConfigOption { sl@0: const char *zName; sl@0: int iValue; sl@0: } aOpt[] = { sl@0: {"singlethread", SQLITE_CONFIG_SINGLETHREAD}, sl@0: {"multithread", SQLITE_CONFIG_MULTITHREAD}, sl@0: {"serialized", SQLITE_CONFIG_SERIALIZED}, sl@0: {0, 0} sl@0: }; sl@0: int s = sizeof(struct ConfigOption); sl@0: int i; sl@0: int rc; sl@0: sl@0: if( objc!=2 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, ""); sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: if( Tcl_GetIndexFromObjStruct(interp, objv[1], aOpt, s, "flag", 0, &i) ){ sl@0: if( Tcl_GetIntFromObj(interp, objv[1], &i) ){ sl@0: return TCL_ERROR; sl@0: } sl@0: }else{ sl@0: i = aOpt[i].iValue; sl@0: } sl@0: sl@0: rc = sqlite3_config(i); sl@0: Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: int Sqlitetest_mutex_Init(Tcl_Interp *interp){ sl@0: static struct { sl@0: char *zName; sl@0: Tcl_ObjCmdProc *xProc; sl@0: } aCmd[] = { sl@0: { "sqlite3_shutdown", (Tcl_ObjCmdProc*)test_shutdown }, sl@0: { "sqlite3_initialize", (Tcl_ObjCmdProc*)test_initialize }, sl@0: { "sqlite3_config", (Tcl_ObjCmdProc*)test_config }, sl@0: sl@0: { "alloc_dealloc_mutex", (Tcl_ObjCmdProc*)test_alloc_mutex }, sl@0: { "install_mutex_counters", (Tcl_ObjCmdProc*)test_install_mutex_counters }, sl@0: { "read_mutex_counters", (Tcl_ObjCmdProc*)test_read_mutex_counters }, sl@0: { "clear_mutex_counters", (Tcl_ObjCmdProc*)test_clear_mutex_counters }, sl@0: }; sl@0: int i; sl@0: for(i=0; i