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_THREAD_CREATE_RETURN; 88 } 89 90 /* 91 ** sqlite3async_start 92 ** 93 ** Start a new writer thread. 94 */ 95 static int testAsyncStart( 96 void * clientData, 97 Tcl_Interp *interp, 98 int objc, 99 Tcl_Obj *CONST objv[] 100 ){ 101 volatile int isStarted = 0; 102 ClientData threadData = (ClientData)&isStarted; 103 104 Tcl_ThreadId x; 105 const int nStack = TCL_THREAD_STACK_DEFAULT; 106 const int flags = TCL_THREAD_NOFLAGS; 107 int rc; 108 109 rc = Tcl_CreateThread(&x, tclWriterThread, threadData, nStack, flags); 110 if( rc!=TCL_OK ){ 111 Tcl_AppendResult(interp, "Tcl_CreateThread() failed", 0); 112 return TCL_ERROR; 113 } 114 115 while( isStarted==0 ) { /* Busy loop */ } 116 return TCL_OK; 117 } 118 119 /* 120 ** sqlite3async_wait 121 ** 122 ** Wait for the current writer thread to terminate. 123 ** 124 ** If the current writer thread is set to run forever then this 125 ** command would block forever. To prevent that, an error is returned. 126 */ 127 static int testAsyncWait( 128 void * clientData, 129 Tcl_Interp *interp, 130 int objc, 131 Tcl_Obj *CONST objv[] 132 ){ 133 int eCond; 134 if( objc!=1 ){ 135 Tcl_WrongNumArgs(interp, 1, objv, ""); 136 return TCL_ERROR; 137 } 138 139 sqlite3async_control(SQLITEASYNC_GET_HALT, &eCond); 140 if( eCond==SQLITEASYNC_HALT_NEVER ){ 141 Tcl_AppendResult(interp, "would block forever", (char*)0); 142 return TCL_ERROR; 143 } 144 145 Tcl_MutexLock(&testasync_g_writerMutex); 146 Tcl_MutexUnlock(&testasync_g_writerMutex); 147 return TCL_OK; 148 } 149 150 /* 151 ** sqlite3async_control OPTION ?VALUE? 152 */ 153 static int testAsyncControl( 154 void * clientData, 155 Tcl_Interp *interp, 156 int objc, 157 Tcl_Obj *CONST objv[] 158 ){ 159 int rc = SQLITE_OK; 160 int aeOpt[] = { SQLITEASYNC_HALT, SQLITEASYNC_DELAY, SQLITEASYNC_LOCKFILES }; 161 const char *azOpt[] = { "halt", "delay", "lockfiles", 0 }; 162 const char *az[] = { "never", "now", "idle", 0 }; 163 int iVal; 164 int eOpt; 165 166 if( objc!=2 && objc!=3 ){ 167 Tcl_WrongNumArgs(interp, 1, objv, "OPTION ?VALUE?"); 168 return TCL_ERROR; 169 } 170 if( Tcl_GetIndexFromObj(interp, objv[1], azOpt, "option", 0, &eOpt) ){ 171 return TCL_ERROR; 172 } 173 eOpt = aeOpt[eOpt]; 174 175 if( objc==3 ){ 176 switch( eOpt ){ 177 case SQLITEASYNC_HALT: { 178 assert( SQLITEASYNC_HALT_NEVER==0 ); 179 assert( SQLITEASYNC_HALT_NOW==1 ); 180 assert( SQLITEASYNC_HALT_IDLE==2 ); 181 if( Tcl_GetIndexFromObj(interp, objv[2], az, "value", 0, &iVal) ){ 182 return TCL_ERROR; 183 } 184 break; 185 } 186 case SQLITEASYNC_DELAY: 187 if( Tcl_GetIntFromObj(interp, objv[2], &iVal) ){ 188 return TCL_ERROR; 189 } 190 break; 191 192 case SQLITEASYNC_LOCKFILES: 193 if( Tcl_GetBooleanFromObj(interp, objv[2], &iVal) ){ 194 return TCL_ERROR; 195 } 196 break; 197 } 198 199 rc = sqlite3async_control(eOpt, iVal); 200 } 201 202 if( rc==SQLITE_OK ){ 203 rc = sqlite3async_control( 204 eOpt==SQLITEASYNC_HALT ? SQLITEASYNC_GET_HALT : 205 eOpt==SQLITEASYNC_DELAY ? SQLITEASYNC_GET_DELAY : 206 SQLITEASYNC_GET_LOCKFILES, &iVal); 207 } 208 209 if( rc!=SQLITE_OK ){ 210 Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3TestErrorName(rc), -1)); 211 return TCL_ERROR; 212 } 213 214 if( eOpt==SQLITEASYNC_HALT ){ 215 Tcl_SetObjResult(interp, Tcl_NewStringObj(az[iVal], -1)); 216 }else{ 217 Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal)); 218 } 219 220 return TCL_OK; 221 } 222 223 #endif /* SQLITE_ENABLE_ASYNCIO */ 224 225 /* 226 ** This routine registers the custom TCL commands defined in this 227 ** module. This should be the only procedure visible from outside 228 ** of this module. 229 */ 230 int Sqlitetestasync_Init(Tcl_Interp *interp){ 231 #if SQLITE_ENABLE_ASYNCIO 232 Tcl_CreateObjCommand(interp,"sqlite3async_start",testAsyncStart,0,0); 233 Tcl_CreateObjCommand(interp,"sqlite3async_wait",testAsyncWait,0,0); 234 235 Tcl_CreateObjCommand(interp,"sqlite3async_control",testAsyncControl,0,0); 236 Tcl_CreateObjCommand(interp,"sqlite3async_initialize",testAsyncInit,0,0); 237 Tcl_CreateObjCommand(interp,"sqlite3async_shutdown",testAsyncShutdown,0,0); 238 #endif /* SQLITE_ENABLE_ASYNCIO */ 239 return TCL_OK; 240 } 241