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