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