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 */ 23 const char *sqlite3TestErrorName(int); 24 25 /* A countable mutex */ 26 struct sqlite3_mutex { 27 sqlite3_mutex *pReal; 28 int eType; 29 }; 30 31 /* State variables */ 32 static 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 */ 43 static 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 */ 48 static 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 */ 57 static 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 */ 68 static int counterMutexEnd(void){ 69 g.isInit = 0; 70 return g.m.xMutexEnd(); 71 } 72 73 /* 74 ** Allocate a countable mutex 75 */ 76 static 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 */ 100 static 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 */ 111 static 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 */ 120 static 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 */ 129 static void counterMutexLeave(sqlite3_mutex *p){ 130 assert( g.isInit ); 131 g.m.xMutexLeave(p->pReal); 132 } 133 134 /* 135 ** sqlite3_shutdown 136 */ 137 static 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 */ 158 static 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 */ 179 static 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 */ 240 static 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 */ 273 static 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 */ 297 static 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 */ 324 static 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 361 static 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 374 static 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 393 static 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 412 int 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