| 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 **  |  | 
|   13 ** $Id: test_mutex.c,v 1.15 2009/03/20 13:15:30 drh Exp $ |  | 
|   14 */ |  | 
|   15  |  | 
|   16 #include "tcl.h" |  | 
|   17 #include "sqlite3.h" |  | 
|   18 #include "sqliteInt.h" |  | 
|   19 #include <stdlib.h> |  | 
|   20 #include <assert.h> |  | 
|   21 #include <string.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 = {0}; |  | 
|   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 SQLITE_BUSY; |  | 
|  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_open", "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 #if SQLITE_THREADSAFE |  | 
|  305   sqlite3_mutex *p = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); |  | 
|  306   char zBuf[100]; |  | 
|  307   sqlite3_mutex_free(p); |  | 
|  308   sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", p); |  | 
|  309   Tcl_AppendResult(interp, zBuf, (char*)0); |  | 
|  310 #endif |  | 
|  311   return TCL_OK; |  | 
|  312 } |  | 
|  313  |  | 
|  314 /* |  | 
|  315 ** sqlite3_config OPTION |  | 
|  316 ** |  | 
|  317 ** OPTION can be either one of the keywords: |  | 
|  318 ** |  | 
|  319 **            SQLITE_CONFIG_SINGLETHREAD |  | 
|  320 **            SQLITE_CONFIG_MULTITHREAD |  | 
|  321 **            SQLITE_CONFIG_SERIALIZED |  | 
|  322 ** |  | 
|  323 ** Or OPTION can be an raw integer. |  | 
|  324 */ |  | 
|  325 static int test_config( |  | 
|  326   void * clientData, |  | 
|  327   Tcl_Interp *interp, |  | 
|  328   int objc, |  | 
|  329   Tcl_Obj *CONST objv[] |  | 
|  330 ){ |  | 
|  331   struct ConfigOption { |  | 
|  332     const char *zName; |  | 
|  333     int iValue; |  | 
|  334   } aOpt[] = { |  | 
|  335     {"singlethread", SQLITE_CONFIG_SINGLETHREAD}, |  | 
|  336     {"multithread",  SQLITE_CONFIG_MULTITHREAD}, |  | 
|  337     {"serialized",   SQLITE_CONFIG_SERIALIZED}, |  | 
|  338     {0, 0} |  | 
|  339   }; |  | 
|  340   int s = sizeof(struct ConfigOption); |  | 
|  341   int i; |  | 
|  342   int rc; |  | 
|  343  |  | 
|  344   if( objc!=2 ){ |  | 
|  345     Tcl_WrongNumArgs(interp, 1, objv, ""); |  | 
|  346     return TCL_ERROR; |  | 
|  347   } |  | 
|  348  |  | 
|  349   if( Tcl_GetIndexFromObjStruct(interp, objv[1], aOpt, s, "flag", 0, &i) ){ |  | 
|  350     if( Tcl_GetIntFromObj(interp, objv[1], &i) ){ |  | 
|  351       return TCL_ERROR; |  | 
|  352     } |  | 
|  353   }else{ |  | 
|  354     i = aOpt[i].iValue; |  | 
|  355   } |  | 
|  356  |  | 
|  357   rc = sqlite3_config(i); |  | 
|  358   Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); |  | 
|  359   return TCL_OK; |  | 
|  360 } |  | 
|  361  |  | 
|  362 static sqlite3 *getDbPointer(Tcl_Interp *pInterp, Tcl_Obj *pObj){ |  | 
|  363   sqlite3 *db; |  | 
|  364   Tcl_CmdInfo info; |  | 
|  365   char *zCmd = Tcl_GetString(pObj); |  | 
|  366   if( Tcl_GetCommandInfo(pInterp, zCmd, &info) ){ |  | 
|  367     db = *((sqlite3 **)info.objClientData); |  | 
|  368   }else{ |  | 
|  369     db = (sqlite3*)sqlite3TestTextToPtr(zCmd); |  | 
|  370   } |  | 
|  371   assert( db ); |  | 
|  372   return db; |  | 
|  373 } |  | 
|  374  |  | 
|  375 static int test_enter_db_mutex( |  | 
|  376   void * clientData, |  | 
|  377   Tcl_Interp *interp, |  | 
|  378   int objc, |  | 
|  379   Tcl_Obj *CONST objv[] |  | 
|  380 ){ |  | 
|  381   sqlite3 *db; |  | 
|  382   if( objc!=2 ){ |  | 
|  383     Tcl_WrongNumArgs(interp, 1, objv, "DB"); |  | 
|  384     return TCL_ERROR; |  | 
|  385   } |  | 
|  386   db = getDbPointer(interp, objv[1]); |  | 
|  387   if( !db ){ |  | 
|  388     return TCL_ERROR; |  | 
|  389   } |  | 
|  390   sqlite3_mutex_enter(sqlite3_db_mutex(db)); |  | 
|  391   return TCL_OK; |  | 
|  392 } |  | 
|  393  |  | 
|  394 static int test_leave_db_mutex( |  | 
|  395   void * clientData, |  | 
|  396   Tcl_Interp *interp, |  | 
|  397   int objc, |  | 
|  398   Tcl_Obj *CONST objv[] |  | 
|  399 ){ |  | 
|  400   sqlite3 *db; |  | 
|  401   if( objc!=2 ){ |  | 
|  402     Tcl_WrongNumArgs(interp, 1, objv, "DB"); |  | 
|  403     return TCL_ERROR; |  | 
|  404   } |  | 
|  405   db = getDbPointer(interp, objv[1]); |  | 
|  406   if( !db ){ |  | 
|  407     return TCL_ERROR; |  | 
|  408   } |  | 
|  409   sqlite3_mutex_leave(sqlite3_db_mutex(db)); |  | 
|  410   return TCL_OK; |  | 
|  411 } |  | 
|  412  |  | 
|  413 int Sqlitetest_mutex_Init(Tcl_Interp *interp){ |  | 
|  414   static struct { |  | 
|  415     char *zName; |  | 
|  416     Tcl_ObjCmdProc *xProc; |  | 
|  417   } aCmd[] = { |  | 
|  418     { "sqlite3_shutdown",        (Tcl_ObjCmdProc*)test_shutdown }, |  | 
|  419     { "sqlite3_initialize",      (Tcl_ObjCmdProc*)test_initialize }, |  | 
|  420     { "sqlite3_config",          (Tcl_ObjCmdProc*)test_config }, |  | 
|  421  |  | 
|  422     { "enter_db_mutex",          (Tcl_ObjCmdProc*)test_enter_db_mutex }, |  | 
|  423     { "leave_db_mutex",          (Tcl_ObjCmdProc*)test_leave_db_mutex }, |  | 
|  424  |  | 
|  425     { "alloc_dealloc_mutex",     (Tcl_ObjCmdProc*)test_alloc_mutex }, |  | 
|  426     { "install_mutex_counters",  (Tcl_ObjCmdProc*)test_install_mutex_counters }, |  | 
|  427     { "read_mutex_counters",     (Tcl_ObjCmdProc*)test_read_mutex_counters }, |  | 
|  428     { "clear_mutex_counters",    (Tcl_ObjCmdProc*)test_clear_mutex_counters }, |  | 
|  429   }; |  | 
|  430   int i; |  | 
|  431   for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ |  | 
|  432     Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); |  | 
|  433   } |  | 
|  434  |  | 
|  435   Tcl_LinkVar(interp, "disable_mutex_init",  |  | 
|  436               (char*)&g.disableInit, TCL_LINK_INT); |  | 
|  437   Tcl_LinkVar(interp, "disable_mutex_try",  |  | 
|  438               (char*)&g.disableTry, TCL_LINK_INT); |  | 
|  439   return SQLITE_OK; |  | 
|  440 } |  | 
| OLD | NEW |