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