| OLD | NEW |
| (Empty) |
| 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 } | |
| OLD | NEW |