xref: /sqlite-3.40.0/src/test_async.c (revision a3f06598)
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