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