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