xref: /sqlite-3.40.0/src/test_async.c (revision 4598b8e4)
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.60 2009/04/24 10:13:06 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     Tcl_AppendResult(interp, "Tcl_CreateThread() failed", 0);
164     return TCL_ERROR;
165   }
166 
167   while( isStarted==0 ) { /* Busy loop */ }
168   return TCL_OK;
169 }
170 
171 /*
172 ** sqlite3async_wait
173 **
174 ** Wait for the current writer thread to terminate.
175 **
176 ** If the current writer thread is set to run forever then this
177 ** command would block forever.  To prevent that, an error is returned.
178 */
179 static int testAsyncWait(
180   void * clientData,
181   Tcl_Interp *interp,
182   int objc,
183   Tcl_Obj *CONST objv[]
184 ){
185   int eCond;
186   if( objc!=1 ){
187     Tcl_WrongNumArgs(interp, 1, objv, "");
188     return TCL_ERROR;
189   }
190 
191   sqlite3async_control(SQLITEASYNC_GET_HALT, &eCond);
192   if( eCond==SQLITEASYNC_HALT_NEVER ){
193     Tcl_AppendResult(interp, "would block forever", (char*)0);
194     return TCL_ERROR;
195   }
196 
197   Tcl_MutexLock(&testasync_g_writerMutex);
198   Tcl_MutexUnlock(&testasync_g_writerMutex);
199   return TCL_OK;
200 }
201 
202 #endif  /* SQLITE_ENABLE_ASYNCIO */
203 
204 /*
205 ** This routine registers the custom TCL commands defined in this
206 ** module.  This should be the only procedure visible from outside
207 ** of this module.
208 */
209 int Sqlitetestasync_Init(Tcl_Interp *interp){
210 #if SQLITE_ENABLE_ASYNCIO
211   Tcl_CreateObjCommand(interp,"sqlite3async_enable",testAsyncEnable,0,0);
212   Tcl_CreateObjCommand(interp,"sqlite3async_halt",testAsyncHalt,0,0);
213   Tcl_CreateObjCommand(interp,"sqlite3async_delay",testAsyncDelay,0,0);
214   Tcl_CreateObjCommand(interp,"sqlite3async_start",testAsyncStart,0,0);
215   Tcl_CreateObjCommand(interp,"sqlite3async_wait",testAsyncWait,0,0);
216 #endif  /* SQLITE_ENABLE_ASYNCIO */
217   return TCL_OK;
218 }
219 
220