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