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