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