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