xref: /sqlite-3.40.0/src/test_async.c (revision 52b1dbb5)
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 #endif
23 
24 #ifdef SQLITE_ENABLE_ASYNCIO
25 
26 #include "sqlite3async.h"
27 #include "sqlite3.h"
28 #include <assert.h>
29 
30 /* From main.c */
31 extern const char *sqlite3ErrName(int);
32 
33 
34 struct TestAsyncGlobal {
35   int isInstalled;                     /* True when async VFS is installed */
36 } testasync_g = { 0 };
37 
38 TCL_DECLARE_MUTEX(testasync_g_writerMutex);
39 
40 /*
41 ** sqlite3async_initialize PARENT-VFS ISDEFAULT
42 */
43 static int testAsyncInit(
44   void * clientData,
45   Tcl_Interp *interp,
46   int objc,
47   Tcl_Obj *CONST objv[]
48 ){
49   const char *zParent;
50   int isDefault;
51   int rc;
52 
53   if( objc!=3 ){
54     Tcl_WrongNumArgs(interp, 1, objv, "PARENT-VFS ISDEFAULT");
55     return TCL_ERROR;
56   }
57   zParent = Tcl_GetString(objv[1]);
58   if( !*zParent ) {
59     zParent = 0;
60   }
61   if( Tcl_GetBooleanFromObj(interp, objv[2], &isDefault) ){
62     return TCL_ERROR;
63   }
64 
65   rc = sqlite3async_initialize(zParent, isDefault);
66   if( rc!=SQLITE_OK ){
67     Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
68     return TCL_ERROR;
69   }
70   return TCL_OK;
71 }
72 
73 /*
74 ** sqlite3async_shutdown
75 */
76 static int testAsyncShutdown(
77   void * clientData,
78   Tcl_Interp *interp,
79   int objc,
80   Tcl_Obj *CONST objv[]
81 ){
82   sqlite3async_shutdown();
83   return TCL_OK;
84 }
85 
86 static Tcl_ThreadCreateType tclWriterThread(ClientData pIsStarted){
87   Tcl_MutexLock(&testasync_g_writerMutex);
88   *((int *)pIsStarted) = 1;
89   sqlite3async_run();
90   Tcl_MutexUnlock(&testasync_g_writerMutex);
91   Tcl_ExitThread(0);
92   TCL_THREAD_CREATE_RETURN;
93 }
94 
95 /*
96 ** sqlite3async_start
97 **
98 ** Start a new writer thread.
99 */
100 static int testAsyncStart(
101   void * clientData,
102   Tcl_Interp *interp,
103   int objc,
104   Tcl_Obj *CONST objv[]
105 ){
106   volatile int isStarted = 0;
107   ClientData threadData = (ClientData)&isStarted;
108 
109   Tcl_ThreadId x;
110   const int nStack = TCL_THREAD_STACK_DEFAULT;
111   const int flags = TCL_THREAD_NOFLAGS;
112   int rc;
113 
114   rc = Tcl_CreateThread(&x, tclWriterThread, threadData, nStack, flags);
115   if( rc!=TCL_OK ){
116     Tcl_AppendResult(interp, "Tcl_CreateThread() failed", 0);
117     return TCL_ERROR;
118   }
119 
120   while( isStarted==0 ) { /* Busy loop */ }
121   return TCL_OK;
122 }
123 
124 /*
125 ** sqlite3async_wait
126 **
127 ** Wait for the current writer thread to terminate.
128 **
129 ** If the current writer thread is set to run forever then this
130 ** command would block forever.  To prevent that, an error is returned.
131 */
132 static int testAsyncWait(
133   void * clientData,
134   Tcl_Interp *interp,
135   int objc,
136   Tcl_Obj *CONST objv[]
137 ){
138   int eCond;
139   if( objc!=1 ){
140     Tcl_WrongNumArgs(interp, 1, objv, "");
141     return TCL_ERROR;
142   }
143 
144   sqlite3async_control(SQLITEASYNC_GET_HALT, &eCond);
145   if( eCond==SQLITEASYNC_HALT_NEVER ){
146     Tcl_AppendResult(interp, "would block forever", (char*)0);
147     return TCL_ERROR;
148   }
149 
150   Tcl_MutexLock(&testasync_g_writerMutex);
151   Tcl_MutexUnlock(&testasync_g_writerMutex);
152   return TCL_OK;
153 }
154 
155 /*
156 ** sqlite3async_control OPTION ?VALUE?
157 */
158 static int testAsyncControl(
159   void * clientData,
160   Tcl_Interp *interp,
161   int objc,
162   Tcl_Obj *CONST objv[]
163 ){
164   int rc = SQLITE_OK;
165   int aeOpt[] = { SQLITEASYNC_HALT, SQLITEASYNC_DELAY, SQLITEASYNC_LOCKFILES };
166   const char *azOpt[] = { "halt", "delay", "lockfiles", 0 };
167   const char *az[] = { "never", "now", "idle", 0 };
168   int iVal;
169   int eOpt;
170 
171   if( objc!=2 && objc!=3 ){
172     Tcl_WrongNumArgs(interp, 1, objv, "OPTION ?VALUE?");
173     return TCL_ERROR;
174   }
175   if( Tcl_GetIndexFromObj(interp, objv[1], azOpt, "option", 0, &eOpt) ){
176     return TCL_ERROR;
177   }
178   eOpt = aeOpt[eOpt];
179 
180   if( objc==3 ){
181     switch( eOpt ){
182       case SQLITEASYNC_HALT: {
183         assert( SQLITEASYNC_HALT_NEVER==0 );
184         assert( SQLITEASYNC_HALT_NOW==1 );
185         assert( SQLITEASYNC_HALT_IDLE==2 );
186         if( Tcl_GetIndexFromObj(interp, objv[2], az, "value", 0, &iVal) ){
187           return TCL_ERROR;
188         }
189         break;
190       }
191       case SQLITEASYNC_DELAY:
192         if( Tcl_GetIntFromObj(interp, objv[2], &iVal) ){
193           return TCL_ERROR;
194         }
195         break;
196 
197       case SQLITEASYNC_LOCKFILES:
198         if( Tcl_GetBooleanFromObj(interp, objv[2], &iVal) ){
199           return TCL_ERROR;
200         }
201         break;
202     }
203 
204     rc = sqlite3async_control(eOpt, iVal);
205   }
206 
207   if( rc==SQLITE_OK ){
208     rc = sqlite3async_control(
209         eOpt==SQLITEASYNC_HALT ? SQLITEASYNC_GET_HALT :
210         eOpt==SQLITEASYNC_DELAY ? SQLITEASYNC_GET_DELAY :
211         SQLITEASYNC_GET_LOCKFILES, &iVal);
212   }
213 
214   if( rc!=SQLITE_OK ){
215     Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
216     return TCL_ERROR;
217   }
218 
219   if( eOpt==SQLITEASYNC_HALT ){
220     Tcl_SetObjResult(interp, Tcl_NewStringObj(az[iVal], -1));
221   }else{
222     Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal));
223   }
224 
225   return TCL_OK;
226 }
227 
228 #endif  /* SQLITE_ENABLE_ASYNCIO */
229 
230 /*
231 ** This routine registers the custom TCL commands defined in this
232 ** module.  This should be the only procedure visible from outside
233 ** of this module.
234 */
235 int Sqlitetestasync_Init(Tcl_Interp *interp){
236 #ifdef SQLITE_ENABLE_ASYNCIO
237   Tcl_CreateObjCommand(interp,"sqlite3async_start",testAsyncStart,0,0);
238   Tcl_CreateObjCommand(interp,"sqlite3async_wait",testAsyncWait,0,0);
239 
240   Tcl_CreateObjCommand(interp,"sqlite3async_control",testAsyncControl,0,0);
241   Tcl_CreateObjCommand(interp,"sqlite3async_initialize",testAsyncInit,0,0);
242   Tcl_CreateObjCommand(interp,"sqlite3async_shutdown",testAsyncShutdown,0,0);
243 #endif  /* SQLITE_ENABLE_ASYNCIO */
244   return TCL_OK;
245 }
246