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 */ 27const char *sqlite3TestErrorName(int); 28 29 30struct TestAsyncGlobal { 31 int isInstalled; /* True when async VFS is installed */ 32} testasync_g = { 0 }; 33 34TCL_DECLARE_MUTEX(testasync_g_writerMutex); 35 36/* 37** sqlite3async_initialize PARENT-VFS ISDEFAULT 38*/ 39static 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*/ 72static 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 82static 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*/ 96static 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*/ 128static 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*/ 154static 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*/ 231int 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