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.6 2008/07/08 02:12:37 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 isInit; /* True if initialized */ 36 sqlite3_mutex_methods m; /* Interface to "real" mutex system */ 37 int aCounter[8]; /* Number of grabs of each type of mutex */ 38 sqlite3_mutex aStatic[6]; /* The six static mutexes */ 39 } g; 40 41 /* Return true if the countable mutex is currently held */ 42 static int counterMutexHeld(sqlite3_mutex *p){ 43 return g.m.xMutexHeld(p->pReal); 44 } 45 46 /* Return true if the countable mutex is not currently held */ 47 static int counterMutexNotheld(sqlite3_mutex *p){ 48 return g.m.xMutexNotheld(p->pReal); 49 } 50 51 /* Initialize the countable mutex interface 52 ** Or, if g.disableInit is non-zero, then do not initialize but instead 53 ** return the value of g.disableInit as the result code. This can be used 54 ** to simulate an initialization failure. 55 */ 56 static int counterMutexInit(void){ 57 int rc; 58 if( g.disableInit ) return g.disableInit; 59 rc = g.m.xMutexInit(); 60 g.isInit = 1; 61 return rc; 62 } 63 64 /* 65 ** Uninitialize the mutex subsystem 66 */ 67 static int counterMutexEnd(void){ 68 g.isInit = 0; 69 return g.m.xMutexEnd(); 70 } 71 72 /* 73 ** Allocate a countable mutex 74 */ 75 static sqlite3_mutex *counterMutexAlloc(int eType){ 76 sqlite3_mutex *pReal; 77 sqlite3_mutex *pRet = 0; 78 79 assert( g.isInit ); 80 assert(eType<8 && eType>=0); 81 82 pReal = g.m.xMutexAlloc(eType); 83 if( !pReal ) return 0; 84 85 if( eType==SQLITE_MUTEX_FAST || eType==SQLITE_MUTEX_RECURSIVE ){ 86 pRet = (sqlite3_mutex *)malloc(sizeof(sqlite3_mutex)); 87 }else{ 88 pRet = &g.aStatic[eType-2]; 89 } 90 91 pRet->eType = eType; 92 pRet->pReal = pReal; 93 return pRet; 94 } 95 96 /* 97 ** Free a countable mutex 98 */ 99 static void counterMutexFree(sqlite3_mutex *p){ 100 assert( g.isInit ); 101 g.m.xMutexFree(p->pReal); 102 if( p->eType==SQLITE_MUTEX_FAST || p->eType==SQLITE_MUTEX_RECURSIVE ){ 103 free(p); 104 } 105 } 106 107 /* 108 ** Enter a countable mutex. Block until entry is safe. 109 */ 110 static void counterMutexEnter(sqlite3_mutex *p){ 111 assert( g.isInit ); 112 g.aCounter[p->eType]++; 113 g.m.xMutexEnter(p->pReal); 114 } 115 116 /* 117 ** Try to enter a mutex. Return true on success. 118 */ 119 static int counterMutexTry(sqlite3_mutex *p){ 120 assert( g.isInit ); 121 g.aCounter[p->eType]++; 122 return g.m.xMutexTry(p->pReal); 123 } 124 125 /* Leave a mutex 126 */ 127 static void counterMutexLeave(sqlite3_mutex *p){ 128 assert( g.isInit ); 129 g.m.xMutexLeave(p->pReal); 130 } 131 132 /* 133 ** sqlite3_shutdown 134 */ 135 static int test_shutdown( 136 void * clientData, 137 Tcl_Interp *interp, 138 int objc, 139 Tcl_Obj *CONST objv[] 140 ){ 141 int rc; 142 143 if( objc!=1 ){ 144 Tcl_WrongNumArgs(interp, 1, objv, ""); 145 return TCL_ERROR; 146 } 147 148 rc = sqlite3_shutdown(); 149 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); 150 return TCL_OK; 151 } 152 153 /* 154 ** sqlite3_initialize 155 */ 156 static int test_initialize( 157 void * clientData, 158 Tcl_Interp *interp, 159 int objc, 160 Tcl_Obj *CONST objv[] 161 ){ 162 int rc; 163 164 if( objc!=1 ){ 165 Tcl_WrongNumArgs(interp, 1, objv, ""); 166 return TCL_ERROR; 167 } 168 169 rc = sqlite3_initialize(); 170 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); 171 return TCL_OK; 172 } 173 174 /* 175 ** install_mutex_counters BOOLEAN 176 */ 177 static int test_install_mutex_counters( 178 void * clientData, 179 Tcl_Interp *interp, 180 int objc, 181 Tcl_Obj *CONST objv[] 182 ){ 183 int rc = SQLITE_OK; 184 int isInstall; 185 186 sqlite3_mutex_methods counter_methods = { 187 counterMutexInit, 188 counterMutexEnd, 189 counterMutexAlloc, 190 counterMutexFree, 191 counterMutexEnter, 192 counterMutexTry, 193 counterMutexLeave, 194 counterMutexHeld, 195 counterMutexNotheld 196 }; 197 198 if( objc!=2 ){ 199 Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN"); 200 return TCL_ERROR; 201 } 202 if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[1], &isInstall) ){ 203 return TCL_ERROR; 204 } 205 206 assert(isInstall==0 || isInstall==1); 207 assert(g.isInstalled==0 || g.isInstalled==1); 208 if( isInstall==g.isInstalled ){ 209 Tcl_AppendResult(interp, "mutex counters are ", 0); 210 Tcl_AppendResult(interp, isInstall?"already installed":"not installed", 0); 211 return TCL_ERROR; 212 } 213 214 if( isInstall ){ 215 assert( g.m.xMutexAlloc==0 ); 216 rc = sqlite3_config(SQLITE_CONFIG_GETMUTEX, &g.m); 217 if( rc==SQLITE_OK ){ 218 sqlite3_config(SQLITE_CONFIG_MUTEX, &counter_methods); 219 } 220 }else{ 221 assert( g.m.xMutexAlloc ); 222 rc = sqlite3_config(SQLITE_CONFIG_MUTEX, &g.m); 223 memset(&g.m, 0, sizeof(sqlite3_mutex_methods)); 224 } 225 226 if( rc==SQLITE_OK ){ 227 g.isInstalled = isInstall; 228 } 229 230 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); 231 return TCL_OK; 232 } 233 234 /* 235 ** read_mutex_counters 236 */ 237 static int test_read_mutex_counters( 238 void * clientData, 239 Tcl_Interp *interp, 240 int objc, 241 Tcl_Obj *CONST objv[] 242 ){ 243 Tcl_Obj *pRet; 244 int ii; 245 char *aName[8] = { 246 "fast", "recursive", "static_master", "static_mem", 247 "static_mem2", "static_prng", "static_lru", "static_lru2" 248 }; 249 250 if( objc!=1 ){ 251 Tcl_WrongNumArgs(interp, 1, objv, ""); 252 return TCL_ERROR; 253 } 254 255 pRet = Tcl_NewObj(); 256 Tcl_IncrRefCount(pRet); 257 for(ii=0; ii<8; ii++){ 258 Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(aName[ii], -1)); 259 Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(g.aCounter[ii])); 260 } 261 Tcl_SetObjResult(interp, pRet); 262 Tcl_DecrRefCount(pRet); 263 264 return TCL_OK; 265 } 266 267 /* 268 ** clear_mutex_counters 269 */ 270 static int test_clear_mutex_counters( 271 void * clientData, 272 Tcl_Interp *interp, 273 int objc, 274 Tcl_Obj *CONST objv[] 275 ){ 276 int ii; 277 278 if( objc!=1 ){ 279 Tcl_WrongNumArgs(interp, 1, objv, ""); 280 return TCL_ERROR; 281 } 282 283 for(ii=0; ii<8; ii++){ 284 g.aCounter[ii] = 0; 285 } 286 return TCL_OK; 287 } 288 289 /* 290 ** Create and free a mutex. Return the mutex pointer. The pointer 291 ** will be invalid since the mutex has already been freed. The 292 ** return pointer just checks to see if the mutex really was allocated. 293 */ 294 static int test_alloc_mutex( 295 void * clientData, 296 Tcl_Interp *interp, 297 int objc, 298 Tcl_Obj *CONST objv[] 299 ){ 300 sqlite3_mutex *p = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); 301 char zBuf[100]; 302 sqlite3_mutex_free(p); 303 sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", p); 304 Tcl_AppendResult(interp, zBuf, (char*)0); 305 return TCL_OK; 306 } 307 308 /* 309 ** sqlite3_config OPTION 310 */ 311 static int test_config( 312 void * clientData, 313 Tcl_Interp *interp, 314 int objc, 315 Tcl_Obj *CONST objv[] 316 ){ 317 struct ConfigOption { 318 const char *zName; 319 int iValue; 320 } aOpt[] = { 321 {"singlethread", SQLITE_CONFIG_SINGLETHREAD}, 322 {"multithread", SQLITE_CONFIG_MULTITHREAD}, 323 {"serialized", SQLITE_CONFIG_SERIALIZED}, 324 {0, 0} 325 }; 326 int s = sizeof(struct ConfigOption); 327 int i; 328 int rc; 329 330 if( objc!=2 ){ 331 Tcl_WrongNumArgs(interp, 1, objv, ""); 332 return TCL_ERROR; 333 } 334 335 if( Tcl_GetIndexFromObjStruct(interp, objv[1], aOpt, s, "flag", 0, &i) ){ 336 return TCL_ERROR; 337 } 338 339 rc = sqlite3_config(aOpt[i].iValue); 340 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); 341 return TCL_OK; 342 } 343 344 int Sqlitetest_mutex_Init(Tcl_Interp *interp){ 345 static struct { 346 char *zName; 347 Tcl_ObjCmdProc *xProc; 348 } aCmd[] = { 349 { "sqlite3_shutdown", (Tcl_ObjCmdProc*)test_shutdown }, 350 { "sqlite3_initialize", (Tcl_ObjCmdProc*)test_initialize }, 351 { "sqlite3_config", (Tcl_ObjCmdProc*)test_config }, 352 353 { "alloc_dealloc_mutex", (Tcl_ObjCmdProc*)test_alloc_mutex }, 354 { "install_mutex_counters", (Tcl_ObjCmdProc*)test_install_mutex_counters }, 355 { "read_mutex_counters", (Tcl_ObjCmdProc*)test_read_mutex_counters }, 356 { "clear_mutex_counters", (Tcl_ObjCmdProc*)test_clear_mutex_counters }, 357 }; 358 int i; 359 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ 360 Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); 361 } 362 memset(&g, 0, sizeof(g)); 363 364 Tcl_LinkVar(interp, "disable_mutex_init", 365 (char*)&g.disableInit, TCL_LINK_INT); 366 return SQLITE_OK; 367 } 368