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 */
27const char *sqlite3TestErrorName(int);
28
29
30struct TestAsyncGlobal {
31  int isInstalled;                     /* True when async VFS is installed */
32} testasync_g = { 0 };
33
34TCL_DECLARE_MUTEX(testasync_g_writerMutex);
35
36/*
37** sqlite3async_initialize PARENT-VFS ISDEFAULT
38*/
39static 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*/
72static 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
82static 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*/
96static 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*/
128static 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*/
154static 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*/
231int 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