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