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