Home | History | Annotate | Download | only in src
      1 /*
      2 ** 2005 December 14
      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 ** This file contains a binding of the asynchronous IO extension interface
     14 ** (defined in ext/async/sqlite3async.h) to Tcl.
     15 */
     16 
     17 #define TCL_THREADS
     18 #include <tcl.h>
     19 
     20 #ifdef SQLITE_ENABLE_ASYNCIO
     21 
     22 #include "sqlite3async.h"
     23 #include "sqlite3.h"
     24 #include <assert.h>
     25 
     26 /* From test1.c */
     27 const char *sqlite3TestErrorName(int);
     28 
     29 
     30 struct TestAsyncGlobal {
     31   int isInstalled;                     /* True when async VFS is installed */
     32 } testasync_g = { 0 };
     33 
     34 TCL_DECLARE_MUTEX(testasync_g_writerMutex);
     35 
     36 /*
     37 ** sqlite3async_initialize PARENT-VFS ISDEFAULT
     38 */
     39 static int testAsyncInit(
     40   void * clientData,
     41   Tcl_Interp *interp,
     42   int objc,
     43   Tcl_Obj *CONST objv[]
     44 ){
     45   const char *zParent;
     46   int isDefault;
     47   int rc;
     48 
     49   if( objc!=3 ){
     50     Tcl_WrongNumArgs(interp, 1, objv, "PARENT-VFS ISDEFAULT");
     51     return TCL_ERROR;
     52   }
     53   zParent = Tcl_GetString(objv[1]);
     54   if( !*zParent ) {
     55     zParent = 0;
     56   }
     57   if( Tcl_GetBooleanFromObj(interp, objv[2], &isDefault) ){
     58     return TCL_ERROR;
     59   }
     60 
     61   rc = sqlite3async_initialize(zParent, isDefault);
     62   if( rc!=SQLITE_OK ){
     63     Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3TestErrorName(rc), -1));
     64     return TCL_ERROR;
     65   }
     66   return TCL_OK;
     67 }
     68 
     69 /*
     70 ** sqlite3async_shutdown
     71 */
     72 static int testAsyncShutdown(
     73   void * clientData,
     74   Tcl_Interp *interp,
     75   int objc,
     76   Tcl_Obj *CONST objv[]
     77 ){
     78   sqlite3async_shutdown();
     79   return TCL_OK;
     80 }
     81 
     82 static Tcl_ThreadCreateType tclWriterThread(ClientData pIsStarted){
     83   Tcl_MutexLock(&testasync_g_writerMutex);
     84   *((int *)pIsStarted) = 1;
     85   sqlite3async_run();
     86   Tcl_MutexUnlock(&testasync_g_writerMutex);
     87   Tcl_ExitThread(0);
     88   TCL_THREAD_CREATE_RETURN;
     89 }
     90 
     91 /*
     92 ** sqlite3async_start
     93 **
     94 ** Start a new writer thread.
     95 */
     96 static int testAsyncStart(
     97   void * clientData,
     98   Tcl_Interp *interp,
     99   int objc,
    100   Tcl_Obj *CONST objv[]
    101 ){
    102   volatile int isStarted = 0;
    103   ClientData threadData = (ClientData)&isStarted;
    104 
    105   Tcl_ThreadId x;
    106   const int nStack = TCL_THREAD_STACK_DEFAULT;
    107   const int flags = TCL_THREAD_NOFLAGS;
    108   int rc;
    109 
    110   rc = Tcl_CreateThread(&x, tclWriterThread, threadData, nStack, flags);
    111   if( rc!=TCL_OK ){
    112     Tcl_AppendResult(interp, "Tcl_CreateThread() failed", 0);
    113     return TCL_ERROR;
    114   }
    115 
    116   while( isStarted==0 ) { /* Busy loop */ }
    117   return TCL_OK;
    118 }
    119 
    120 /*
    121 ** sqlite3async_wait
    122 **
    123 ** Wait for the current writer thread to terminate.
    124 **
    125 ** If the current writer thread is set to run forever then this
    126 ** command would block forever.  To prevent that, an error is returned.
    127 */
    128 static int testAsyncWait(
    129   void * clientData,
    130   Tcl_Interp *interp,
    131   int objc,
    132   Tcl_Obj *CONST objv[]
    133 ){
    134   int eCond;
    135   if( objc!=1 ){
    136     Tcl_WrongNumArgs(interp, 1, objv, "");
    137     return TCL_ERROR;
    138   }
    139 
    140   sqlite3async_control(SQLITEASYNC_GET_HALT, &eCond);
    141   if( eCond==SQLITEASYNC_HALT_NEVER ){
    142     Tcl_AppendResult(interp, "would block forever", (char*)0);
    143     return TCL_ERROR;
    144   }
    145 
    146   Tcl_MutexLock(&testasync_g_writerMutex);
    147   Tcl_MutexUnlock(&testasync_g_writerMutex);
    148   return TCL_OK;
    149 }
    150 
    151 /*
    152 ** sqlite3async_control OPTION ?VALUE?
    153 */
    154 static int testAsyncControl(
    155   void * clientData,
    156   Tcl_Interp *interp,
    157   int objc,
    158   Tcl_Obj *CONST objv[]
    159 ){
    160   int rc = SQLITE_OK;
    161   int aeOpt[] = { SQLITEASYNC_HALT, SQLITEASYNC_DELAY, SQLITEASYNC_LOCKFILES };
    162   const char *azOpt[] = { "halt", "delay", "lockfiles", 0 };
    163   const char *az[] = { "never", "now", "idle", 0 };
    164   int iVal;
    165   int eOpt;
    166 
    167   if( objc!=2 && objc!=3 ){
    168     Tcl_WrongNumArgs(interp, 1, objv, "OPTION ?VALUE?");
    169     return TCL_ERROR;
    170   }
    171   if( Tcl_GetIndexFromObj(interp, objv[1], azOpt, "option", 0, &eOpt) ){
    172     return TCL_ERROR;
    173   }
    174   eOpt = aeOpt[eOpt];
    175 
    176   if( objc==3 ){
    177     switch( eOpt ){
    178       case SQLITEASYNC_HALT: {
    179         assert( SQLITEASYNC_HALT_NEVER==0 );
    180         assert( SQLITEASYNC_HALT_NOW==1 );
    181         assert( SQLITEASYNC_HALT_IDLE==2 );
    182         if( Tcl_GetIndexFromObj(interp, objv[2], az, "value", 0, &iVal) ){
    183           return TCL_ERROR;
    184         }
    185         break;
    186       }
    187       case SQLITEASYNC_DELAY:
    188         if( Tcl_GetIntFromObj(interp, objv[2], &iVal) ){
    189           return TCL_ERROR;
    190         }
    191         break;
    192 
    193       case SQLITEASYNC_LOCKFILES:
    194         if( Tcl_GetBooleanFromObj(interp, objv[2], &iVal) ){
    195           return TCL_ERROR;
    196         }
    197         break;
    198     }
    199 
    200     rc = sqlite3async_control(eOpt, iVal);
    201   }
    202 
    203   if( rc==SQLITE_OK ){
    204     rc = sqlite3async_control(
    205         eOpt==SQLITEASYNC_HALT ? SQLITEASYNC_GET_HALT :
    206         eOpt==SQLITEASYNC_DELAY ? SQLITEASYNC_GET_DELAY :
    207         SQLITEASYNC_GET_LOCKFILES, &iVal);
    208   }
    209 
    210   if( rc!=SQLITE_OK ){
    211     Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3TestErrorName(rc), -1));
    212     return TCL_ERROR;
    213   }
    214 
    215   if( eOpt==SQLITEASYNC_HALT ){
    216     Tcl_SetObjResult(interp, Tcl_NewStringObj(az[iVal], -1));
    217   }else{
    218     Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal));
    219   }
    220 
    221   return TCL_OK;
    222 }
    223 
    224 #endif  /* SQLITE_ENABLE_ASYNCIO */
    225 
    226 /*
    227 ** This routine registers the custom TCL commands defined in this
    228 ** module.  This should be the only procedure visible from outside
    229 ** of this module.
    230 */
    231 int Sqlitetestasync_Init(Tcl_Interp *interp){
    232 #ifdef SQLITE_ENABLE_ASYNCIO
    233   Tcl_CreateObjCommand(interp,"sqlite3async_start",testAsyncStart,0,0);
    234   Tcl_CreateObjCommand(interp,"sqlite3async_wait",testAsyncWait,0,0);
    235 
    236   Tcl_CreateObjCommand(interp,"sqlite3async_control",testAsyncControl,0,0);
    237   Tcl_CreateObjCommand(interp,"sqlite3async_initialize",testAsyncInit,0,0);
    238   Tcl_CreateObjCommand(interp,"sqlite3async_shutdown",testAsyncShutdown,0,0);
    239 #endif  /* SQLITE_ENABLE_ASYNCIO */
    240   return TCL_OK;
    241 }
    242