1/* 2** 2008 June 18 3** 4** The author disclaims copyright to this source code. In place of 5** a legal notice, here is a blessing: 6** 7** May you do good and not evil. 8** May you find forgiveness for yourself and forgive others. 9** May you share freely, never taking more than you give. 10** 11************************************************************************* 12** This file contains test logic for the sqlite3_mutex interfaces. 13*/ 14 15#include "tcl.h" 16#include "sqlite3.h" 17#include "sqliteInt.h" 18#include <stdlib.h> 19#include <assert.h> 20#include <string.h> 21 22/* defined in test1.c */ 23const char *sqlite3TestErrorName(int); 24 25/* A countable mutex */ 26struct sqlite3_mutex { 27 sqlite3_mutex *pReal; 28 int eType; 29}; 30 31/* State variables */ 32static struct test_mutex_globals { 33 int isInstalled; /* True if installed */ 34 int disableInit; /* True to cause sqlite3_initalize() to fail */ 35 int disableTry; /* True to force sqlite3_mutex_try() to fail */ 36 int isInit; /* True if initialized */ 37 sqlite3_mutex_methods m; /* Interface to "real" mutex system */ 38 int aCounter[8]; /* Number of grabs of each type of mutex */ 39 sqlite3_mutex aStatic[6]; /* The six static mutexes */ 40} g = {0}; 41 42/* Return true if the countable mutex is currently held */ 43static int counterMutexHeld(sqlite3_mutex *p){ 44 return g.m.xMutexHeld(p->pReal); 45} 46 47/* Return true if the countable mutex is not currently held */ 48static int counterMutexNotheld(sqlite3_mutex *p){ 49 return g.m.xMutexNotheld(p->pReal); 50} 51 52/* Initialize the countable mutex interface 53** Or, if g.disableInit is non-zero, then do not initialize but instead 54** return the value of g.disableInit as the result code. This can be used 55** to simulate an initialization failure. 56*/ 57static int counterMutexInit(void){ 58 int rc; 59 if( g.disableInit ) return g.disableInit; 60 rc = g.m.xMutexInit(); 61 g.isInit = 1; 62 return rc; 63} 64 65/* 66** Uninitialize the mutex subsystem 67*/ 68static int counterMutexEnd(void){ 69 g.isInit = 0; 70 return g.m.xMutexEnd(); 71} 72 73/* 74** Allocate a countable mutex 75*/ 76static sqlite3_mutex *counterMutexAlloc(int eType){ 77 sqlite3_mutex *pReal; 78 sqlite3_mutex *pRet = 0; 79 80 assert( g.isInit ); 81 assert(eType<8 && eType>=0); 82 83 pReal = g.m.xMutexAlloc(eType); 84 if( !pReal ) return 0; 85 86 if( eType==SQLITE_MUTEX_FAST || eType==SQLITE_MUTEX_RECURSIVE ){ 87 pRet = (sqlite3_mutex *)malloc(sizeof(sqlite3_mutex)); 88 }else{ 89 pRet = &g.aStatic[eType-2]; 90 } 91 92 pRet->eType = eType; 93 pRet->pReal = pReal; 94 return pRet; 95} 96 97/* 98** Free a countable mutex 99*/ 100static void counterMutexFree(sqlite3_mutex *p){ 101 assert( g.isInit ); 102 g.m.xMutexFree(p->pReal); 103 if( p->eType==SQLITE_MUTEX_FAST || p->eType==SQLITE_MUTEX_RECURSIVE ){ 104 free(p); 105 } 106} 107 108/* 109** Enter a countable mutex. Block until entry is safe. 110*/ 111static void counterMutexEnter(sqlite3_mutex *p){ 112 assert( g.isInit ); 113 g.aCounter[p->eType]++; 114 g.m.xMutexEnter(p->pReal); 115} 116 117/* 118** Try to enter a mutex. Return true on success. 119*/ 120static int counterMutexTry(sqlite3_mutex *p){ 121 assert( g.isInit ); 122 g.aCounter[p->eType]++; 123 if( g.disableTry ) return SQLITE_BUSY; 124 return g.m.xMutexTry(p->pReal); 125} 126 127/* Leave a mutex 128*/ 129static void counterMutexLeave(sqlite3_mutex *p){ 130 assert( g.isInit ); 131 g.m.xMutexLeave(p->pReal); 132} 133 134/* 135** sqlite3_shutdown 136*/ 137static int test_shutdown( 138 void * clientData, 139 Tcl_Interp *interp, 140 int objc, 141 Tcl_Obj *CONST objv[] 142){ 143 int rc; 144 145 if( objc!=1 ){ 146 Tcl_WrongNumArgs(interp, 1, objv, ""); 147 return TCL_ERROR; 148 } 149 150 rc = sqlite3_shutdown(); 151 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); 152 return TCL_OK; 153} 154 155/* 156** sqlite3_initialize 157*/ 158static int test_initialize( 159 void * clientData, 160 Tcl_Interp *interp, 161 int objc, 162 Tcl_Obj *CONST objv[] 163){ 164 int rc; 165 166 if( objc!=1 ){ 167 Tcl_WrongNumArgs(interp, 1, objv, ""); 168 return TCL_ERROR; 169 } 170 171 rc = sqlite3_initialize(); 172 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); 173 return TCL_OK; 174} 175 176/* 177** install_mutex_counters BOOLEAN 178*/ 179static int test_install_mutex_counters( 180 void * clientData, 181 Tcl_Interp *interp, 182 int objc, 183 Tcl_Obj *CONST objv[] 184){ 185 int rc = SQLITE_OK; 186 int isInstall; 187 188 sqlite3_mutex_methods counter_methods = { 189 counterMutexInit, 190 counterMutexEnd, 191 counterMutexAlloc, 192 counterMutexFree, 193 counterMutexEnter, 194 counterMutexTry, 195 counterMutexLeave, 196 counterMutexHeld, 197 counterMutexNotheld 198 }; 199 200 if( objc!=2 ){ 201 Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN"); 202 return TCL_ERROR; 203 } 204 if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[1], &isInstall) ){ 205 return TCL_ERROR; 206 } 207 208 assert(isInstall==0 || isInstall==1); 209 assert(g.isInstalled==0 || g.isInstalled==1); 210 if( isInstall==g.isInstalled ){ 211 Tcl_AppendResult(interp, "mutex counters are ", 0); 212 Tcl_AppendResult(interp, isInstall?"already installed":"not installed", 0); 213 return TCL_ERROR; 214 } 215 216 if( isInstall ){ 217 assert( g.m.xMutexAlloc==0 ); 218 rc = sqlite3_config(SQLITE_CONFIG_GETMUTEX, &g.m); 219 if( rc==SQLITE_OK ){ 220 sqlite3_config(SQLITE_CONFIG_MUTEX, &counter_methods); 221 } 222 g.disableTry = 0; 223 }else{ 224 assert( g.m.xMutexAlloc ); 225 rc = sqlite3_config(SQLITE_CONFIG_MUTEX, &g.m); 226 memset(&g.m, 0, sizeof(sqlite3_mutex_methods)); 227 } 228 229 if( rc==SQLITE_OK ){ 230 g.isInstalled = isInstall; 231 } 232 233 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); 234 return TCL_OK; 235} 236 237/* 238** read_mutex_counters 239*/ 240static int test_read_mutex_counters( 241 void * clientData, 242 Tcl_Interp *interp, 243 int objc, 244 Tcl_Obj *CONST objv[] 245){ 246 Tcl_Obj *pRet; 247 int ii; 248 char *aName[8] = { 249 "fast", "recursive", "static_master", "static_mem", 250 "static_open", "static_prng", "static_lru", "static_pmem" 251 }; 252 253 if( objc!=1 ){ 254 Tcl_WrongNumArgs(interp, 1, objv, ""); 255 return TCL_ERROR; 256 } 257 258 pRet = Tcl_NewObj(); 259 Tcl_IncrRefCount(pRet); 260 for(ii=0; ii<8; ii++){ 261 Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(aName[ii], -1)); 262 Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(g.aCounter[ii])); 263 } 264 Tcl_SetObjResult(interp, pRet); 265 Tcl_DecrRefCount(pRet); 266 267 return TCL_OK; 268} 269 270/* 271** clear_mutex_counters 272*/ 273static int test_clear_mutex_counters( 274 void * clientData, 275 Tcl_Interp *interp, 276 int objc, 277 Tcl_Obj *CONST objv[] 278){ 279 int ii; 280 281 if( objc!=1 ){ 282 Tcl_WrongNumArgs(interp, 1, objv, ""); 283 return TCL_ERROR; 284 } 285 286 for(ii=0; ii<8; ii++){ 287 g.aCounter[ii] = 0; 288 } 289 return TCL_OK; 290} 291 292/* 293** Create and free a mutex. Return the mutex pointer. The pointer 294** will be invalid since the mutex has already been freed. The 295** return pointer just checks to see if the mutex really was allocated. 296*/ 297static int test_alloc_mutex( 298 void * clientData, 299 Tcl_Interp *interp, 300 int objc, 301 Tcl_Obj *CONST objv[] 302){ 303#if SQLITE_THREADSAFE 304 sqlite3_mutex *p = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); 305 char zBuf[100]; 306 sqlite3_mutex_free(p); 307 sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", p); 308 Tcl_AppendResult(interp, zBuf, (char*)0); 309#endif 310 return TCL_OK; 311} 312 313/* 314** sqlite3_config OPTION 315** 316** OPTION can be either one of the keywords: 317** 318** SQLITE_CONFIG_SINGLETHREAD 319** SQLITE_CONFIG_MULTITHREAD 320** SQLITE_CONFIG_SERIALIZED 321** 322** Or OPTION can be an raw integer. 323*/ 324static int test_config( 325 void * clientData, 326 Tcl_Interp *interp, 327 int objc, 328 Tcl_Obj *CONST objv[] 329){ 330 struct ConfigOption { 331 const char *zName; 332 int iValue; 333 } aOpt[] = { 334 {"singlethread", SQLITE_CONFIG_SINGLETHREAD}, 335 {"multithread", SQLITE_CONFIG_MULTITHREAD}, 336 {"serialized", SQLITE_CONFIG_SERIALIZED}, 337 {0, 0} 338 }; 339 int s = sizeof(struct ConfigOption); 340 int i; 341 int rc; 342 343 if( objc!=2 ){ 344 Tcl_WrongNumArgs(interp, 1, objv, ""); 345 return TCL_ERROR; 346 } 347 348 if( Tcl_GetIndexFromObjStruct(interp, objv[1], aOpt, s, "flag", 0, &i) ){ 349 if( Tcl_GetIntFromObj(interp, objv[1], &i) ){ 350 return TCL_ERROR; 351 } 352 }else{ 353 i = aOpt[i].iValue; 354 } 355 356 rc = sqlite3_config(i); 357 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); 358 return TCL_OK; 359} 360 361static sqlite3 *getDbPointer(Tcl_Interp *pInterp, Tcl_Obj *pObj){ 362 sqlite3 *db; 363 Tcl_CmdInfo info; 364 char *zCmd = Tcl_GetString(pObj); 365 if( Tcl_GetCommandInfo(pInterp, zCmd, &info) ){ 366 db = *((sqlite3 **)info.objClientData); 367 }else{ 368 db = (sqlite3*)sqlite3TestTextToPtr(zCmd); 369 } 370 assert( db ); 371 return db; 372} 373 374static int test_enter_db_mutex( 375 void * clientData, 376 Tcl_Interp *interp, 377 int objc, 378 Tcl_Obj *CONST objv[] 379){ 380 sqlite3 *db; 381 if( objc!=2 ){ 382 Tcl_WrongNumArgs(interp, 1, objv, "DB"); 383 return TCL_ERROR; 384 } 385 db = getDbPointer(interp, objv[1]); 386 if( !db ){ 387 return TCL_ERROR; 388 } 389 sqlite3_mutex_enter(sqlite3_db_mutex(db)); 390 return TCL_OK; 391} 392 393static int test_leave_db_mutex( 394 void * clientData, 395 Tcl_Interp *interp, 396 int objc, 397 Tcl_Obj *CONST objv[] 398){ 399 sqlite3 *db; 400 if( objc!=2 ){ 401 Tcl_WrongNumArgs(interp, 1, objv, "DB"); 402 return TCL_ERROR; 403 } 404 db = getDbPointer(interp, objv[1]); 405 if( !db ){ 406 return TCL_ERROR; 407 } 408 sqlite3_mutex_leave(sqlite3_db_mutex(db)); 409 return TCL_OK; 410} 411 412int Sqlitetest_mutex_Init(Tcl_Interp *interp){ 413 static struct { 414 char *zName; 415 Tcl_ObjCmdProc *xProc; 416 } aCmd[] = { 417 { "sqlite3_shutdown", (Tcl_ObjCmdProc*)test_shutdown }, 418 { "sqlite3_initialize", (Tcl_ObjCmdProc*)test_initialize }, 419 { "sqlite3_config", (Tcl_ObjCmdProc*)test_config }, 420 421 { "enter_db_mutex", (Tcl_ObjCmdProc*)test_enter_db_mutex }, 422 { "leave_db_mutex", (Tcl_ObjCmdProc*)test_leave_db_mutex }, 423 424 { "alloc_dealloc_mutex", (Tcl_ObjCmdProc*)test_alloc_mutex }, 425 { "install_mutex_counters", (Tcl_ObjCmdProc*)test_install_mutex_counters }, 426 { "read_mutex_counters", (Tcl_ObjCmdProc*)test_read_mutex_counters }, 427 { "clear_mutex_counters", (Tcl_ObjCmdProc*)test_clear_mutex_counters }, 428 }; 429 int i; 430 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ 431 Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); 432 } 433 434 Tcl_LinkVar(interp, "disable_mutex_init", 435 (char*)&g.disableInit, TCL_LINK_INT); 436 Tcl_LinkVar(interp, "disable_mutex_try", 437 (char*)&g.disableTry, TCL_LINK_INT); 438 return SQLITE_OK; 439} 440