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.7 2008/07/10 17:52:49 danielk1977 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 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 return g.m.xMutexTry(p->pReal); 124 } 125 126 /* Leave a mutex 127 */ 128 static void counterMutexLeave(sqlite3_mutex *p){ 129 assert( g.isInit ); 130 g.m.xMutexLeave(p->pReal); 131 } 132 133 /* 134 ** sqlite3_shutdown 135 */ 136 static int test_shutdown( 137 void * clientData, 138 Tcl_Interp *interp, 139 int objc, 140 Tcl_Obj *CONST objv[] 141 ){ 142 int rc; 143 144 if( objc!=1 ){ 145 Tcl_WrongNumArgs(interp, 1, objv, ""); 146 return TCL_ERROR; 147 } 148 149 rc = sqlite3_shutdown(); 150 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); 151 return TCL_OK; 152 } 153 154 /* 155 ** sqlite3_initialize 156 */ 157 static int test_initialize( 158 void * clientData, 159 Tcl_Interp *interp, 160 int objc, 161 Tcl_Obj *CONST objv[] 162 ){ 163 int rc; 164 165 if( objc!=1 ){ 166 Tcl_WrongNumArgs(interp, 1, objv, ""); 167 return TCL_ERROR; 168 } 169 170 rc = sqlite3_initialize(); 171 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); 172 return TCL_OK; 173 } 174 175 /* 176 ** install_mutex_counters BOOLEAN 177 */ 178 static int test_install_mutex_counters( 179 void * clientData, 180 Tcl_Interp *interp, 181 int objc, 182 Tcl_Obj *CONST objv[] 183 ){ 184 int rc = SQLITE_OK; 185 int isInstall; 186 187 sqlite3_mutex_methods counter_methods = { 188 counterMutexInit, 189 counterMutexEnd, 190 counterMutexAlloc, 191 counterMutexFree, 192 counterMutexEnter, 193 counterMutexTry, 194 counterMutexLeave, 195 counterMutexHeld, 196 counterMutexNotheld 197 }; 198 199 if( objc!=2 ){ 200 Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN"); 201 return TCL_ERROR; 202 } 203 if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[1], &isInstall) ){ 204 return TCL_ERROR; 205 } 206 207 assert(isInstall==0 || isInstall==1); 208 assert(g.isInstalled==0 || g.isInstalled==1); 209 if( isInstall==g.isInstalled ){ 210 Tcl_AppendResult(interp, "mutex counters are ", 0); 211 Tcl_AppendResult(interp, isInstall?"already installed":"not installed", 0); 212 return TCL_ERROR; 213 } 214 215 if( isInstall ){ 216 assert( g.m.xMutexAlloc==0 ); 217 rc = sqlite3_config(SQLITE_CONFIG_GETMUTEX, &g.m); 218 if( rc==SQLITE_OK ){ 219 sqlite3_config(SQLITE_CONFIG_MUTEX, &counter_methods); 220 } 221 }else{ 222 assert( g.m.xMutexAlloc ); 223 rc = sqlite3_config(SQLITE_CONFIG_MUTEX, &g.m); 224 memset(&g.m, 0, sizeof(sqlite3_mutex_methods)); 225 } 226 227 if( rc==SQLITE_OK ){ 228 g.isInstalled = isInstall; 229 } 230 231 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); 232 return TCL_OK; 233 } 234 235 /* 236 ** read_mutex_counters 237 */ 238 static int test_read_mutex_counters( 239 void * clientData, 240 Tcl_Interp *interp, 241 int objc, 242 Tcl_Obj *CONST objv[] 243 ){ 244 Tcl_Obj *pRet; 245 int ii; 246 char *aName[8] = { 247 "fast", "recursive", "static_master", "static_mem", 248 "static_mem2", "static_prng", "static_lru", "static_lru2" 249 }; 250 251 if( objc!=1 ){ 252 Tcl_WrongNumArgs(interp, 1, objv, ""); 253 return TCL_ERROR; 254 } 255 256 pRet = Tcl_NewObj(); 257 Tcl_IncrRefCount(pRet); 258 for(ii=0; ii<8; ii++){ 259 Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(aName[ii], -1)); 260 Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(g.aCounter[ii])); 261 } 262 Tcl_SetObjResult(interp, pRet); 263 Tcl_DecrRefCount(pRet); 264 265 return TCL_OK; 266 } 267 268 /* 269 ** clear_mutex_counters 270 */ 271 static int test_clear_mutex_counters( 272 void * clientData, 273 Tcl_Interp *interp, 274 int objc, 275 Tcl_Obj *CONST objv[] 276 ){ 277 int ii; 278 279 if( objc!=1 ){ 280 Tcl_WrongNumArgs(interp, 1, objv, ""); 281 return TCL_ERROR; 282 } 283 284 for(ii=0; ii<8; ii++){ 285 g.aCounter[ii] = 0; 286 } 287 return TCL_OK; 288 } 289 290 /* 291 ** Create and free a mutex. Return the mutex pointer. The pointer 292 ** will be invalid since the mutex has already been freed. The 293 ** return pointer just checks to see if the mutex really was allocated. 294 */ 295 static int test_alloc_mutex( 296 void * clientData, 297 Tcl_Interp *interp, 298 int objc, 299 Tcl_Obj *CONST objv[] 300 ){ 301 sqlite3_mutex *p = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); 302 char zBuf[100]; 303 sqlite3_mutex_free(p); 304 sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", p); 305 Tcl_AppendResult(interp, zBuf, (char*)0); 306 return TCL_OK; 307 } 308 309 /* 310 ** sqlite3_config OPTION 311 */ 312 static int test_config( 313 void * clientData, 314 Tcl_Interp *interp, 315 int objc, 316 Tcl_Obj *CONST objv[] 317 ){ 318 struct ConfigOption { 319 const char *zName; 320 int iValue; 321 } aOpt[] = { 322 {"singlethread", SQLITE_CONFIG_SINGLETHREAD}, 323 {"multithread", SQLITE_CONFIG_MULTITHREAD}, 324 {"serialized", SQLITE_CONFIG_SERIALIZED}, 325 {0, 0} 326 }; 327 int s = sizeof(struct ConfigOption); 328 int i; 329 int rc; 330 331 if( objc!=2 ){ 332 Tcl_WrongNumArgs(interp, 1, objv, ""); 333 return TCL_ERROR; 334 } 335 336 if( Tcl_GetIndexFromObjStruct(interp, objv[1], aOpt, s, "flag", 0, &i) ){ 337 return TCL_ERROR; 338 } 339 340 rc = sqlite3_config(aOpt[i].iValue); 341 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); 342 return TCL_OK; 343 } 344 345 int Sqlitetest_mutex_Init(Tcl_Interp *interp){ 346 static struct { 347 char *zName; 348 Tcl_ObjCmdProc *xProc; 349 } aCmd[] = { 350 { "sqlite3_shutdown", (Tcl_ObjCmdProc*)test_shutdown }, 351 { "sqlite3_initialize", (Tcl_ObjCmdProc*)test_initialize }, 352 { "sqlite3_config", (Tcl_ObjCmdProc*)test_config }, 353 354 { "alloc_dealloc_mutex", (Tcl_ObjCmdProc*)test_alloc_mutex }, 355 { "install_mutex_counters", (Tcl_ObjCmdProc*)test_install_mutex_counters }, 356 { "read_mutex_counters", (Tcl_ObjCmdProc*)test_read_mutex_counters }, 357 { "clear_mutex_counters", (Tcl_ObjCmdProc*)test_clear_mutex_counters }, 358 }; 359 int i; 360 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ 361 Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); 362 } 363 memset(&g, 0, sizeof(g)); 364 365 Tcl_LinkVar(interp, "disable_mutex_init", 366 (char*)&g.disableInit, TCL_LINK_INT); 367 return SQLITE_OK; 368 } 369