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