xref: /sqlite-3.40.0/src/test_thread.c (revision 44e95d4f)
144918fa0Sdanielk1977 /*
244918fa0Sdanielk1977 ** 2007 September 9
344918fa0Sdanielk1977 **
444918fa0Sdanielk1977 ** The author disclaims copyright to this source code.  In place of
544918fa0Sdanielk1977 ** a legal notice, here is a blessing:
644918fa0Sdanielk1977 **
744918fa0Sdanielk1977 **    May you do good and not evil.
844918fa0Sdanielk1977 **    May you find forgiveness for yourself and forgive others.
944918fa0Sdanielk1977 **    May you share freely, never taking more than you give.
1044918fa0Sdanielk1977 **
1144918fa0Sdanielk1977 *************************************************************************
1244918fa0Sdanielk1977 **
1344918fa0Sdanielk1977 ** This file contains the implementation of some Tcl commands used to
1444918fa0Sdanielk1977 ** test that sqlite3 database handles may be concurrently accessed by
1544918fa0Sdanielk1977 ** multiple threads. Right now this only works on unix.
1644918fa0Sdanielk1977 */
1744918fa0Sdanielk1977 
1844918fa0Sdanielk1977 #include "sqliteInt.h"
1952b1dbb5Smistachkin #if defined(INCLUDE_SQLITE_TCL_H)
2052b1dbb5Smistachkin #  include "sqlite_tcl.h"
2152b1dbb5Smistachkin #else
2252b1dbb5Smistachkin #  include "tcl.h"
2352b1dbb5Smistachkin #endif
24570f7e25Sdanielk1977 
25b8613ab1Sdrh #if SQLITE_THREADSAFE
2644918fa0Sdanielk1977 
2744918fa0Sdanielk1977 #include <errno.h>
2839070edbSshane 
2939070edbSshane #if !defined(_MSC_VER)
3044918fa0Sdanielk1977 #include <unistd.h>
3139070edbSshane #endif
3244918fa0Sdanielk1977 
3344918fa0Sdanielk1977 /*
3444918fa0Sdanielk1977 ** One of these is allocated for each thread created by [sqlthread spawn].
3544918fa0Sdanielk1977 */
3644918fa0Sdanielk1977 typedef struct SqlThread SqlThread;
3744918fa0Sdanielk1977 struct SqlThread {
38570f7e25Sdanielk1977   Tcl_ThreadId parent;     /* Thread id of parent thread */
39570f7e25Sdanielk1977   Tcl_Interp *interp;      /* Parent interpreter */
4044918fa0Sdanielk1977   char *zScript;           /* The script to execute. */
4144918fa0Sdanielk1977   char *zVarname;          /* Varname in parent script */
4244918fa0Sdanielk1977 };
4344918fa0Sdanielk1977 
44570f7e25Sdanielk1977 /*
45570f7e25Sdanielk1977 ** A custom Tcl_Event type used by this module. When the event is
46570f7e25Sdanielk1977 ** handled, script zScript is evaluated in interpreter interp. If
47570f7e25Sdanielk1977 ** the evaluation throws an exception (returns TCL_ERROR), then the
48570f7e25Sdanielk1977 ** error is handled by Tcl_BackgroundError(). If no error occurs,
49570f7e25Sdanielk1977 ** the result is simply discarded.
50570f7e25Sdanielk1977 */
51570f7e25Sdanielk1977 typedef struct EvalEvent EvalEvent;
52570f7e25Sdanielk1977 struct EvalEvent {
53570f7e25Sdanielk1977   Tcl_Event base;          /* Base class of type Tcl_Event */
54570f7e25Sdanielk1977   char *zScript;           /* The script to execute. */
55570f7e25Sdanielk1977   Tcl_Interp *interp;      /* The interpreter to execute it in. */
5644918fa0Sdanielk1977 };
5744918fa0Sdanielk1977 
5844918fa0Sdanielk1977 static Tcl_ObjCmdProc sqlthread_proc;
5981fa193aSdanielk1977 static Tcl_ObjCmdProc clock_seconds_proc;
603a2d29f8Sshaneh #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
61404ca075Sdanielk1977 static Tcl_ObjCmdProc blocking_step_proc;
6265a2ea11Sdanielk1977 static Tcl_ObjCmdProc blocking_prepare_v2_proc;
6369910da9Sdrh #endif
64570f7e25Sdanielk1977 int Sqlitetest1_Init(Tcl_Interp *);
655e0ce87aSdan int Sqlite3_Init(Tcl_Interp *);
6644918fa0Sdanielk1977 
67e84d8d32Smistachkin /* Functions from main.c */
68e84d8d32Smistachkin extern const char *sqlite3ErrName(int);
69e84d8d32Smistachkin 
7065a2ea11Sdanielk1977 /* Functions from test1.c */
71e84d8d32Smistachkin extern void *sqlite3TestTextToPtr(const char *);
72e84d8d32Smistachkin extern int getDbPointer(Tcl_Interp *, const char *, sqlite3 **);
73e84d8d32Smistachkin extern int sqlite3TestMakePointerStr(Tcl_Interp *, char *, void *);
74e84d8d32Smistachkin extern int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int);
7565a2ea11Sdanielk1977 
76570f7e25Sdanielk1977 /*
77570f7e25Sdanielk1977 ** Handler for events of type EvalEvent.
78570f7e25Sdanielk1977 */
797617e4a8Smistachkin static int SQLITE_TCLAPI tclScriptEvent(Tcl_Event *evPtr, int flags){
80570f7e25Sdanielk1977   int rc;
81570f7e25Sdanielk1977   EvalEvent *p = (EvalEvent *)evPtr;
82570f7e25Sdanielk1977   rc = Tcl_Eval(p->interp, p->zScript);
83570f7e25Sdanielk1977   if( rc!=TCL_OK ){
84570f7e25Sdanielk1977     Tcl_BackgroundError(p->interp);
85570f7e25Sdanielk1977   }
8639070edbSshane   UNUSED_PARAMETER(flags);
87570f7e25Sdanielk1977   return 1;
88570f7e25Sdanielk1977 }
89570f7e25Sdanielk1977 
90570f7e25Sdanielk1977 /*
91570f7e25Sdanielk1977 ** Register an EvalEvent to evaluate the script pScript in the
92570f7e25Sdanielk1977 ** parent interpreter/thread of SqlThread p.
93570f7e25Sdanielk1977 */
94570f7e25Sdanielk1977 static void postToParent(SqlThread *p, Tcl_Obj *pScript){
95570f7e25Sdanielk1977   EvalEvent *pEvent;
96570f7e25Sdanielk1977   char *zMsg;
97570f7e25Sdanielk1977   int nMsg;
98570f7e25Sdanielk1977 
99570f7e25Sdanielk1977   zMsg = Tcl_GetStringFromObj(pScript, &nMsg);
100570f7e25Sdanielk1977   pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
101570f7e25Sdanielk1977   pEvent->base.nextPtr = 0;
102570f7e25Sdanielk1977   pEvent->base.proc = tclScriptEvent;
103570f7e25Sdanielk1977   pEvent->zScript = (char *)&pEvent[1];
104570f7e25Sdanielk1977   memcpy(pEvent->zScript, zMsg, nMsg+1);
105570f7e25Sdanielk1977   pEvent->interp = p->interp;
106570f7e25Sdanielk1977 
107570f7e25Sdanielk1977   Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
108570f7e25Sdanielk1977   Tcl_ThreadAlert(p->parent);
109570f7e25Sdanielk1977 }
110570f7e25Sdanielk1977 
111570f7e25Sdanielk1977 /*
112570f7e25Sdanielk1977 ** The main function for threads created with [sqlthread spawn].
113570f7e25Sdanielk1977 */
114570f7e25Sdanielk1977 static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){
11544918fa0Sdanielk1977   Tcl_Interp *interp;
11644918fa0Sdanielk1977   Tcl_Obj *pRes;
11744918fa0Sdanielk1977   Tcl_Obj *pList;
11844918fa0Sdanielk1977   int rc;
11944918fa0Sdanielk1977   SqlThread *p = (SqlThread *)pSqlThread;
120b8613ab1Sdrh   extern int Sqlitetest_mutex_Init(Tcl_Interp*);
12144918fa0Sdanielk1977 
12244918fa0Sdanielk1977   interp = Tcl_CreateInterp();
12381fa193aSdanielk1977   Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
12444918fa0Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, pSqlThread, 0);
1253a2d29f8Sshaneh #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
126404ca075Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
127a8bbef84Sdanielk1977   Tcl_CreateObjCommand(interp,
128a8bbef84Sdanielk1977       "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
129a8bbef84Sdanielk1977   Tcl_CreateObjCommand(interp,
130a8bbef84Sdanielk1977       "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
131404ca075Sdanielk1977 #endif
13244918fa0Sdanielk1977   Sqlitetest1_Init(interp);
133b8613ab1Sdrh   Sqlitetest_mutex_Init(interp);
1345e0ce87aSdan   Sqlite3_Init(interp);
13544918fa0Sdanielk1977 
13644918fa0Sdanielk1977   rc = Tcl_Eval(interp, p->zScript);
13744918fa0Sdanielk1977   pRes = Tcl_GetObjResult(interp);
13844918fa0Sdanielk1977   pList = Tcl_NewObj();
13944918fa0Sdanielk1977   Tcl_IncrRefCount(pList);
140570f7e25Sdanielk1977   Tcl_IncrRefCount(pRes);
14144918fa0Sdanielk1977 
142d9b5b117Sdanielk1977   if( rc!=TCL_OK ){
143d9b5b117Sdanielk1977     Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("error", -1));
144d9b5b117Sdanielk1977     Tcl_ListObjAppendElement(interp, pList, pRes);
145d9b5b117Sdanielk1977     postToParent(p, pList);
146d9b5b117Sdanielk1977     Tcl_DecrRefCount(pList);
147d9b5b117Sdanielk1977     pList = Tcl_NewObj();
148d9b5b117Sdanielk1977   }
149d9b5b117Sdanielk1977 
15044918fa0Sdanielk1977   Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("set", -1));
15144918fa0Sdanielk1977   Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(p->zVarname, -1));
15244918fa0Sdanielk1977   Tcl_ListObjAppendElement(interp, pList, pRes);
153570f7e25Sdanielk1977   postToParent(p, pList);
154570f7e25Sdanielk1977 
155570f7e25Sdanielk1977   ckfree((void *)p);
15644918fa0Sdanielk1977   Tcl_DecrRefCount(pList);
157570f7e25Sdanielk1977   Tcl_DecrRefCount(pRes);
15844918fa0Sdanielk1977   Tcl_DeleteInterp(interp);
159e1a2a990Sdan   while( Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT) );
1603374f8aeSdan   Tcl_ExitThread(0);
16139070edbSshane   TCL_THREAD_CREATE_RETURN;
16244918fa0Sdanielk1977 }
16344918fa0Sdanielk1977 
16444918fa0Sdanielk1977 /*
16544918fa0Sdanielk1977 ** sqlthread spawn VARNAME SCRIPT
16644918fa0Sdanielk1977 **
16785b623f2Sdrh **     Spawn a new thread with its own Tcl interpreter and run the
16844918fa0Sdanielk1977 **     specified SCRIPT(s) in it. The thread terminates after running
16944918fa0Sdanielk1977 **     the script. The result of the script is stored in the variable
17044918fa0Sdanielk1977 **     VARNAME.
17144918fa0Sdanielk1977 **
17244918fa0Sdanielk1977 **     The caller can wait for the script to terminate using [vwait VARNAME].
17344918fa0Sdanielk1977 */
1747617e4a8Smistachkin static int SQLITE_TCLAPI sqlthread_spawn(
17544918fa0Sdanielk1977   ClientData clientData,
17644918fa0Sdanielk1977   Tcl_Interp *interp,
17744918fa0Sdanielk1977   int objc,
17844918fa0Sdanielk1977   Tcl_Obj *CONST objv[]
17944918fa0Sdanielk1977 ){
180570f7e25Sdanielk1977   Tcl_ThreadId x;
18144918fa0Sdanielk1977   SqlThread *pNew;
18244918fa0Sdanielk1977   int rc;
18344918fa0Sdanielk1977 
18444918fa0Sdanielk1977   int nVarname; char *zVarname;
18544918fa0Sdanielk1977   int nScript; char *zScript;
18644918fa0Sdanielk1977 
187570f7e25Sdanielk1977   /* Parameters for thread creation */
188570f7e25Sdanielk1977   const int nStack = TCL_THREAD_STACK_DEFAULT;
189570f7e25Sdanielk1977   const int flags = TCL_THREAD_NOFLAGS;
190570f7e25Sdanielk1977 
19144918fa0Sdanielk1977   assert(objc==4);
19239070edbSshane   UNUSED_PARAMETER(clientData);
19339070edbSshane   UNUSED_PARAMETER(objc);
19444918fa0Sdanielk1977 
19544918fa0Sdanielk1977   zVarname = Tcl_GetStringFromObj(objv[2], &nVarname);
19644918fa0Sdanielk1977   zScript = Tcl_GetStringFromObj(objv[3], &nScript);
197570f7e25Sdanielk1977 
198570f7e25Sdanielk1977   pNew = (SqlThread *)ckalloc(sizeof(SqlThread)+nVarname+nScript+2);
19944918fa0Sdanielk1977   pNew->zVarname = (char *)&pNew[1];
20044918fa0Sdanielk1977   pNew->zScript = (char *)&pNew->zVarname[nVarname+1];
20144918fa0Sdanielk1977   memcpy(pNew->zVarname, zVarname, nVarname+1);
20244918fa0Sdanielk1977   memcpy(pNew->zScript, zScript, nScript+1);
203570f7e25Sdanielk1977   pNew->parent = Tcl_GetCurrentThread();
204570f7e25Sdanielk1977   pNew->interp = interp;
20544918fa0Sdanielk1977 
206570f7e25Sdanielk1977   rc = Tcl_CreateThread(&x, tclScriptThread, (void *)pNew, nStack, flags);
207570f7e25Sdanielk1977   if( rc!=TCL_OK ){
208570f7e25Sdanielk1977     Tcl_AppendResult(interp, "Error in Tcl_CreateThread()", 0);
20981fa193aSdanielk1977     ckfree((char *)pNew);
21044918fa0Sdanielk1977     return TCL_ERROR;
21144918fa0Sdanielk1977   }
21244918fa0Sdanielk1977 
21344918fa0Sdanielk1977   return TCL_OK;
21444918fa0Sdanielk1977 }
21544918fa0Sdanielk1977 
21644918fa0Sdanielk1977 /*
21744918fa0Sdanielk1977 ** sqlthread parent SCRIPT
21844918fa0Sdanielk1977 **
21944918fa0Sdanielk1977 **     This can be called by spawned threads only. It sends the specified
22044918fa0Sdanielk1977 **     script back to the parent thread for execution. The result of
22144918fa0Sdanielk1977 **     evaluating the SCRIPT is returned. The parent thread must enter
22244918fa0Sdanielk1977 **     the event loop for this to work - otherwise the caller will
22344918fa0Sdanielk1977 **     block indefinitely.
22444918fa0Sdanielk1977 **
22544918fa0Sdanielk1977 **     NOTE: At the moment, this doesn't work. FIXME.
22644918fa0Sdanielk1977 */
2277617e4a8Smistachkin static int SQLITE_TCLAPI sqlthread_parent(
22844918fa0Sdanielk1977   ClientData clientData,
22944918fa0Sdanielk1977   Tcl_Interp *interp,
23044918fa0Sdanielk1977   int objc,
23144918fa0Sdanielk1977   Tcl_Obj *CONST objv[]
23244918fa0Sdanielk1977 ){
233570f7e25Sdanielk1977   EvalEvent *pEvent;
23444918fa0Sdanielk1977   char *zMsg;
23544918fa0Sdanielk1977   int nMsg;
23644918fa0Sdanielk1977   SqlThread *p = (SqlThread *)clientData;
23744918fa0Sdanielk1977 
23844918fa0Sdanielk1977   assert(objc==3);
23939070edbSshane   UNUSED_PARAMETER(objc);
24039070edbSshane 
24144918fa0Sdanielk1977   if( p==0 ){
24244918fa0Sdanielk1977     Tcl_AppendResult(interp, "no parent thread", 0);
24344918fa0Sdanielk1977     return TCL_ERROR;
24444918fa0Sdanielk1977   }
24544918fa0Sdanielk1977 
24644918fa0Sdanielk1977   zMsg = Tcl_GetStringFromObj(objv[2], &nMsg);
247570f7e25Sdanielk1977   pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
248570f7e25Sdanielk1977   pEvent->base.nextPtr = 0;
249570f7e25Sdanielk1977   pEvent->base.proc = tclScriptEvent;
250570f7e25Sdanielk1977   pEvent->zScript = (char *)&pEvent[1];
251570f7e25Sdanielk1977   memcpy(pEvent->zScript, zMsg, nMsg+1);
252570f7e25Sdanielk1977   pEvent->interp = p->interp;
253570f7e25Sdanielk1977   Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
254570f7e25Sdanielk1977   Tcl_ThreadAlert(p->parent);
25544918fa0Sdanielk1977 
25644918fa0Sdanielk1977   return TCL_OK;
25744918fa0Sdanielk1977 }
25844918fa0Sdanielk1977 
259d9b5b117Sdanielk1977 static int xBusy(void *pArg, int nBusy){
26039070edbSshane   UNUSED_PARAMETER(pArg);
26139070edbSshane   UNUSED_PARAMETER(nBusy);
262d9b5b117Sdanielk1977   sqlite3_sleep(50);
263d9b5b117Sdanielk1977   return 1;             /* Try again... */
264d9b5b117Sdanielk1977 }
265d9b5b117Sdanielk1977 
266e9dcd5e6Sdanielk1977 /*
267e9dcd5e6Sdanielk1977 ** sqlthread open
268e9dcd5e6Sdanielk1977 **
269e9dcd5e6Sdanielk1977 **     Open a database handle and return the string representation of
270e9dcd5e6Sdanielk1977 **     the pointer value.
271e9dcd5e6Sdanielk1977 */
2727617e4a8Smistachkin static int SQLITE_TCLAPI sqlthread_open(
273d9b5b117Sdanielk1977   ClientData clientData,
274d9b5b117Sdanielk1977   Tcl_Interp *interp,
275d9b5b117Sdanielk1977   int objc,
276d9b5b117Sdanielk1977   Tcl_Obj *CONST objv[]
277d9b5b117Sdanielk1977 ){
278d9b5b117Sdanielk1977   int sqlite3TestMakePointerStr(Tcl_Interp *interp, char *zPtr, void *p);
279d9b5b117Sdanielk1977 
280d9b5b117Sdanielk1977   const char *zFilename;
281d9b5b117Sdanielk1977   sqlite3 *db;
282d9b5b117Sdanielk1977   char zBuf[100];
283*44e95d4fSmistachkin   extern int Md5_Register(sqlite3 *, char **, const void *);
284d9b5b117Sdanielk1977 
28539070edbSshane   UNUSED_PARAMETER(clientData);
28639070edbSshane   UNUSED_PARAMETER(objc);
28739070edbSshane 
288d9b5b117Sdanielk1977   zFilename = Tcl_GetString(objv[2]);
289caffb1a5Sdrh   sqlite3_open(zFilename, &db);
2900ee469c9Sdrh #ifdef SQLITE_HAS_CODEC
2910ee469c9Sdrh   if( db && objc>=4 ){
2920ee469c9Sdrh     const char *zKey;
2930ee469c9Sdrh     int nKey;
294caffb1a5Sdrh     int rc;
2950ee469c9Sdrh     zKey = Tcl_GetStringFromObj(objv[3], &nKey);
2960ee469c9Sdrh     rc = sqlite3_key(db, zKey, nKey);
2970ee469c9Sdrh     if( rc!=SQLITE_OK ){
2980ee469c9Sdrh       char *zErrMsg = sqlite3_mprintf("error %d: %s", rc, sqlite3_errmsg(db));
2990ee469c9Sdrh       sqlite3_close(db);
3000ee469c9Sdrh       Tcl_AppendResult(interp, zErrMsg, (char*)0);
3010ee469c9Sdrh       sqlite3_free(zErrMsg);
3020ee469c9Sdrh       return TCL_ERROR;
3030ee469c9Sdrh     }
3040ee469c9Sdrh   }
3050ee469c9Sdrh #endif
306*44e95d4fSmistachkin   Md5_Register(db, 0, 0);
307d9b5b117Sdanielk1977   sqlite3_busy_handler(db, xBusy, 0);
308d9b5b117Sdanielk1977 
309d9b5b117Sdanielk1977   if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
310d9b5b117Sdanielk1977   Tcl_AppendResult(interp, zBuf, 0);
311d9b5b117Sdanielk1977 
312d9b5b117Sdanielk1977   return TCL_OK;
313d9b5b117Sdanielk1977 }
314d9b5b117Sdanielk1977 
315d9b5b117Sdanielk1977 
31644918fa0Sdanielk1977 /*
317e9dcd5e6Sdanielk1977 ** sqlthread open
318e9dcd5e6Sdanielk1977 **
319e9dcd5e6Sdanielk1977 **     Return the current thread-id (Tcl_GetCurrentThread()) cast to
320e9dcd5e6Sdanielk1977 **     an integer.
321e9dcd5e6Sdanielk1977 */
3227617e4a8Smistachkin static int SQLITE_TCLAPI sqlthread_id(
323e9dcd5e6Sdanielk1977   ClientData clientData,
324e9dcd5e6Sdanielk1977   Tcl_Interp *interp,
325e9dcd5e6Sdanielk1977   int objc,
326e9dcd5e6Sdanielk1977   Tcl_Obj *CONST objv[]
327e9dcd5e6Sdanielk1977 ){
328e9dcd5e6Sdanielk1977   Tcl_ThreadId id = Tcl_GetCurrentThread();
329860e332cSdrh   Tcl_SetObjResult(interp, Tcl_NewIntObj(SQLITE_PTR_TO_INT(id)));
33039070edbSshane   UNUSED_PARAMETER(clientData);
33139070edbSshane   UNUSED_PARAMETER(objc);
33239070edbSshane   UNUSED_PARAMETER(objv);
333e9dcd5e6Sdanielk1977   return TCL_OK;
334e9dcd5e6Sdanielk1977 }
335e9dcd5e6Sdanielk1977 
336e9dcd5e6Sdanielk1977 
337e9dcd5e6Sdanielk1977 /*
33844918fa0Sdanielk1977 ** Dispatch routine for the sub-commands of [sqlthread].
33944918fa0Sdanielk1977 */
3407617e4a8Smistachkin static int SQLITE_TCLAPI sqlthread_proc(
34144918fa0Sdanielk1977   ClientData clientData,
34244918fa0Sdanielk1977   Tcl_Interp *interp,
34344918fa0Sdanielk1977   int objc,
34444918fa0Sdanielk1977   Tcl_Obj *CONST objv[]
34544918fa0Sdanielk1977 ){
34644918fa0Sdanielk1977   struct SubCommand {
34744918fa0Sdanielk1977     char *zName;
34844918fa0Sdanielk1977     Tcl_ObjCmdProc *xProc;
34944918fa0Sdanielk1977     int nArg;
35044918fa0Sdanielk1977     char *zUsage;
35144918fa0Sdanielk1977   } aSub[] = {
35244918fa0Sdanielk1977     {"parent", sqlthread_parent, 1, "SCRIPT"},
35344918fa0Sdanielk1977     {"spawn",  sqlthread_spawn,  2, "VARNAME SCRIPT"},
354d9b5b117Sdanielk1977     {"open",   sqlthread_open,   1, "DBNAME"},
355e9dcd5e6Sdanielk1977     {"id",     sqlthread_id,     0, ""},
35644918fa0Sdanielk1977     {0, 0, 0}
35744918fa0Sdanielk1977   };
35844918fa0Sdanielk1977   struct SubCommand *pSub;
35944918fa0Sdanielk1977   int rc;
36044918fa0Sdanielk1977   int iIndex;
36144918fa0Sdanielk1977 
36244918fa0Sdanielk1977   if( objc<2 ){
36344918fa0Sdanielk1977     Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND");
36444918fa0Sdanielk1977     return TCL_ERROR;
36544918fa0Sdanielk1977   }
36644918fa0Sdanielk1977 
36744918fa0Sdanielk1977   rc = Tcl_GetIndexFromObjStruct(
36844918fa0Sdanielk1977       interp, objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iIndex
36944918fa0Sdanielk1977   );
37044918fa0Sdanielk1977   if( rc!=TCL_OK ) return rc;
37144918fa0Sdanielk1977   pSub = &aSub[iIndex];
37244918fa0Sdanielk1977 
3730ee469c9Sdrh   if( objc<(pSub->nArg+2) ){
37444918fa0Sdanielk1977     Tcl_WrongNumArgs(interp, 2, objv, pSub->zUsage);
37544918fa0Sdanielk1977     return TCL_ERROR;
37644918fa0Sdanielk1977   }
37744918fa0Sdanielk1977 
37844918fa0Sdanielk1977   return pSub->xProc(clientData, interp, objc, objv);
37944918fa0Sdanielk1977 }
38044918fa0Sdanielk1977 
38144918fa0Sdanielk1977 /*
38281fa193aSdanielk1977 ** The [clock_seconds] command. This is more or less the same as the
38381fa193aSdanielk1977 ** regular tcl [clock seconds], except that it is available in testfixture
38481fa193aSdanielk1977 ** when linked against both Tcl 8.4 and 8.5. Because [clock seconds] is
38581fa193aSdanielk1977 ** implemented as a script in Tcl 8.5, it is not usually available to
38681fa193aSdanielk1977 ** testfixture.
38781fa193aSdanielk1977 */
3887617e4a8Smistachkin static int SQLITE_TCLAPI clock_seconds_proc(
38981fa193aSdanielk1977   ClientData clientData,
39081fa193aSdanielk1977   Tcl_Interp *interp,
39181fa193aSdanielk1977   int objc,
39281fa193aSdanielk1977   Tcl_Obj *CONST objv[]
39381fa193aSdanielk1977 ){
39481fa193aSdanielk1977   Tcl_Time now;
39581fa193aSdanielk1977   Tcl_GetTime(&now);
39681fa193aSdanielk1977   Tcl_SetObjResult(interp, Tcl_NewIntObj(now.sec));
39739070edbSshane   UNUSED_PARAMETER(clientData);
39839070edbSshane   UNUSED_PARAMETER(objc);
39939070edbSshane   UNUSED_PARAMETER(objv);
40081fa193aSdanielk1977   return TCL_OK;
40181fa193aSdanielk1977 }
40281fa193aSdanielk1977 
403404ca075Sdanielk1977 /*************************************************************************
404404ca075Sdanielk1977 ** This block contains the implementation of the [sqlite3_blocking_step]
405404ca075Sdanielk1977 ** command available to threads created by [sqlthread spawn] commands. It
406404ca075Sdanielk1977 ** is only available on UNIX for now. This is because pthread condition
407404ca075Sdanielk1977 ** variables are used.
408404ca075Sdanielk1977 **
409404ca075Sdanielk1977 ** The source code for the C functions sqlite3_blocking_step(),
410404ca075Sdanielk1977 ** blocking_step_notify() and the structure UnlockNotification is
411404ca075Sdanielk1977 ** automatically extracted from this file and used as part of the
412404ca075Sdanielk1977 ** documentation for the sqlite3_unlock_notify() API function. This
413404ca075Sdanielk1977 ** should be considered if these functions are to be extended (i.e. to
414404ca075Sdanielk1977 ** support windows) in the future.
415404ca075Sdanielk1977 */
4163a2d29f8Sshaneh #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
417404ca075Sdanielk1977 
418404ca075Sdanielk1977 /* BEGIN_SQLITE_BLOCKING_STEP */
419404ca075Sdanielk1977 /* This example uses the pthreads API */
420404ca075Sdanielk1977 #include <pthread.h>
421404ca075Sdanielk1977 
422404ca075Sdanielk1977 /*
423404ca075Sdanielk1977 ** A pointer to an instance of this structure is passed as the user-context
424404ca075Sdanielk1977 ** pointer when registering for an unlock-notify callback.
425404ca075Sdanielk1977 */
426404ca075Sdanielk1977 typedef struct UnlockNotification UnlockNotification;
427404ca075Sdanielk1977 struct UnlockNotification {
428ef8662bbSdrh   int fired;                         /* True after unlock event has occurred */
429404ca075Sdanielk1977   pthread_cond_t cond;               /* Condition variable to wait on */
430404ca075Sdanielk1977   pthread_mutex_t mutex;             /* Mutex to protect structure */
431404ca075Sdanielk1977 };
432404ca075Sdanielk1977 
433404ca075Sdanielk1977 /*
434404ca075Sdanielk1977 ** This function is an unlock-notify callback registered with SQLite.
435404ca075Sdanielk1977 */
43665a2ea11Sdanielk1977 static void unlock_notify_cb(void **apArg, int nArg){
437404ca075Sdanielk1977   int i;
438404ca075Sdanielk1977   for(i=0; i<nArg; i++){
439404ca075Sdanielk1977     UnlockNotification *p = (UnlockNotification *)apArg[i];
440404ca075Sdanielk1977     pthread_mutex_lock(&p->mutex);
441404ca075Sdanielk1977     p->fired = 1;
442404ca075Sdanielk1977     pthread_cond_signal(&p->cond);
443404ca075Sdanielk1977     pthread_mutex_unlock(&p->mutex);
444404ca075Sdanielk1977   }
445404ca075Sdanielk1977 }
446404ca075Sdanielk1977 
447404ca075Sdanielk1977 /*
44865a2ea11Sdanielk1977 ** This function assumes that an SQLite API call (either sqlite3_prepare_v2()
44965a2ea11Sdanielk1977 ** or sqlite3_step()) has just returned SQLITE_LOCKED. The argument is the
45065a2ea11Sdanielk1977 ** associated database connection.
45165a2ea11Sdanielk1977 **
45265a2ea11Sdanielk1977 ** This function calls sqlite3_unlock_notify() to register for an
45365a2ea11Sdanielk1977 ** unlock-notify callback, then blocks until that callback is delivered
45465a2ea11Sdanielk1977 ** and returns SQLITE_OK. The caller should then retry the failed operation.
45565a2ea11Sdanielk1977 **
45665a2ea11Sdanielk1977 ** Or, if sqlite3_unlock_notify() indicates that to block would deadlock
45765a2ea11Sdanielk1977 ** the system, then this function returns SQLITE_LOCKED immediately. In
45865a2ea11Sdanielk1977 ** this case the caller should not retry the operation and should roll
45965a2ea11Sdanielk1977 ** back the current transaction (if any).
46065a2ea11Sdanielk1977 */
46165a2ea11Sdanielk1977 static int wait_for_unlock_notify(sqlite3 *db){
46265a2ea11Sdanielk1977   int rc;
46365a2ea11Sdanielk1977   UnlockNotification un;
46465a2ea11Sdanielk1977 
46565a2ea11Sdanielk1977   /* Initialize the UnlockNotification structure. */
46665a2ea11Sdanielk1977   un.fired = 0;
46765a2ea11Sdanielk1977   pthread_mutex_init(&un.mutex, 0);
46865a2ea11Sdanielk1977   pthread_cond_init(&un.cond, 0);
46965a2ea11Sdanielk1977 
47065a2ea11Sdanielk1977   /* Register for an unlock-notify callback. */
47165a2ea11Sdanielk1977   rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un);
47265a2ea11Sdanielk1977   assert( rc==SQLITE_LOCKED || rc==SQLITE_OK );
47365a2ea11Sdanielk1977 
47465a2ea11Sdanielk1977   /* The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED
47565a2ea11Sdanielk1977   ** or SQLITE_OK.
47665a2ea11Sdanielk1977   **
47765a2ea11Sdanielk1977   ** If SQLITE_LOCKED was returned, then the system is deadlocked. In this
47865a2ea11Sdanielk1977   ** case this function needs to return SQLITE_LOCKED to the caller so
47965a2ea11Sdanielk1977   ** that the current transaction can be rolled back. Otherwise, block
48065a2ea11Sdanielk1977   ** until the unlock-notify callback is invoked, then return SQLITE_OK.
48165a2ea11Sdanielk1977   */
48265a2ea11Sdanielk1977   if( rc==SQLITE_OK ){
48365a2ea11Sdanielk1977     pthread_mutex_lock(&un.mutex);
48465a2ea11Sdanielk1977     if( !un.fired ){
48565a2ea11Sdanielk1977       pthread_cond_wait(&un.cond, &un.mutex);
48665a2ea11Sdanielk1977     }
48765a2ea11Sdanielk1977     pthread_mutex_unlock(&un.mutex);
48865a2ea11Sdanielk1977   }
48965a2ea11Sdanielk1977 
49065a2ea11Sdanielk1977   /* Destroy the mutex and condition variables. */
49165a2ea11Sdanielk1977   pthread_cond_destroy(&un.cond);
49265a2ea11Sdanielk1977   pthread_mutex_destroy(&un.mutex);
49365a2ea11Sdanielk1977 
49465a2ea11Sdanielk1977   return rc;
49565a2ea11Sdanielk1977 }
49665a2ea11Sdanielk1977 
49765a2ea11Sdanielk1977 /*
498404ca075Sdanielk1977 ** This function is a wrapper around the SQLite function sqlite3_step().
499404ca075Sdanielk1977 ** It functions in the same way as step(), except that if a required
500404ca075Sdanielk1977 ** shared-cache lock cannot be obtained, this function may block waiting for
501404ca075Sdanielk1977 ** the lock to become available. In this scenario the normal API step()
502404ca075Sdanielk1977 ** function always returns SQLITE_LOCKED.
503404ca075Sdanielk1977 **
504404ca075Sdanielk1977 ** If this function returns SQLITE_LOCKED, the caller should rollback
505404ca075Sdanielk1977 ** the current transaction (if any) and try again later. Otherwise, the
506404ca075Sdanielk1977 ** system may become deadlocked.
507404ca075Sdanielk1977 */
508404ca075Sdanielk1977 int sqlite3_blocking_step(sqlite3_stmt *pStmt){
50965a2ea11Sdanielk1977   int rc;
51065a2ea11Sdanielk1977   while( SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){
51165a2ea11Sdanielk1977     rc = wait_for_unlock_notify(sqlite3_db_handle(pStmt));
51265a2ea11Sdanielk1977     if( rc!=SQLITE_OK ) break;
513404ca075Sdanielk1977     sqlite3_reset(pStmt);
514404ca075Sdanielk1977   }
51565a2ea11Sdanielk1977   return rc;
516404ca075Sdanielk1977 }
517404ca075Sdanielk1977 
51865a2ea11Sdanielk1977 /*
51965a2ea11Sdanielk1977 ** This function is a wrapper around the SQLite function sqlite3_prepare_v2().
52065a2ea11Sdanielk1977 ** It functions in the same way as prepare_v2(), except that if a required
52165a2ea11Sdanielk1977 ** shared-cache lock cannot be obtained, this function may block waiting for
52265a2ea11Sdanielk1977 ** the lock to become available. In this scenario the normal API prepare_v2()
52365a2ea11Sdanielk1977 ** function always returns SQLITE_LOCKED.
52465a2ea11Sdanielk1977 **
52565a2ea11Sdanielk1977 ** If this function returns SQLITE_LOCKED, the caller should rollback
52665a2ea11Sdanielk1977 ** the current transaction (if any) and try again later. Otherwise, the
52765a2ea11Sdanielk1977 ** system may become deadlocked.
52865a2ea11Sdanielk1977 */
52965a2ea11Sdanielk1977 int sqlite3_blocking_prepare_v2(
53065a2ea11Sdanielk1977   sqlite3 *db,              /* Database handle. */
53165a2ea11Sdanielk1977   const char *zSql,         /* UTF-8 encoded SQL statement. */
53265a2ea11Sdanielk1977   int nSql,                 /* Length of zSql in bytes. */
53365a2ea11Sdanielk1977   sqlite3_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
53465a2ea11Sdanielk1977   const char **pz           /* OUT: End of parsed string */
53565a2ea11Sdanielk1977 ){
53665a2ea11Sdanielk1977   int rc;
53765a2ea11Sdanielk1977   while( SQLITE_LOCKED==(rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, pz)) ){
53865a2ea11Sdanielk1977     rc = wait_for_unlock_notify(db);
53965a2ea11Sdanielk1977     if( rc!=SQLITE_OK ) break;
540404ca075Sdanielk1977   }
541404ca075Sdanielk1977   return rc;
542404ca075Sdanielk1977 }
543404ca075Sdanielk1977 /* END_SQLITE_BLOCKING_STEP */
544404ca075Sdanielk1977 
545404ca075Sdanielk1977 /*
546404ca075Sdanielk1977 ** Usage: sqlite3_blocking_step STMT
547404ca075Sdanielk1977 **
548404ca075Sdanielk1977 ** Advance the statement to the next row.
549404ca075Sdanielk1977 */
5507617e4a8Smistachkin static int SQLITE_TCLAPI blocking_step_proc(
551404ca075Sdanielk1977   void * clientData,
552404ca075Sdanielk1977   Tcl_Interp *interp,
553404ca075Sdanielk1977   int objc,
554404ca075Sdanielk1977   Tcl_Obj *CONST objv[]
555404ca075Sdanielk1977 ){
556404ca075Sdanielk1977 
557404ca075Sdanielk1977   sqlite3_stmt *pStmt;
558404ca075Sdanielk1977   int rc;
559404ca075Sdanielk1977 
560404ca075Sdanielk1977   if( objc!=2 ){
561404ca075Sdanielk1977     Tcl_WrongNumArgs(interp, 1, objv, "STMT");
562404ca075Sdanielk1977     return TCL_ERROR;
563404ca075Sdanielk1977   }
564404ca075Sdanielk1977 
565404ca075Sdanielk1977   pStmt = (sqlite3_stmt*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
566404ca075Sdanielk1977   rc = sqlite3_blocking_step(pStmt);
567404ca075Sdanielk1977 
568e84d8d32Smistachkin   Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), 0);
569404ca075Sdanielk1977   return TCL_OK;
570404ca075Sdanielk1977 }
571404ca075Sdanielk1977 
57265a2ea11Sdanielk1977 /*
57365a2ea11Sdanielk1977 ** Usage: sqlite3_blocking_prepare_v2 DB sql bytes ?tailvar?
574a8bbef84Sdanielk1977 ** Usage: sqlite3_nonblocking_prepare_v2 DB sql bytes ?tailvar?
57565a2ea11Sdanielk1977 */
5767617e4a8Smistachkin static int SQLITE_TCLAPI blocking_prepare_v2_proc(
57765a2ea11Sdanielk1977   void * clientData,
57865a2ea11Sdanielk1977   Tcl_Interp *interp,
57965a2ea11Sdanielk1977   int objc,
58065a2ea11Sdanielk1977   Tcl_Obj *CONST objv[]
58165a2ea11Sdanielk1977 ){
58265a2ea11Sdanielk1977   sqlite3 *db;
58365a2ea11Sdanielk1977   const char *zSql;
58465a2ea11Sdanielk1977   int bytes;
58565a2ea11Sdanielk1977   const char *zTail = 0;
58665a2ea11Sdanielk1977   sqlite3_stmt *pStmt = 0;
58765a2ea11Sdanielk1977   char zBuf[50];
58865a2ea11Sdanielk1977   int rc;
589a8bbef84Sdanielk1977   int isBlocking = !(clientData==0);
59065a2ea11Sdanielk1977 
59165a2ea11Sdanielk1977   if( objc!=5 && objc!=4 ){
59265a2ea11Sdanielk1977     Tcl_AppendResult(interp, "wrong # args: should be \"",
59365a2ea11Sdanielk1977        Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
59465a2ea11Sdanielk1977     return TCL_ERROR;
59565a2ea11Sdanielk1977   }
59665a2ea11Sdanielk1977   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
59765a2ea11Sdanielk1977   zSql = Tcl_GetString(objv[2]);
59865a2ea11Sdanielk1977   if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
59965a2ea11Sdanielk1977 
600a8bbef84Sdanielk1977   if( isBlocking ){
601a8bbef84Sdanielk1977     rc = sqlite3_blocking_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
602a8bbef84Sdanielk1977   }else{
603a8bbef84Sdanielk1977     rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
604a8bbef84Sdanielk1977   }
605a8bbef84Sdanielk1977 
60665a2ea11Sdanielk1977   assert(rc==SQLITE_OK || pStmt==0);
60765a2ea11Sdanielk1977   if( zTail && objc>=5 ){
60865a2ea11Sdanielk1977     if( bytes>=0 ){
60965a2ea11Sdanielk1977       bytes = bytes - (zTail-zSql);
61065a2ea11Sdanielk1977     }
61165a2ea11Sdanielk1977     Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
61265a2ea11Sdanielk1977   }
61365a2ea11Sdanielk1977   if( rc!=SQLITE_OK ){
61465a2ea11Sdanielk1977     assert( pStmt==0 );
61565545b59Sdrh     sqlite3_snprintf(sizeof(zBuf), zBuf, "%s ", (char *)sqlite3ErrName(rc));
61665a2ea11Sdanielk1977     Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
61765a2ea11Sdanielk1977     return TCL_ERROR;
61865a2ea11Sdanielk1977   }
61965a2ea11Sdanielk1977 
62065a2ea11Sdanielk1977   if( pStmt ){
62165a2ea11Sdanielk1977     if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
62265a2ea11Sdanielk1977     Tcl_AppendResult(interp, zBuf, 0);
62365a2ea11Sdanielk1977   }
62465a2ea11Sdanielk1977   return TCL_OK;
62565a2ea11Sdanielk1977 }
62665a2ea11Sdanielk1977 
62769910da9Sdrh #endif /* SQLITE_OS_UNIX && SQLITE_ENABLE_UNLOCK_NOTIFY */
628404ca075Sdanielk1977 /*
629404ca075Sdanielk1977 ** End of implementation of [sqlite3_blocking_step].
630404ca075Sdanielk1977 ************************************************************************/
631404ca075Sdanielk1977 
63281fa193aSdanielk1977 /*
63344918fa0Sdanielk1977 ** Register commands with the TCL interpreter.
63444918fa0Sdanielk1977 */
63544918fa0Sdanielk1977 int SqlitetestThread_Init(Tcl_Interp *interp){
63644918fa0Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, 0, 0);
63781fa193aSdanielk1977   Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
6383a2d29f8Sshaneh #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
639404ca075Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
640a8bbef84Sdanielk1977   Tcl_CreateObjCommand(interp,
641a8bbef84Sdanielk1977       "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
642a8bbef84Sdanielk1977   Tcl_CreateObjCommand(interp,
643a8bbef84Sdanielk1977       "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
644404ca075Sdanielk1977 #endif
64544918fa0Sdanielk1977   return TCL_OK;
64644918fa0Sdanielk1977 }
64744918fa0Sdanielk1977 #else
64844918fa0Sdanielk1977 int SqlitetestThread_Init(Tcl_Interp *interp){
64944918fa0Sdanielk1977   return TCL_OK;
65044918fa0Sdanielk1977 }
65144918fa0Sdanielk1977 #endif
652