| /* | |
| ** 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. | |
| */ | |
| #include "tcl.h" | |
| #include "sqlite3.h" | |
| #include "sqliteInt.h" | |
| #include <stdlib.h> | |
| #include <assert.h> | |
| #include <string.h> | |
| /* defined in main.c */ | |
| extern const char *sqlite3ErrName(int); | |
| /* 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[8]; /* Number of grabs of each type of mutex */ | |
| sqlite3_mutex aStatic[6]; /* The six 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<8 && eType>=0); | |
| 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{ | |
| pRet = &g.aStatic[eType-2]; | |
| } | |
| 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 ); | |
| 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 ); | |
| 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 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 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 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 test_read_mutex_counters( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| Tcl_Obj *pRet; | |
| int ii; | |
| char *aName[8] = { | |
| "fast", "recursive", "static_master", "static_mem", | |
| "static_open", "static_prng", "static_lru", "static_pmem" | |
| }; | |
| if( objc!=1 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, ""); | |
| return TCL_ERROR; | |
| } | |
| pRet = Tcl_NewObj(); | |
| Tcl_IncrRefCount(pRet); | |
| for(ii=0; ii<8; 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 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<8; 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 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 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 int 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 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_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; | |
| } |