using System.Diagnostics; namespace Community.CsharpSqlite { #if TCLSH using tcl.lang; using Tcl_Interp = tcl.lang.Interp; using Tcl_Obj = tcl.lang.TclObject; using System.Text; public partial class Sqlite3 { /* ** 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. ************************************************************************* ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart ** C#-SQLite is an independent reimplementation of the SQLite software library ** ** SQLITE_SOURCE_ID: 2011-01-28 17:03:50 ed759d5a9edb3bba5f48f243df47be29e3fe8cd7 ** ************************************************************************* */ //#include "tcl.h" //#include "sqlite3.h" //#include "sqliteInt.h" //#include //#include //#include /* defined in test1.c */ //string sqlite3TestErrorName(int); /* A countable mutex */ public partial class sqlite3_mutex { public sqlite3_mutex pReal; public int eType; }; static Var.SQLITE3_GETSET disableInit = new Var.SQLITE3_GETSET( "disable_mutex_init" ); static Var.SQLITE3_GETSET disableTry = new Var.SQLITE3_GETSET( "disable_mutex_try" ); /* State variables */ public class test_mutex_globals { public bool isInstalled; /* True if installed */ public bool disableInit; /* True to cause sqlite3_initalize() to fail */ public bool disableTry; /* True to force sqlite3_mutex_try() to fail */ public bool isInit; /* True if initialized */ public sqlite3_mutex_methods m = new sqlite3_mutex_methods(); /* Interface to "real" mutex system */ public int[] aCounter = new int[8]; /* Number of grabs of each type of mutex */ public sqlite3_mutex[] aStatic = new sqlite3_mutex[6]; /* The six static mutexes */ public test_mutex_globals() { for ( int i = 0; i < aStatic.Length; i++ ) aStatic[i] = new sqlite3_mutex(); } } static test_mutex_globals g = new test_mutex_globals(); /* Return true if the countable mutex is currently held */ static bool counterMutexHeld( sqlite3_mutex p ) { return g.m.xMutexHeld( p.pReal ); } /* Return true if the countable mutex is not currently held */ static bool 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() { int rc; if ( g.disableInit ) return g.disableInit ? 1 : 0; rc = g.m.xMutexInit(); g.isInit = true; return rc; } /* ** Uninitialize the mutex subsystem */ static int counterMutexEnd() { g.isInit = false; return g.m.xMutexEnd(); } /* ** Allocate a countable mutex */ static sqlite3_mutex counterMutexAlloc( int eType ) { sqlite3_mutex pReal; sqlite3_mutex pRet = null; Debug.Assert( g.isInit ); Debug.Assert( eType < 8 && eType >= 0 ); pReal = g.m.xMutexAlloc( eType ); if ( null == pReal ) return null; if ( eType == SQLITE_MUTEX_FAST || eType == SQLITE_MUTEX_RECURSIVE ) { pRet = new sqlite3_mutex(); ;//(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 ) { Debug.Assert( g.isInit ); g.m.xMutexFree( p.pReal ); if ( p.eType == SQLITE_MUTEX_FAST || p.eType == SQLITE_MUTEX_RECURSIVE ) { p = null;//free(p); } } /* ** Enter a countable mutex. Block until entry is safe. */ static void counterMutexEnter( sqlite3_mutex p ) { Debug.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 ) { Debug.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 ) { Debug.Assert( g.isInit ); g.m.xMutexLeave( p.pReal ); } /* ** sqlite3_shutdown */ static int test_shutdown( object clientdata, Tcl_Interp interp, int objc, Tcl_Obj[] objv ) { int rc; if ( objc != 1 ) { TCL.Tcl_WrongNumArgs( interp, 1, objv, "" ); return TCL.TCL_ERROR; } rc = sqlite3_shutdown(); TCL.Tcl_SetResult( interp, sqlite3TestErrorName( rc ), TCL.TCL_VOLATILE ); return TCL.TCL_OK; } /* ** sqlite3_initialize */ static int test_initialize( object clientdata, Tcl_Interp interp, int objc, Tcl_Obj[] objv ) { int rc; if ( objc != 1 ) { TCL.Tcl_WrongNumArgs( interp, 1, objv, "" ); return TCL.TCL_ERROR; } rc = sqlite3_initialize(); TCL.Tcl_SetResult( interp, sqlite3TestErrorName( rc ), TCL.TCL_VOLATILE ); return TCL.TCL_OK; } /* ** install_mutex_counters BOOLEAN */ static int test_install_mutex_counters( object clientdata, Tcl_Interp interp, int objc, Tcl_Obj[] objv ) { int rc = SQLITE_OK; bool isInstall = false; sqlite3_mutex_methods counter_methods = new sqlite3_mutex_methods( (dxMutexInit)counterMutexInit, (dxMutexEnd)counterMutexEnd, (dxMutexAlloc)counterMutexAlloc, (dxMutexFree)counterMutexFree, (dxMutexEnter)counterMutexEnter, (dxMutexTry)counterMutexTry, (dxMutexLeave)counterMutexLeave, (dxMutexHeld)counterMutexHeld, (dxMutexNotheld)counterMutexNotheld ); if ( objc != 2 ) { TCL.Tcl_WrongNumArgs( interp, 1, objv, "BOOLEAN" ); return TCL.TCL_ERROR; } if ( TCL.Tcl_GetBoolean( interp, objv[1], out isInstall )) { return TCL.TCL_ERROR; } Debug.Assert( isInstall == false || isInstall == true ); Debug.Assert( g.isInstalled == false || g.isInstalled == true ); if ( isInstall == g.isInstalled ) { TCL.Tcl_AppendResult( interp, "mutex counters are " ); TCL.Tcl_AppendResult( interp, isInstall ? "already installed" : "not installed" ); return TCL.TCL_ERROR; } if ( isInstall ) { Debug.Assert( g.m.xMutexAlloc == null ); rc = sqlite3_config( SQLITE_CONFIG_GETMUTEX, ref g.m ); if ( rc == SQLITE_OK ) { sqlite3_config( SQLITE_CONFIG_MUTEX, counter_methods ); } g.disableTry = false; } else { Debug.Assert( g.m.xMutexAlloc != null ); rc = sqlite3_config( SQLITE_CONFIG_MUTEX, g.m ); g.m = new sqlite3_mutex_methods();// memset( &g.m, 0, sizeof( sqlite3_mutex_methods ) ); } if ( rc == SQLITE_OK ) { g.isInstalled = isInstall; } TCL.Tcl_SetResult( interp, sqlite3TestErrorName( rc ), TCL.TCL_VOLATILE ); return TCL.TCL_OK; } /* ** read_mutex_counters */ static int test_read_mutex_counters( object clientdata, Tcl_Interp interp, int objc, Tcl_Obj[] objv ) { Tcl_Obj pRet; int ii; string[] aName = new string[] { "fast", "recursive", "static_master", "static_mem", "static_open", "static_prng", "static_lru", "static_pmem" }; if ( objc != 1 ) { TCL.Tcl_WrongNumArgs( interp, 1, objv, "" ); return TCL.TCL_ERROR; } pRet = TCL.Tcl_NewObj(); TCL.Tcl_IncrRefCount( pRet ); for ( ii = 0; ii < 8; ii++ ) { TCL.Tcl_ListObjAppendElement( interp, pRet, TCL.Tcl_NewStringObj( aName[ii], -1 ) ); TCL.Tcl_ListObjAppendElement( interp, pRet, TCL.Tcl_NewIntObj( g.aCounter[ii] ) ); } TCL.Tcl_SetObjResult( interp, pRet ); TCL.Tcl_DecrRefCount( ref pRet ); return TCL.TCL_OK; } /* ** clear_mutex_counters */ static int test_clear_mutex_counters( object clientdata, Tcl_Interp interp, int objc, Tcl_Obj[] objv ) { int ii; if ( objc != 1 ) { TCL.Tcl_WrongNumArgs( interp, 1, objv, "" ); return TCL.TCL_ERROR; } for ( ii = 0; ii < 8; ii++ ) { g.aCounter[ii] = 0; } return TCL.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( object clientdata, Tcl_Interp interp, int objc, Tcl_Obj[] objv ) { #if SQLITE_THREADSAFE sqlite3_mutex p = sqlite3_mutex_alloc( SQLITE_MUTEX_FAST ); StringBuilder zBuf = new StringBuilder( 100 ); sqlite3_mutex_free( p ); sqlite3_snprintf( 100, zBuf, "->%p", p ); TCL.Tcl_AppendResult( interp, zBuf ); #endif return TCL.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. */ struct ConfigOption { public string zName; public int iValue; public ConfigOption( string zName, int iValue ) { this.zName = zName; this.iValue = iValue; } } static bool Tcl_GetIndexFromObjStruct( Interp interp, TclObject to, ConfigOption[] table, int s, string msg, int flags, out int index ) { try { for ( index = 0; index < table.Length; index++ ) { if ( table[index].zName == msg ) return false; } return true; } catch { index = 0; return true; } } static int test_config( object clientdata, Tcl_Interp interp, int objc, Tcl_Obj[] objv ) { ConfigOption[] aOpt = new ConfigOption[] { new ConfigOption("singlethread", SQLITE_CONFIG_SINGLETHREAD), new ConfigOption("multithread", SQLITE_CONFIG_MULTITHREAD), new ConfigOption("serialized", SQLITE_CONFIG_SERIALIZED), new ConfigOption(null,0) }; int s = aOpt.Length;//sizeof(struct ConfigOption); int i = 0; int rc; if ( objc != 2 ) { TCL.Tcl_WrongNumArgs( interp, 1, objv, "" ); return TCL.TCL_ERROR; } if ( Tcl_GetIndexFromObjStruct( interp, objv[1], aOpt, s, "flag", 0, out i ) ) { if ( TCL.TCL_OK != TCL.Tcl_GetIntFromObj( interp, objv[1], out i ) ) { return TCL.TCL_ERROR; } } else { i = aOpt[i].iValue; } rc = sqlite3_config( i ); TCL.Tcl_SetResult( interp, sqlite3TestErrorName( rc ), TCL.TCL_VOLATILE ); return TCL.TCL_OK; } //static sqlite3 *getDbPointer(Tcl_Interp *pInterp, TCL.TCL_Obj *pObj){ //sqlite3 db; //Tcl_CmdInfo info; //string zCmd = TCL.Tcl_GetString(pObj); //if( TCL.Tcl_GetCommandInfo(pInterp, zCmd, &info) ){ // db = *((sqlite3 *)info.objClientData); //}else{ // db = (sqlite3)sqlite3TestTextToPtr(zCmd); //} //Debug.Assert( db ); //return db; static int test_enter_db_mutex( object clientdata, Tcl_Interp interp, int objc, Tcl_Obj[] objv ) { sqlite3 db = null; if ( objc != 2 ) { TCL.Tcl_WrongNumArgs( interp, 1, objv, "DB" ); return TCL.TCL_ERROR; } getDbPointer( interp, TCL.Tcl_GetString( objv[1] ), out db ); if ( null == db ) { return TCL.TCL_ERROR; } sqlite3_mutex_enter( sqlite3_db_mutex( db ) ); return TCL.TCL_OK; } static int test_leave_db_mutex( object clientdata, Tcl_Interp interp, int objc, Tcl_Obj[] objv ) { sqlite3 db = null; if ( objc != 2 ) { TCL.Tcl_WrongNumArgs( interp, 1, objv, "DB" ); return TCL.TCL_ERROR; } getDbPointer( interp, TCL.Tcl_GetString( objv[1] ), out db ); if ( null == db ) { return TCL.TCL_ERROR; } sqlite3_mutex_leave( sqlite3_db_mutex( db ) ); return TCL.TCL_OK; } static public int Sqlitetest_mutex_Init( Tcl_Interp interp ) { //static struct { // string zName; // Tcl_ObjCmdProc *xProc; //} _aObjCmd[] aCmd = new _aObjCmd[]{ new _aObjCmd( "sqlite3_shutdown", test_shutdown ), new _aObjCmd( "sqlite3_initialize", test_initialize ), new _aObjCmd( "sqlite3_config", test_config ), new _aObjCmd("enter_db_mutex", test_enter_db_mutex ), new _aObjCmd( "leave_db_mutex", test_leave_db_mutex ), new _aObjCmd( "alloc_dealloc_mutex", test_alloc_mutex ), new _aObjCmd( "install_mutex_counters", test_install_mutex_counters ), new _aObjCmd( "read_mutex_counters", test_read_mutex_counters ), new _aObjCmd( "clear_mutex_counters", test_clear_mutex_counters ), }; int i; for ( i = 0; i < aCmd.Length; i++ ) {//sizeof(aCmd)/sizeof(aCmd[0]); i++){ TCL.Tcl_CreateObjCommand( interp, aCmd[i].zName, aCmd[i].xProc, null, null ); } TCL.Tcl_LinkVar(interp, "disable_mutex_init",disableInit, VarFlags.SQLITE3_LINK_INT ); //g.disableInit, VarFlags.SQLITE3_LINK_INT ); TCL.Tcl_LinkVar( interp, "disable_mutex_try",disableTry, VarFlags.SQLITE3_LINK_INT ); //g.disableTry, VarFlags.SQLITE3_LINK_INT ); return SQLITE_OK; } } #endif }