xref: /sqlite-3.40.0/src/test_thread.c (revision b48c0d59)
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 */
tclScriptEvent(Tcl_Event * evPtr,int flags)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 */
postToParent(SqlThread * p,Tcl_Obj * pScript)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 */
tclScriptThread(ClientData pSqlThread)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 */
sqlthread_spawn(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])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 */
sqlthread_parent(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])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 
xBusy(void * pArg,int nBusy)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 */
sqlthread_open(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])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*85bd982bSmistachkin   extern int Md5_Register(sqlite3*,char**,const sqlite3_api_routines*);
284d9b5b117Sdanielk1977 
28539070edbSshane   UNUSED_PARAMETER(clientData);
28639070edbSshane   UNUSED_PARAMETER(objc);
28739070edbSshane 
288d9b5b117Sdanielk1977   zFilename = Tcl_GetString(objv[2]);
289caffb1a5Sdrh   sqlite3_open(zFilename, &db);
29044e95d4fSmistachkin   Md5_Register(db, 0, 0);
291d9b5b117Sdanielk1977   sqlite3_busy_handler(db, xBusy, 0);
292d9b5b117Sdanielk1977 
293d9b5b117Sdanielk1977   if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
294d9b5b117Sdanielk1977   Tcl_AppendResult(interp, zBuf, 0);
295d9b5b117Sdanielk1977 
296d9b5b117Sdanielk1977   return TCL_OK;
297d9b5b117Sdanielk1977 }
298d9b5b117Sdanielk1977 
299d9b5b117Sdanielk1977 
30044918fa0Sdanielk1977 /*
301e9dcd5e6Sdanielk1977 ** sqlthread open
302e9dcd5e6Sdanielk1977 **
303e9dcd5e6Sdanielk1977 **     Return the current thread-id (Tcl_GetCurrentThread()) cast to
304e9dcd5e6Sdanielk1977 **     an integer.
305e9dcd5e6Sdanielk1977 */
sqlthread_id(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])3067617e4a8Smistachkin static int SQLITE_TCLAPI sqlthread_id(
307e9dcd5e6Sdanielk1977   ClientData clientData,
308e9dcd5e6Sdanielk1977   Tcl_Interp *interp,
309e9dcd5e6Sdanielk1977   int objc,
310e9dcd5e6Sdanielk1977   Tcl_Obj *CONST objv[]
311e9dcd5e6Sdanielk1977 ){
312e9dcd5e6Sdanielk1977   Tcl_ThreadId id = Tcl_GetCurrentThread();
313860e332cSdrh   Tcl_SetObjResult(interp, Tcl_NewIntObj(SQLITE_PTR_TO_INT(id)));
31439070edbSshane   UNUSED_PARAMETER(clientData);
31539070edbSshane   UNUSED_PARAMETER(objc);
31639070edbSshane   UNUSED_PARAMETER(objv);
317e9dcd5e6Sdanielk1977   return TCL_OK;
318e9dcd5e6Sdanielk1977 }
319e9dcd5e6Sdanielk1977 
320e9dcd5e6Sdanielk1977 
321e9dcd5e6Sdanielk1977 /*
32244918fa0Sdanielk1977 ** Dispatch routine for the sub-commands of [sqlthread].
32344918fa0Sdanielk1977 */
sqlthread_proc(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])3247617e4a8Smistachkin static int SQLITE_TCLAPI sqlthread_proc(
32544918fa0Sdanielk1977   ClientData clientData,
32644918fa0Sdanielk1977   Tcl_Interp *interp,
32744918fa0Sdanielk1977   int objc,
32844918fa0Sdanielk1977   Tcl_Obj *CONST objv[]
32944918fa0Sdanielk1977 ){
33044918fa0Sdanielk1977   struct SubCommand {
33144918fa0Sdanielk1977     char *zName;
33244918fa0Sdanielk1977     Tcl_ObjCmdProc *xProc;
33344918fa0Sdanielk1977     int nArg;
33444918fa0Sdanielk1977     char *zUsage;
33544918fa0Sdanielk1977   } aSub[] = {
33644918fa0Sdanielk1977     {"parent", sqlthread_parent, 1, "SCRIPT"},
33744918fa0Sdanielk1977     {"spawn",  sqlthread_spawn,  2, "VARNAME SCRIPT"},
338d9b5b117Sdanielk1977     {"open",   sqlthread_open,   1, "DBNAME"},
339e9dcd5e6Sdanielk1977     {"id",     sqlthread_id,     0, ""},
34044918fa0Sdanielk1977     {0, 0, 0}
34144918fa0Sdanielk1977   };
34244918fa0Sdanielk1977   struct SubCommand *pSub;
34344918fa0Sdanielk1977   int rc;
34444918fa0Sdanielk1977   int iIndex;
34544918fa0Sdanielk1977 
34644918fa0Sdanielk1977   if( objc<2 ){
34744918fa0Sdanielk1977     Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND");
34844918fa0Sdanielk1977     return TCL_ERROR;
34944918fa0Sdanielk1977   }
35044918fa0Sdanielk1977 
35144918fa0Sdanielk1977   rc = Tcl_GetIndexFromObjStruct(
35244918fa0Sdanielk1977       interp, objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iIndex
35344918fa0Sdanielk1977   );
35444918fa0Sdanielk1977   if( rc!=TCL_OK ) return rc;
35544918fa0Sdanielk1977   pSub = &aSub[iIndex];
35644918fa0Sdanielk1977 
3570ee469c9Sdrh   if( objc<(pSub->nArg+2) ){
35844918fa0Sdanielk1977     Tcl_WrongNumArgs(interp, 2, objv, pSub->zUsage);
35944918fa0Sdanielk1977     return TCL_ERROR;
36044918fa0Sdanielk1977   }
36144918fa0Sdanielk1977 
36244918fa0Sdanielk1977   return pSub->xProc(clientData, interp, objc, objv);
36344918fa0Sdanielk1977 }
36444918fa0Sdanielk1977 
36544918fa0Sdanielk1977 /*
36681fa193aSdanielk1977 ** The [clock_seconds] command. This is more or less the same as the
36781fa193aSdanielk1977 ** regular tcl [clock seconds], except that it is available in testfixture
36881fa193aSdanielk1977 ** when linked against both Tcl 8.4 and 8.5. Because [clock seconds] is
36981fa193aSdanielk1977 ** implemented as a script in Tcl 8.5, it is not usually available to
37081fa193aSdanielk1977 ** testfixture.
37181fa193aSdanielk1977 */
clock_seconds_proc(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])3727617e4a8Smistachkin static int SQLITE_TCLAPI clock_seconds_proc(
37381fa193aSdanielk1977   ClientData clientData,
37481fa193aSdanielk1977   Tcl_Interp *interp,
37581fa193aSdanielk1977   int objc,
37681fa193aSdanielk1977   Tcl_Obj *CONST objv[]
37781fa193aSdanielk1977 ){
37881fa193aSdanielk1977   Tcl_Time now;
37981fa193aSdanielk1977   Tcl_GetTime(&now);
38081fa193aSdanielk1977   Tcl_SetObjResult(interp, Tcl_NewIntObj(now.sec));
38139070edbSshane   UNUSED_PARAMETER(clientData);
38239070edbSshane   UNUSED_PARAMETER(objc);
38339070edbSshane   UNUSED_PARAMETER(objv);
38481fa193aSdanielk1977   return TCL_OK;
38581fa193aSdanielk1977 }
38681fa193aSdanielk1977 
387404ca075Sdanielk1977 /*************************************************************************
388404ca075Sdanielk1977 ** This block contains the implementation of the [sqlite3_blocking_step]
389404ca075Sdanielk1977 ** command available to threads created by [sqlthread spawn] commands. It
390404ca075Sdanielk1977 ** is only available on UNIX for now. This is because pthread condition
391404ca075Sdanielk1977 ** variables are used.
392404ca075Sdanielk1977 **
393404ca075Sdanielk1977 ** The source code for the C functions sqlite3_blocking_step(),
394404ca075Sdanielk1977 ** blocking_step_notify() and the structure UnlockNotification is
395404ca075Sdanielk1977 ** automatically extracted from this file and used as part of the
396404ca075Sdanielk1977 ** documentation for the sqlite3_unlock_notify() API function. This
397404ca075Sdanielk1977 ** should be considered if these functions are to be extended (i.e. to
398404ca075Sdanielk1977 ** support windows) in the future.
399404ca075Sdanielk1977 */
4003a2d29f8Sshaneh #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
401404ca075Sdanielk1977 
402404ca075Sdanielk1977 /* BEGIN_SQLITE_BLOCKING_STEP */
403404ca075Sdanielk1977 /* This example uses the pthreads API */
404404ca075Sdanielk1977 #include <pthread.h>
405404ca075Sdanielk1977 
406404ca075Sdanielk1977 /*
407404ca075Sdanielk1977 ** A pointer to an instance of this structure is passed as the user-context
408404ca075Sdanielk1977 ** pointer when registering for an unlock-notify callback.
409404ca075Sdanielk1977 */
410404ca075Sdanielk1977 typedef struct UnlockNotification UnlockNotification;
411404ca075Sdanielk1977 struct UnlockNotification {
412ef8662bbSdrh   int fired;                         /* True after unlock event has occurred */
413404ca075Sdanielk1977   pthread_cond_t cond;               /* Condition variable to wait on */
414404ca075Sdanielk1977   pthread_mutex_t mutex;             /* Mutex to protect structure */
415404ca075Sdanielk1977 };
416404ca075Sdanielk1977 
417404ca075Sdanielk1977 /*
418404ca075Sdanielk1977 ** This function is an unlock-notify callback registered with SQLite.
419404ca075Sdanielk1977 */
unlock_notify_cb(void ** apArg,int nArg)42065a2ea11Sdanielk1977 static void unlock_notify_cb(void **apArg, int nArg){
421404ca075Sdanielk1977   int i;
422404ca075Sdanielk1977   for(i=0; i<nArg; i++){
423404ca075Sdanielk1977     UnlockNotification *p = (UnlockNotification *)apArg[i];
424404ca075Sdanielk1977     pthread_mutex_lock(&p->mutex);
425404ca075Sdanielk1977     p->fired = 1;
426404ca075Sdanielk1977     pthread_cond_signal(&p->cond);
427404ca075Sdanielk1977     pthread_mutex_unlock(&p->mutex);
428404ca075Sdanielk1977   }
429404ca075Sdanielk1977 }
430404ca075Sdanielk1977 
431404ca075Sdanielk1977 /*
43265a2ea11Sdanielk1977 ** This function assumes that an SQLite API call (either sqlite3_prepare_v2()
43365a2ea11Sdanielk1977 ** or sqlite3_step()) has just returned SQLITE_LOCKED. The argument is the
43465a2ea11Sdanielk1977 ** associated database connection.
43565a2ea11Sdanielk1977 **
43665a2ea11Sdanielk1977 ** This function calls sqlite3_unlock_notify() to register for an
43765a2ea11Sdanielk1977 ** unlock-notify callback, then blocks until that callback is delivered
43865a2ea11Sdanielk1977 ** and returns SQLITE_OK. The caller should then retry the failed operation.
43965a2ea11Sdanielk1977 **
44065a2ea11Sdanielk1977 ** Or, if sqlite3_unlock_notify() indicates that to block would deadlock
44165a2ea11Sdanielk1977 ** the system, then this function returns SQLITE_LOCKED immediately. In
44265a2ea11Sdanielk1977 ** this case the caller should not retry the operation and should roll
44365a2ea11Sdanielk1977 ** back the current transaction (if any).
44465a2ea11Sdanielk1977 */
wait_for_unlock_notify(sqlite3 * db)44565a2ea11Sdanielk1977 static int wait_for_unlock_notify(sqlite3 *db){
44665a2ea11Sdanielk1977   int rc;
44765a2ea11Sdanielk1977   UnlockNotification un;
44865a2ea11Sdanielk1977 
44965a2ea11Sdanielk1977   /* Initialize the UnlockNotification structure. */
45065a2ea11Sdanielk1977   un.fired = 0;
45165a2ea11Sdanielk1977   pthread_mutex_init(&un.mutex, 0);
45265a2ea11Sdanielk1977   pthread_cond_init(&un.cond, 0);
45365a2ea11Sdanielk1977 
45465a2ea11Sdanielk1977   /* Register for an unlock-notify callback. */
45565a2ea11Sdanielk1977   rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un);
45665a2ea11Sdanielk1977   assert( rc==SQLITE_LOCKED || rc==SQLITE_OK );
45765a2ea11Sdanielk1977 
45865a2ea11Sdanielk1977   /* The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED
45965a2ea11Sdanielk1977   ** or SQLITE_OK.
46065a2ea11Sdanielk1977   **
46165a2ea11Sdanielk1977   ** If SQLITE_LOCKED was returned, then the system is deadlocked. In this
46265a2ea11Sdanielk1977   ** case this function needs to return SQLITE_LOCKED to the caller so
46365a2ea11Sdanielk1977   ** that the current transaction can be rolled back. Otherwise, block
46465a2ea11Sdanielk1977   ** until the unlock-notify callback is invoked, then return SQLITE_OK.
46565a2ea11Sdanielk1977   */
46665a2ea11Sdanielk1977   if( rc==SQLITE_OK ){
46765a2ea11Sdanielk1977     pthread_mutex_lock(&un.mutex);
46865a2ea11Sdanielk1977     if( !un.fired ){
46965a2ea11Sdanielk1977       pthread_cond_wait(&un.cond, &un.mutex);
47065a2ea11Sdanielk1977     }
47165a2ea11Sdanielk1977     pthread_mutex_unlock(&un.mutex);
47265a2ea11Sdanielk1977   }
47365a2ea11Sdanielk1977 
47465a2ea11Sdanielk1977   /* Destroy the mutex and condition variables. */
47565a2ea11Sdanielk1977   pthread_cond_destroy(&un.cond);
47665a2ea11Sdanielk1977   pthread_mutex_destroy(&un.mutex);
47765a2ea11Sdanielk1977 
47865a2ea11Sdanielk1977   return rc;
47965a2ea11Sdanielk1977 }
48065a2ea11Sdanielk1977 
48165a2ea11Sdanielk1977 /*
482404ca075Sdanielk1977 ** This function is a wrapper around the SQLite function sqlite3_step().
483404ca075Sdanielk1977 ** It functions in the same way as step(), except that if a required
484404ca075Sdanielk1977 ** shared-cache lock cannot be obtained, this function may block waiting for
485404ca075Sdanielk1977 ** the lock to become available. In this scenario the normal API step()
486404ca075Sdanielk1977 ** function always returns SQLITE_LOCKED.
487404ca075Sdanielk1977 **
488404ca075Sdanielk1977 ** If this function returns SQLITE_LOCKED, the caller should rollback
489404ca075Sdanielk1977 ** the current transaction (if any) and try again later. Otherwise, the
490404ca075Sdanielk1977 ** system may become deadlocked.
491404ca075Sdanielk1977 */
sqlite3_blocking_step(sqlite3_stmt * pStmt)492404ca075Sdanielk1977 int sqlite3_blocking_step(sqlite3_stmt *pStmt){
49365a2ea11Sdanielk1977   int rc;
49465a2ea11Sdanielk1977   while( SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){
49565a2ea11Sdanielk1977     rc = wait_for_unlock_notify(sqlite3_db_handle(pStmt));
49665a2ea11Sdanielk1977     if( rc!=SQLITE_OK ) break;
497404ca075Sdanielk1977     sqlite3_reset(pStmt);
498404ca075Sdanielk1977   }
49965a2ea11Sdanielk1977   return rc;
500404ca075Sdanielk1977 }
501404ca075Sdanielk1977 
50265a2ea11Sdanielk1977 /*
50365a2ea11Sdanielk1977 ** This function is a wrapper around the SQLite function sqlite3_prepare_v2().
50465a2ea11Sdanielk1977 ** It functions in the same way as prepare_v2(), except that if a required
50565a2ea11Sdanielk1977 ** shared-cache lock cannot be obtained, this function may block waiting for
50665a2ea11Sdanielk1977 ** the lock to become available. In this scenario the normal API prepare_v2()
50765a2ea11Sdanielk1977 ** function always returns SQLITE_LOCKED.
50865a2ea11Sdanielk1977 **
50965a2ea11Sdanielk1977 ** If this function returns SQLITE_LOCKED, the caller should rollback
51065a2ea11Sdanielk1977 ** the current transaction (if any) and try again later. Otherwise, the
51165a2ea11Sdanielk1977 ** system may become deadlocked.
51265a2ea11Sdanielk1977 */
sqlite3_blocking_prepare_v2(sqlite3 * db,const char * zSql,int nSql,sqlite3_stmt ** ppStmt,const char ** pz)51365a2ea11Sdanielk1977 int sqlite3_blocking_prepare_v2(
51465a2ea11Sdanielk1977   sqlite3 *db,              /* Database handle. */
51565a2ea11Sdanielk1977   const char *zSql,         /* UTF-8 encoded SQL statement. */
51665a2ea11Sdanielk1977   int nSql,                 /* Length of zSql in bytes. */
51765a2ea11Sdanielk1977   sqlite3_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
51865a2ea11Sdanielk1977   const char **pz           /* OUT: End of parsed string */
51965a2ea11Sdanielk1977 ){
52065a2ea11Sdanielk1977   int rc;
52165a2ea11Sdanielk1977   while( SQLITE_LOCKED==(rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, pz)) ){
52265a2ea11Sdanielk1977     rc = wait_for_unlock_notify(db);
52365a2ea11Sdanielk1977     if( rc!=SQLITE_OK ) break;
524404ca075Sdanielk1977   }
525404ca075Sdanielk1977   return rc;
526404ca075Sdanielk1977 }
527404ca075Sdanielk1977 /* END_SQLITE_BLOCKING_STEP */
528404ca075Sdanielk1977 
529404ca075Sdanielk1977 /*
530404ca075Sdanielk1977 ** Usage: sqlite3_blocking_step STMT
531404ca075Sdanielk1977 **
532404ca075Sdanielk1977 ** Advance the statement to the next row.
533404ca075Sdanielk1977 */
blocking_step_proc(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])5347617e4a8Smistachkin static int SQLITE_TCLAPI blocking_step_proc(
535404ca075Sdanielk1977   void * clientData,
536404ca075Sdanielk1977   Tcl_Interp *interp,
537404ca075Sdanielk1977   int objc,
538404ca075Sdanielk1977   Tcl_Obj *CONST objv[]
539404ca075Sdanielk1977 ){
540404ca075Sdanielk1977 
541404ca075Sdanielk1977   sqlite3_stmt *pStmt;
542404ca075Sdanielk1977   int rc;
543404ca075Sdanielk1977 
544404ca075Sdanielk1977   if( objc!=2 ){
545404ca075Sdanielk1977     Tcl_WrongNumArgs(interp, 1, objv, "STMT");
546404ca075Sdanielk1977     return TCL_ERROR;
547404ca075Sdanielk1977   }
548404ca075Sdanielk1977 
549404ca075Sdanielk1977   pStmt = (sqlite3_stmt*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
550404ca075Sdanielk1977   rc = sqlite3_blocking_step(pStmt);
551404ca075Sdanielk1977 
552e84d8d32Smistachkin   Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), 0);
553404ca075Sdanielk1977   return TCL_OK;
554404ca075Sdanielk1977 }
555404ca075Sdanielk1977 
55665a2ea11Sdanielk1977 /*
55765a2ea11Sdanielk1977 ** Usage: sqlite3_blocking_prepare_v2 DB sql bytes ?tailvar?
558a8bbef84Sdanielk1977 ** Usage: sqlite3_nonblocking_prepare_v2 DB sql bytes ?tailvar?
55965a2ea11Sdanielk1977 */
blocking_prepare_v2_proc(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])5607617e4a8Smistachkin static int SQLITE_TCLAPI blocking_prepare_v2_proc(
56165a2ea11Sdanielk1977   void * clientData,
56265a2ea11Sdanielk1977   Tcl_Interp *interp,
56365a2ea11Sdanielk1977   int objc,
56465a2ea11Sdanielk1977   Tcl_Obj *CONST objv[]
56565a2ea11Sdanielk1977 ){
56665a2ea11Sdanielk1977   sqlite3 *db;
56765a2ea11Sdanielk1977   const char *zSql;
56865a2ea11Sdanielk1977   int bytes;
56965a2ea11Sdanielk1977   const char *zTail = 0;
57065a2ea11Sdanielk1977   sqlite3_stmt *pStmt = 0;
57165a2ea11Sdanielk1977   char zBuf[50];
57265a2ea11Sdanielk1977   int rc;
573a8bbef84Sdanielk1977   int isBlocking = !(clientData==0);
57465a2ea11Sdanielk1977 
57565a2ea11Sdanielk1977   if( objc!=5 && objc!=4 ){
57665a2ea11Sdanielk1977     Tcl_AppendResult(interp, "wrong # args: should be \"",
57765a2ea11Sdanielk1977        Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
57865a2ea11Sdanielk1977     return TCL_ERROR;
57965a2ea11Sdanielk1977   }
58065a2ea11Sdanielk1977   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
58165a2ea11Sdanielk1977   zSql = Tcl_GetString(objv[2]);
58265a2ea11Sdanielk1977   if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
58365a2ea11Sdanielk1977 
584a8bbef84Sdanielk1977   if( isBlocking ){
585a8bbef84Sdanielk1977     rc = sqlite3_blocking_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
586a8bbef84Sdanielk1977   }else{
587a8bbef84Sdanielk1977     rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
588a8bbef84Sdanielk1977   }
589a8bbef84Sdanielk1977 
59065a2ea11Sdanielk1977   assert(rc==SQLITE_OK || pStmt==0);
59165a2ea11Sdanielk1977   if( zTail && objc>=5 ){
59265a2ea11Sdanielk1977     if( bytes>=0 ){
59365a2ea11Sdanielk1977       bytes = bytes - (zTail-zSql);
59465a2ea11Sdanielk1977     }
59565a2ea11Sdanielk1977     Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
59665a2ea11Sdanielk1977   }
59765a2ea11Sdanielk1977   if( rc!=SQLITE_OK ){
59865a2ea11Sdanielk1977     assert( pStmt==0 );
59965545b59Sdrh     sqlite3_snprintf(sizeof(zBuf), zBuf, "%s ", (char *)sqlite3ErrName(rc));
60065a2ea11Sdanielk1977     Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
60165a2ea11Sdanielk1977     return TCL_ERROR;
60265a2ea11Sdanielk1977   }
60365a2ea11Sdanielk1977 
60465a2ea11Sdanielk1977   if( pStmt ){
60565a2ea11Sdanielk1977     if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
60665a2ea11Sdanielk1977     Tcl_AppendResult(interp, zBuf, 0);
60765a2ea11Sdanielk1977   }
60865a2ea11Sdanielk1977   return TCL_OK;
60965a2ea11Sdanielk1977 }
61065a2ea11Sdanielk1977 
61169910da9Sdrh #endif /* SQLITE_OS_UNIX && SQLITE_ENABLE_UNLOCK_NOTIFY */
612404ca075Sdanielk1977 /*
613404ca075Sdanielk1977 ** End of implementation of [sqlite3_blocking_step].
614404ca075Sdanielk1977 ************************************************************************/
615404ca075Sdanielk1977 
61681fa193aSdanielk1977 /*
61744918fa0Sdanielk1977 ** Register commands with the TCL interpreter.
61844918fa0Sdanielk1977 */
SqlitetestThread_Init(Tcl_Interp * interp)61944918fa0Sdanielk1977 int SqlitetestThread_Init(Tcl_Interp *interp){
62044918fa0Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, 0, 0);
62181fa193aSdanielk1977   Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
6223a2d29f8Sshaneh #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
623404ca075Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
624a8bbef84Sdanielk1977   Tcl_CreateObjCommand(interp,
625a8bbef84Sdanielk1977       "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
626a8bbef84Sdanielk1977   Tcl_CreateObjCommand(interp,
627a8bbef84Sdanielk1977       "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
628404ca075Sdanielk1977 #endif
62944918fa0Sdanielk1977   return TCL_OK;
63044918fa0Sdanielk1977 }
63144918fa0Sdanielk1977 #else
SqlitetestThread_Init(Tcl_Interp * interp)63244918fa0Sdanielk1977 int SqlitetestThread_Init(Tcl_Interp *interp){
63344918fa0Sdanielk1977   return TCL_OK;
63444918fa0Sdanielk1977 }
63544918fa0Sdanielk1977 #endif
636