OLD | NEW |
(Empty) | |
| 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 } |
OLD | NEW |