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.59 2009/04/23 14:58:40 danielk1977 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 29 struct TestAsyncGlobal { 30 int isInstalled; /* True when async VFS is installed */ 31 } testasync_g = { 0 }; 32 33 TCL_DECLARE_MUTEX(testasync_g_writerMutex); 34 35 /* 36 ** sqlite3async_enable ?YES/NO? 37 ** 38 ** Enable or disable the asynchronous I/O backend. This command is 39 ** not thread-safe. Do not call it while any database connections 40 ** are open. 41 */ 42 static int testAsyncEnable( 43 void * clientData, 44 Tcl_Interp *interp, 45 int objc, 46 Tcl_Obj *CONST objv[] 47 ){ 48 if( objc!=1 && objc!=2 ){ 49 Tcl_WrongNumArgs(interp, 1, objv, "?YES/NO?"); 50 return TCL_ERROR; 51 } 52 if( objc==1 ){ 53 Tcl_SetObjResult(interp, Tcl_NewIntObj(testasync_g.isInstalled)); 54 }else{ 55 int enable; 56 if( Tcl_GetBooleanFromObj(interp, objv[1], &enable) ) return TCL_ERROR; 57 if( enable ){ 58 sqlite3async_initialize(0, 1); 59 }else{ 60 sqlite3async_shutdown(); 61 } 62 testasync_g.isInstalled = enable; 63 } 64 return TCL_OK; 65 } 66 67 /* 68 ** sqlite3async_halt ?"now"|"idle"|"never"? 69 ** 70 ** Set the conditions at which the writer thread will halt. 71 */ 72 static int testAsyncHalt( 73 void * clientData, 74 Tcl_Interp *interp, 75 int objc, 76 Tcl_Obj *CONST objv[] 77 ){ 78 int eWhen; 79 const char *azConstant[] = { "never", "now", "idle", 0 }; 80 81 assert( SQLITEASYNC_HALT_NEVER==0 ); 82 assert( SQLITEASYNC_HALT_NOW==1 ); 83 assert( SQLITEASYNC_HALT_IDLE==2 ); 84 85 if( objc!=1 && objc!=2 ){ 86 Tcl_WrongNumArgs(interp, 1, objv, "?OPTION?"); 87 return TCL_ERROR; 88 } 89 if( objc==2 ){ 90 if( Tcl_GetIndexFromObj(interp, objv[1], azConstant, "option", 0, &eWhen) ){ 91 return TCL_ERROR; 92 } 93 sqlite3async_control(SQLITEASYNC_HALT, eWhen); 94 } 95 96 /* Always return the current value of the 'halt' option. */ 97 sqlite3async_control(SQLITEASYNC_GET_HALT, &eWhen); 98 Tcl_SetObjResult(interp, Tcl_NewStringObj(azConstant[eWhen], -1)); 99 100 return TCL_OK; 101 } 102 103 /* 104 ** sqlite3async_delay ?MS? 105 ** 106 ** Query or set the number of milliseconds of delay in the writer 107 ** thread after each write operation. The default is 0. By increasing 108 ** the memory delay we can simulate the effect of slow disk I/O. 109 */ 110 static int testAsyncDelay( 111 void * clientData, 112 Tcl_Interp *interp, 113 int objc, 114 Tcl_Obj *CONST objv[] 115 ){ 116 int iMs; 117 if( objc!=1 && objc!=2 ){ 118 Tcl_WrongNumArgs(interp, 1, objv, "?MS?"); 119 return TCL_ERROR; 120 } 121 if( objc==2 ){ 122 if( Tcl_GetIntFromObj(interp, objv[1], &iMs) ){ 123 return TCL_ERROR; 124 } 125 sqlite3async_control(SQLITEASYNC_DELAY, iMs); 126 } 127 128 /* Always return the current value of the 'delay' option. */ 129 sqlite3async_control(SQLITEASYNC_GET_DELAY, &iMs); 130 Tcl_SetObjResult(interp, Tcl_NewIntObj(iMs)); 131 return TCL_OK; 132 } 133 134 static Tcl_ThreadCreateType tclWriterThread(ClientData pIsStarted){ 135 Tcl_MutexLock(&testasync_g_writerMutex); 136 *((int *)pIsStarted) = 1; 137 sqlite3async_run(); 138 Tcl_MutexUnlock(&testasync_g_writerMutex); 139 TCL_THREAD_CREATE_RETURN; 140 } 141 142 /* 143 ** sqlite3async_start 144 ** 145 ** Start a new writer thread. 146 */ 147 static int testAsyncStart( 148 void * clientData, 149 Tcl_Interp *interp, 150 int objc, 151 Tcl_Obj *CONST objv[] 152 ){ 153 volatile int isStarted = 0; 154 ClientData threadData = (ClientData)&isStarted; 155 156 Tcl_ThreadId x; 157 const int nStack = TCL_THREAD_STACK_DEFAULT; 158 const int flags = TCL_THREAD_NOFLAGS; 159 int rc; 160 161 rc = Tcl_CreateThread(&x, tclWriterThread, threadData, nStack, flags); 162 if( rc!=TCL_OK ){ 163 return TCL_ERROR; 164 } 165 while( isStarted==0 ){ 166 #if 0 167 sched_yield(); 168 #endif 169 } 170 return TCL_OK; 171 } 172 173 /* 174 ** sqlite3async_wait 175 ** 176 ** Wait for the current writer thread to terminate. 177 ** 178 ** If the current writer thread is set to run forever then this 179 ** command would block forever. To prevent that, an error is returned. 180 */ 181 static int testAsyncWait( 182 void * clientData, 183 Tcl_Interp *interp, 184 int objc, 185 Tcl_Obj *CONST objv[] 186 ){ 187 int eCond; 188 if( objc!=1 ){ 189 Tcl_WrongNumArgs(interp, 1, objv, ""); 190 return TCL_ERROR; 191 } 192 193 sqlite3async_control(SQLITEASYNC_GET_HALT, &eCond); 194 if( eCond==SQLITEASYNC_HALT_NEVER ){ 195 Tcl_AppendResult(interp, "would block forever", (char*)0); 196 return TCL_ERROR; 197 } 198 199 Tcl_MutexLock(&testasync_g_writerMutex); 200 Tcl_MutexUnlock(&testasync_g_writerMutex); 201 return TCL_OK; 202 } 203 204 #endif /* SQLITE_ENABLE_ASYNCIO */ 205 206 /* 207 ** This routine registers the custom TCL commands defined in this 208 ** module. This should be the only procedure visible from outside 209 ** of this module. 210 */ 211 int Sqlitetestasync_Init(Tcl_Interp *interp){ 212 #if SQLITE_ENABLE_ASYNCIO 213 Tcl_CreateObjCommand(interp,"sqlite3async_enable",testAsyncEnable,0,0); 214 Tcl_CreateObjCommand(interp,"sqlite3async_halt",testAsyncHalt,0,0); 215 Tcl_CreateObjCommand(interp,"sqlite3async_delay",testAsyncDelay,0,0); 216 Tcl_CreateObjCommand(interp,"sqlite3async_start",testAsyncStart,0,0); 217 Tcl_CreateObjCommand(interp,"sqlite3async_wait",testAsyncWait,0,0); 218 #endif /* SQLITE_ENABLE_ASYNCIO */ 219 return TCL_OK; 220 } 221 222