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