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.9 2008/07/10 20:41:50 drh Exp $ 14 */ 15 16 #include "tcl.h" 17 #include "sqlite3.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; 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_mem2", "static_prng", "static_lru", "static_lru2" 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 sqlite3_mutex *p = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); 304 char zBuf[100]; 305 sqlite3_mutex_free(p); 306 sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", p); 307 Tcl_AppendResult(interp, zBuf, (char*)0); 308 return TCL_OK; 309 } 310 311 /* 312 ** sqlite3_config OPTION 313 ** 314 ** OPTION can be either one of the keywords: 315 ** 316 ** SQLITE_CONFIG_SINGLETHREAD 317 ** SQLITE_CONFIG_MULTITHREAD 318 ** SQLITE_CONFIG_SERIALIZED 319 ** 320 ** Or OPTION can be an raw integer. 321 */ 322 static int test_config( 323 void * clientData, 324 Tcl_Interp *interp, 325 int objc, 326 Tcl_Obj *CONST objv[] 327 ){ 328 struct ConfigOption { 329 const char *zName; 330 int iValue; 331 } aOpt[] = { 332 {"singlethread", SQLITE_CONFIG_SINGLETHREAD}, 333 {"multithread", SQLITE_CONFIG_MULTITHREAD}, 334 {"serialized", SQLITE_CONFIG_SERIALIZED}, 335 {0, 0} 336 }; 337 int s = sizeof(struct ConfigOption); 338 int i; 339 int rc; 340 341 if( objc!=2 ){ 342 Tcl_WrongNumArgs(interp, 1, objv, ""); 343 return TCL_ERROR; 344 } 345 346 if( Tcl_GetIndexFromObjStruct(interp, objv[1], aOpt, s, "flag", 0, &i) ){ 347 if( Tcl_GetIntFromObj(interp, objv[1], &i) ){ 348 return TCL_ERROR; 349 } 350 }else{ 351 i = aOpt[i].iValue; 352 } 353 354 rc = sqlite3_config(i); 355 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); 356 return TCL_OK; 357 } 358 359 int Sqlitetest_mutex_Init(Tcl_Interp *interp){ 360 static struct { 361 char *zName; 362 Tcl_ObjCmdProc *xProc; 363 } aCmd[] = { 364 { "sqlite3_shutdown", (Tcl_ObjCmdProc*)test_shutdown }, 365 { "sqlite3_initialize", (Tcl_ObjCmdProc*)test_initialize }, 366 { "sqlite3_config", (Tcl_ObjCmdProc*)test_config }, 367 368 { "alloc_dealloc_mutex", (Tcl_ObjCmdProc*)test_alloc_mutex }, 369 { "install_mutex_counters", (Tcl_ObjCmdProc*)test_install_mutex_counters }, 370 { "read_mutex_counters", (Tcl_ObjCmdProc*)test_read_mutex_counters }, 371 { "clear_mutex_counters", (Tcl_ObjCmdProc*)test_clear_mutex_counters }, 372 }; 373 int i; 374 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ 375 Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); 376 } 377 memset(&g, 0, sizeof(g)); 378 379 Tcl_LinkVar(interp, "disable_mutex_init", 380 (char*)&g.disableInit, TCL_LINK_INT); 381 Tcl_LinkVar(interp, "disable_mutex_try", 382 (char*)&g.disableTry, TCL_LINK_INT); 383 return SQLITE_OK; 384 } 385