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