xref: /sqlite-3.40.0/src/test_thread.c (revision ef8662bb)
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"
19f78fbdedSdrh #include <tcl.h>
20570f7e25Sdanielk1977 
21b8613ab1Sdrh #if SQLITE_THREADSAFE
2244918fa0Sdanielk1977 
2344918fa0Sdanielk1977 #include <errno.h>
2439070edbSshane 
2539070edbSshane #if !defined(_MSC_VER)
2644918fa0Sdanielk1977 #include <unistd.h>
2739070edbSshane #endif
2844918fa0Sdanielk1977 
2944918fa0Sdanielk1977 /*
3044918fa0Sdanielk1977 ** One of these is allocated for each thread created by [sqlthread spawn].
3144918fa0Sdanielk1977 */
3244918fa0Sdanielk1977 typedef struct SqlThread SqlThread;
3344918fa0Sdanielk1977 struct SqlThread {
34570f7e25Sdanielk1977   Tcl_ThreadId parent;     /* Thread id of parent thread */
35570f7e25Sdanielk1977   Tcl_Interp *interp;      /* Parent interpreter */
3644918fa0Sdanielk1977   char *zScript;           /* The script to execute. */
3744918fa0Sdanielk1977   char *zVarname;          /* Varname in parent script */
3844918fa0Sdanielk1977 };
3944918fa0Sdanielk1977 
40570f7e25Sdanielk1977 /*
41570f7e25Sdanielk1977 ** A custom Tcl_Event type used by this module. When the event is
42570f7e25Sdanielk1977 ** handled, script zScript is evaluated in interpreter interp. If
43570f7e25Sdanielk1977 ** the evaluation throws an exception (returns TCL_ERROR), then the
44570f7e25Sdanielk1977 ** error is handled by Tcl_BackgroundError(). If no error occurs,
45570f7e25Sdanielk1977 ** the result is simply discarded.
46570f7e25Sdanielk1977 */
47570f7e25Sdanielk1977 typedef struct EvalEvent EvalEvent;
48570f7e25Sdanielk1977 struct EvalEvent {
49570f7e25Sdanielk1977   Tcl_Event base;          /* Base class of type Tcl_Event */
50570f7e25Sdanielk1977   char *zScript;           /* The script to execute. */
51570f7e25Sdanielk1977   Tcl_Interp *interp;      /* The interpreter to execute it in. */
5244918fa0Sdanielk1977 };
5344918fa0Sdanielk1977 
5444918fa0Sdanielk1977 static Tcl_ObjCmdProc sqlthread_proc;
5581fa193aSdanielk1977 static Tcl_ObjCmdProc clock_seconds_proc;
563a2d29f8Sshaneh #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
57404ca075Sdanielk1977 static Tcl_ObjCmdProc blocking_step_proc;
5865a2ea11Sdanielk1977 static Tcl_ObjCmdProc blocking_prepare_v2_proc;
5969910da9Sdrh #endif
60570f7e25Sdanielk1977 int Sqlitetest1_Init(Tcl_Interp *);
615e0ce87aSdan int Sqlite3_Init(Tcl_Interp *);
6244918fa0Sdanielk1977 
6365a2ea11Sdanielk1977 /* Functions from test1.c */
6465a2ea11Sdanielk1977 void *sqlite3TestTextToPtr(const char *);
6565a2ea11Sdanielk1977 const char *sqlite3TestErrorName(int);
6665a2ea11Sdanielk1977 int getDbPointer(Tcl_Interp *, const char *, sqlite3 **);
6765a2ea11Sdanielk1977 int sqlite3TestMakePointerStr(Tcl_Interp *, char *, void *);
6865a2ea11Sdanielk1977 int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int);
6965a2ea11Sdanielk1977 
70570f7e25Sdanielk1977 /*
71570f7e25Sdanielk1977 ** Handler for events of type EvalEvent.
72570f7e25Sdanielk1977 */
73570f7e25Sdanielk1977 static int tclScriptEvent(Tcl_Event *evPtr, int flags){
74570f7e25Sdanielk1977   int rc;
75570f7e25Sdanielk1977   EvalEvent *p = (EvalEvent *)evPtr;
76570f7e25Sdanielk1977   rc = Tcl_Eval(p->interp, p->zScript);
77570f7e25Sdanielk1977   if( rc!=TCL_OK ){
78570f7e25Sdanielk1977     Tcl_BackgroundError(p->interp);
79570f7e25Sdanielk1977   }
8039070edbSshane   UNUSED_PARAMETER(flags);
81570f7e25Sdanielk1977   return 1;
82570f7e25Sdanielk1977 }
83570f7e25Sdanielk1977 
84570f7e25Sdanielk1977 /*
85570f7e25Sdanielk1977 ** Register an EvalEvent to evaluate the script pScript in the
86570f7e25Sdanielk1977 ** parent interpreter/thread of SqlThread p.
87570f7e25Sdanielk1977 */
88570f7e25Sdanielk1977 static void postToParent(SqlThread *p, Tcl_Obj *pScript){
89570f7e25Sdanielk1977   EvalEvent *pEvent;
90570f7e25Sdanielk1977   char *zMsg;
91570f7e25Sdanielk1977   int nMsg;
92570f7e25Sdanielk1977 
93570f7e25Sdanielk1977   zMsg = Tcl_GetStringFromObj(pScript, &nMsg);
94570f7e25Sdanielk1977   pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
95570f7e25Sdanielk1977   pEvent->base.nextPtr = 0;
96570f7e25Sdanielk1977   pEvent->base.proc = tclScriptEvent;
97570f7e25Sdanielk1977   pEvent->zScript = (char *)&pEvent[1];
98570f7e25Sdanielk1977   memcpy(pEvent->zScript, zMsg, nMsg+1);
99570f7e25Sdanielk1977   pEvent->interp = p->interp;
100570f7e25Sdanielk1977 
101570f7e25Sdanielk1977   Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
102570f7e25Sdanielk1977   Tcl_ThreadAlert(p->parent);
103570f7e25Sdanielk1977 }
104570f7e25Sdanielk1977 
105570f7e25Sdanielk1977 /*
106570f7e25Sdanielk1977 ** The main function for threads created with [sqlthread spawn].
107570f7e25Sdanielk1977 */
108570f7e25Sdanielk1977 static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){
10944918fa0Sdanielk1977   Tcl_Interp *interp;
11044918fa0Sdanielk1977   Tcl_Obj *pRes;
11144918fa0Sdanielk1977   Tcl_Obj *pList;
11244918fa0Sdanielk1977   int rc;
11344918fa0Sdanielk1977   SqlThread *p = (SqlThread *)pSqlThread;
114b8613ab1Sdrh   extern int Sqlitetest_mutex_Init(Tcl_Interp*);
11544918fa0Sdanielk1977 
11644918fa0Sdanielk1977   interp = Tcl_CreateInterp();
11781fa193aSdanielk1977   Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
11844918fa0Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, pSqlThread, 0);
1193a2d29f8Sshaneh #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
120404ca075Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
121a8bbef84Sdanielk1977   Tcl_CreateObjCommand(interp,
122a8bbef84Sdanielk1977       "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
123a8bbef84Sdanielk1977   Tcl_CreateObjCommand(interp,
124a8bbef84Sdanielk1977       "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
125404ca075Sdanielk1977 #endif
12644918fa0Sdanielk1977   Sqlitetest1_Init(interp);
127b8613ab1Sdrh   Sqlitetest_mutex_Init(interp);
1285e0ce87aSdan   Sqlite3_Init(interp);
12944918fa0Sdanielk1977 
13044918fa0Sdanielk1977   rc = Tcl_Eval(interp, p->zScript);
13144918fa0Sdanielk1977   pRes = Tcl_GetObjResult(interp);
13244918fa0Sdanielk1977   pList = Tcl_NewObj();
13344918fa0Sdanielk1977   Tcl_IncrRefCount(pList);
134570f7e25Sdanielk1977   Tcl_IncrRefCount(pRes);
13544918fa0Sdanielk1977 
136d9b5b117Sdanielk1977   if( rc!=TCL_OK ){
137d9b5b117Sdanielk1977     Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("error", -1));
138d9b5b117Sdanielk1977     Tcl_ListObjAppendElement(interp, pList, pRes);
139d9b5b117Sdanielk1977     postToParent(p, pList);
140d9b5b117Sdanielk1977     Tcl_DecrRefCount(pList);
141d9b5b117Sdanielk1977     pList = Tcl_NewObj();
142d9b5b117Sdanielk1977   }
143d9b5b117Sdanielk1977 
14444918fa0Sdanielk1977   Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("set", -1));
14544918fa0Sdanielk1977   Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(p->zVarname, -1));
14644918fa0Sdanielk1977   Tcl_ListObjAppendElement(interp, pList, pRes);
147570f7e25Sdanielk1977   postToParent(p, pList);
148570f7e25Sdanielk1977 
149570f7e25Sdanielk1977   ckfree((void *)p);
15044918fa0Sdanielk1977   Tcl_DecrRefCount(pList);
151570f7e25Sdanielk1977   Tcl_DecrRefCount(pRes);
15244918fa0Sdanielk1977   Tcl_DeleteInterp(interp);
153e1a2a990Sdan   while( Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT) );
1543374f8aeSdan   Tcl_ExitThread(0);
15539070edbSshane   TCL_THREAD_CREATE_RETURN;
15644918fa0Sdanielk1977 }
15744918fa0Sdanielk1977 
15844918fa0Sdanielk1977 /*
15944918fa0Sdanielk1977 ** sqlthread spawn VARNAME SCRIPT
16044918fa0Sdanielk1977 **
16185b623f2Sdrh **     Spawn a new thread with its own Tcl interpreter and run the
16244918fa0Sdanielk1977 **     specified SCRIPT(s) in it. The thread terminates after running
16344918fa0Sdanielk1977 **     the script. The result of the script is stored in the variable
16444918fa0Sdanielk1977 **     VARNAME.
16544918fa0Sdanielk1977 **
16644918fa0Sdanielk1977 **     The caller can wait for the script to terminate using [vwait VARNAME].
16744918fa0Sdanielk1977 */
16844918fa0Sdanielk1977 static int sqlthread_spawn(
16944918fa0Sdanielk1977   ClientData clientData,
17044918fa0Sdanielk1977   Tcl_Interp *interp,
17144918fa0Sdanielk1977   int objc,
17244918fa0Sdanielk1977   Tcl_Obj *CONST objv[]
17344918fa0Sdanielk1977 ){
174570f7e25Sdanielk1977   Tcl_ThreadId x;
17544918fa0Sdanielk1977   SqlThread *pNew;
17644918fa0Sdanielk1977   int rc;
17744918fa0Sdanielk1977 
17844918fa0Sdanielk1977   int nVarname; char *zVarname;
17944918fa0Sdanielk1977   int nScript; char *zScript;
18044918fa0Sdanielk1977 
181570f7e25Sdanielk1977   /* Parameters for thread creation */
182570f7e25Sdanielk1977   const int nStack = TCL_THREAD_STACK_DEFAULT;
183570f7e25Sdanielk1977   const int flags = TCL_THREAD_NOFLAGS;
184570f7e25Sdanielk1977 
18544918fa0Sdanielk1977   assert(objc==4);
18639070edbSshane   UNUSED_PARAMETER(clientData);
18739070edbSshane   UNUSED_PARAMETER(objc);
18844918fa0Sdanielk1977 
18944918fa0Sdanielk1977   zVarname = Tcl_GetStringFromObj(objv[2], &nVarname);
19044918fa0Sdanielk1977   zScript = Tcl_GetStringFromObj(objv[3], &nScript);
191570f7e25Sdanielk1977 
192570f7e25Sdanielk1977   pNew = (SqlThread *)ckalloc(sizeof(SqlThread)+nVarname+nScript+2);
19344918fa0Sdanielk1977   pNew->zVarname = (char *)&pNew[1];
19444918fa0Sdanielk1977   pNew->zScript = (char *)&pNew->zVarname[nVarname+1];
19544918fa0Sdanielk1977   memcpy(pNew->zVarname, zVarname, nVarname+1);
19644918fa0Sdanielk1977   memcpy(pNew->zScript, zScript, nScript+1);
197570f7e25Sdanielk1977   pNew->parent = Tcl_GetCurrentThread();
198570f7e25Sdanielk1977   pNew->interp = interp;
19944918fa0Sdanielk1977 
200570f7e25Sdanielk1977   rc = Tcl_CreateThread(&x, tclScriptThread, (void *)pNew, nStack, flags);
201570f7e25Sdanielk1977   if( rc!=TCL_OK ){
202570f7e25Sdanielk1977     Tcl_AppendResult(interp, "Error in Tcl_CreateThread()", 0);
20381fa193aSdanielk1977     ckfree((char *)pNew);
20444918fa0Sdanielk1977     return TCL_ERROR;
20544918fa0Sdanielk1977   }
20644918fa0Sdanielk1977 
20744918fa0Sdanielk1977   return TCL_OK;
20844918fa0Sdanielk1977 }
20944918fa0Sdanielk1977 
21044918fa0Sdanielk1977 /*
21144918fa0Sdanielk1977 ** sqlthread parent SCRIPT
21244918fa0Sdanielk1977 **
21344918fa0Sdanielk1977 **     This can be called by spawned threads only. It sends the specified
21444918fa0Sdanielk1977 **     script back to the parent thread for execution. The result of
21544918fa0Sdanielk1977 **     evaluating the SCRIPT is returned. The parent thread must enter
21644918fa0Sdanielk1977 **     the event loop for this to work - otherwise the caller will
21744918fa0Sdanielk1977 **     block indefinitely.
21844918fa0Sdanielk1977 **
21944918fa0Sdanielk1977 **     NOTE: At the moment, this doesn't work. FIXME.
22044918fa0Sdanielk1977 */
22144918fa0Sdanielk1977 static int sqlthread_parent(
22244918fa0Sdanielk1977   ClientData clientData,
22344918fa0Sdanielk1977   Tcl_Interp *interp,
22444918fa0Sdanielk1977   int objc,
22544918fa0Sdanielk1977   Tcl_Obj *CONST objv[]
22644918fa0Sdanielk1977 ){
227570f7e25Sdanielk1977   EvalEvent *pEvent;
22844918fa0Sdanielk1977   char *zMsg;
22944918fa0Sdanielk1977   int nMsg;
23044918fa0Sdanielk1977   SqlThread *p = (SqlThread *)clientData;
23144918fa0Sdanielk1977 
23244918fa0Sdanielk1977   assert(objc==3);
23339070edbSshane   UNUSED_PARAMETER(objc);
23439070edbSshane 
23544918fa0Sdanielk1977   if( p==0 ){
23644918fa0Sdanielk1977     Tcl_AppendResult(interp, "no parent thread", 0);
23744918fa0Sdanielk1977     return TCL_ERROR;
23844918fa0Sdanielk1977   }
23944918fa0Sdanielk1977 
24044918fa0Sdanielk1977   zMsg = Tcl_GetStringFromObj(objv[2], &nMsg);
241570f7e25Sdanielk1977   pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
242570f7e25Sdanielk1977   pEvent->base.nextPtr = 0;
243570f7e25Sdanielk1977   pEvent->base.proc = tclScriptEvent;
244570f7e25Sdanielk1977   pEvent->zScript = (char *)&pEvent[1];
245570f7e25Sdanielk1977   memcpy(pEvent->zScript, zMsg, nMsg+1);
246570f7e25Sdanielk1977   pEvent->interp = p->interp;
247570f7e25Sdanielk1977   Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
248570f7e25Sdanielk1977   Tcl_ThreadAlert(p->parent);
24944918fa0Sdanielk1977 
25044918fa0Sdanielk1977   return TCL_OK;
25144918fa0Sdanielk1977 }
25244918fa0Sdanielk1977 
253d9b5b117Sdanielk1977 static int xBusy(void *pArg, int nBusy){
25439070edbSshane   UNUSED_PARAMETER(pArg);
25539070edbSshane   UNUSED_PARAMETER(nBusy);
256d9b5b117Sdanielk1977   sqlite3_sleep(50);
257d9b5b117Sdanielk1977   return 1;             /* Try again... */
258d9b5b117Sdanielk1977 }
259d9b5b117Sdanielk1977 
260e9dcd5e6Sdanielk1977 /*
261e9dcd5e6Sdanielk1977 ** sqlthread open
262e9dcd5e6Sdanielk1977 **
263e9dcd5e6Sdanielk1977 **     Open a database handle and return the string representation of
264e9dcd5e6Sdanielk1977 **     the pointer value.
265e9dcd5e6Sdanielk1977 */
266d9b5b117Sdanielk1977 static int sqlthread_open(
267d9b5b117Sdanielk1977   ClientData clientData,
268d9b5b117Sdanielk1977   Tcl_Interp *interp,
269d9b5b117Sdanielk1977   int objc,
270d9b5b117Sdanielk1977   Tcl_Obj *CONST objv[]
271d9b5b117Sdanielk1977 ){
272d9b5b117Sdanielk1977   int sqlite3TestMakePointerStr(Tcl_Interp *interp, char *zPtr, void *p);
273d9b5b117Sdanielk1977 
274d9b5b117Sdanielk1977   const char *zFilename;
275d9b5b117Sdanielk1977   sqlite3 *db;
276d9b5b117Sdanielk1977   int rc;
277d9b5b117Sdanielk1977   char zBuf[100];
278d9b5b117Sdanielk1977   extern void Md5_Register(sqlite3*);
279d9b5b117Sdanielk1977 
28039070edbSshane   UNUSED_PARAMETER(clientData);
28139070edbSshane   UNUSED_PARAMETER(objc);
28239070edbSshane 
283d9b5b117Sdanielk1977   zFilename = Tcl_GetString(objv[2]);
284d9b5b117Sdanielk1977   rc = sqlite3_open(zFilename, &db);
285d9b5b117Sdanielk1977   Md5_Register(db);
286d9b5b117Sdanielk1977   sqlite3_busy_handler(db, xBusy, 0);
287d9b5b117Sdanielk1977 
288d9b5b117Sdanielk1977   if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
289d9b5b117Sdanielk1977   Tcl_AppendResult(interp, zBuf, 0);
290d9b5b117Sdanielk1977 
291d9b5b117Sdanielk1977   return TCL_OK;
292d9b5b117Sdanielk1977 }
293d9b5b117Sdanielk1977 
294d9b5b117Sdanielk1977 
29544918fa0Sdanielk1977 /*
296e9dcd5e6Sdanielk1977 ** sqlthread open
297e9dcd5e6Sdanielk1977 **
298e9dcd5e6Sdanielk1977 **     Return the current thread-id (Tcl_GetCurrentThread()) cast to
299e9dcd5e6Sdanielk1977 **     an integer.
300e9dcd5e6Sdanielk1977 */
301e9dcd5e6Sdanielk1977 static int sqlthread_id(
302e9dcd5e6Sdanielk1977   ClientData clientData,
303e9dcd5e6Sdanielk1977   Tcl_Interp *interp,
304e9dcd5e6Sdanielk1977   int objc,
305e9dcd5e6Sdanielk1977   Tcl_Obj *CONST objv[]
306e9dcd5e6Sdanielk1977 ){
307e9dcd5e6Sdanielk1977   Tcl_ThreadId id = Tcl_GetCurrentThread();
308e9dcd5e6Sdanielk1977   Tcl_SetObjResult(interp, Tcl_NewIntObj((int)id));
30939070edbSshane   UNUSED_PARAMETER(clientData);
31039070edbSshane   UNUSED_PARAMETER(objc);
31139070edbSshane   UNUSED_PARAMETER(objv);
312e9dcd5e6Sdanielk1977   return TCL_OK;
313e9dcd5e6Sdanielk1977 }
314e9dcd5e6Sdanielk1977 
315e9dcd5e6Sdanielk1977 
316e9dcd5e6Sdanielk1977 /*
31744918fa0Sdanielk1977 ** Dispatch routine for the sub-commands of [sqlthread].
31844918fa0Sdanielk1977 */
31944918fa0Sdanielk1977 static int sqlthread_proc(
32044918fa0Sdanielk1977   ClientData clientData,
32144918fa0Sdanielk1977   Tcl_Interp *interp,
32244918fa0Sdanielk1977   int objc,
32344918fa0Sdanielk1977   Tcl_Obj *CONST objv[]
32444918fa0Sdanielk1977 ){
32544918fa0Sdanielk1977   struct SubCommand {
32644918fa0Sdanielk1977     char *zName;
32744918fa0Sdanielk1977     Tcl_ObjCmdProc *xProc;
32844918fa0Sdanielk1977     int nArg;
32944918fa0Sdanielk1977     char *zUsage;
33044918fa0Sdanielk1977   } aSub[] = {
33144918fa0Sdanielk1977     {"parent", sqlthread_parent, 1, "SCRIPT"},
33244918fa0Sdanielk1977     {"spawn",  sqlthread_spawn,  2, "VARNAME SCRIPT"},
333d9b5b117Sdanielk1977     {"open",   sqlthread_open,   1, "DBNAME"},
334e9dcd5e6Sdanielk1977     {"id",     sqlthread_id,     0, ""},
33544918fa0Sdanielk1977     {0, 0, 0}
33644918fa0Sdanielk1977   };
33744918fa0Sdanielk1977   struct SubCommand *pSub;
33844918fa0Sdanielk1977   int rc;
33944918fa0Sdanielk1977   int iIndex;
34044918fa0Sdanielk1977 
34144918fa0Sdanielk1977   if( objc<2 ){
34244918fa0Sdanielk1977     Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND");
34344918fa0Sdanielk1977     return TCL_ERROR;
34444918fa0Sdanielk1977   }
34544918fa0Sdanielk1977 
34644918fa0Sdanielk1977   rc = Tcl_GetIndexFromObjStruct(
34744918fa0Sdanielk1977       interp, objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iIndex
34844918fa0Sdanielk1977   );
34944918fa0Sdanielk1977   if( rc!=TCL_OK ) return rc;
35044918fa0Sdanielk1977   pSub = &aSub[iIndex];
35144918fa0Sdanielk1977 
35244918fa0Sdanielk1977   if( objc!=(pSub->nArg+2) ){
35344918fa0Sdanielk1977     Tcl_WrongNumArgs(interp, 2, objv, pSub->zUsage);
35444918fa0Sdanielk1977     return TCL_ERROR;
35544918fa0Sdanielk1977   }
35644918fa0Sdanielk1977 
35744918fa0Sdanielk1977   return pSub->xProc(clientData, interp, objc, objv);
35844918fa0Sdanielk1977 }
35944918fa0Sdanielk1977 
36044918fa0Sdanielk1977 /*
36181fa193aSdanielk1977 ** The [clock_seconds] command. This is more or less the same as the
36281fa193aSdanielk1977 ** regular tcl [clock seconds], except that it is available in testfixture
36381fa193aSdanielk1977 ** when linked against both Tcl 8.4 and 8.5. Because [clock seconds] is
36481fa193aSdanielk1977 ** implemented as a script in Tcl 8.5, it is not usually available to
36581fa193aSdanielk1977 ** testfixture.
36681fa193aSdanielk1977 */
36781fa193aSdanielk1977 static int clock_seconds_proc(
36881fa193aSdanielk1977   ClientData clientData,
36981fa193aSdanielk1977   Tcl_Interp *interp,
37081fa193aSdanielk1977   int objc,
37181fa193aSdanielk1977   Tcl_Obj *CONST objv[]
37281fa193aSdanielk1977 ){
37381fa193aSdanielk1977   Tcl_Time now;
37481fa193aSdanielk1977   Tcl_GetTime(&now);
37581fa193aSdanielk1977   Tcl_SetObjResult(interp, Tcl_NewIntObj(now.sec));
37639070edbSshane   UNUSED_PARAMETER(clientData);
37739070edbSshane   UNUSED_PARAMETER(objc);
37839070edbSshane   UNUSED_PARAMETER(objv);
37981fa193aSdanielk1977   return TCL_OK;
38081fa193aSdanielk1977 }
38181fa193aSdanielk1977 
382404ca075Sdanielk1977 /*************************************************************************
383404ca075Sdanielk1977 ** This block contains the implementation of the [sqlite3_blocking_step]
384404ca075Sdanielk1977 ** command available to threads created by [sqlthread spawn] commands. It
385404ca075Sdanielk1977 ** is only available on UNIX for now. This is because pthread condition
386404ca075Sdanielk1977 ** variables are used.
387404ca075Sdanielk1977 **
388404ca075Sdanielk1977 ** The source code for the C functions sqlite3_blocking_step(),
389404ca075Sdanielk1977 ** blocking_step_notify() and the structure UnlockNotification is
390404ca075Sdanielk1977 ** automatically extracted from this file and used as part of the
391404ca075Sdanielk1977 ** documentation for the sqlite3_unlock_notify() API function. This
392404ca075Sdanielk1977 ** should be considered if these functions are to be extended (i.e. to
393404ca075Sdanielk1977 ** support windows) in the future.
394404ca075Sdanielk1977 */
3953a2d29f8Sshaneh #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
396404ca075Sdanielk1977 
397404ca075Sdanielk1977 /* BEGIN_SQLITE_BLOCKING_STEP */
398404ca075Sdanielk1977 /* This example uses the pthreads API */
399404ca075Sdanielk1977 #include <pthread.h>
400404ca075Sdanielk1977 
401404ca075Sdanielk1977 /*
402404ca075Sdanielk1977 ** A pointer to an instance of this structure is passed as the user-context
403404ca075Sdanielk1977 ** pointer when registering for an unlock-notify callback.
404404ca075Sdanielk1977 */
405404ca075Sdanielk1977 typedef struct UnlockNotification UnlockNotification;
406404ca075Sdanielk1977 struct UnlockNotification {
407*ef8662bbSdrh   int fired;                         /* True after unlock event has occurred */
408404ca075Sdanielk1977   pthread_cond_t cond;               /* Condition variable to wait on */
409404ca075Sdanielk1977   pthread_mutex_t mutex;             /* Mutex to protect structure */
410404ca075Sdanielk1977 };
411404ca075Sdanielk1977 
412404ca075Sdanielk1977 /*
413404ca075Sdanielk1977 ** This function is an unlock-notify callback registered with SQLite.
414404ca075Sdanielk1977 */
41565a2ea11Sdanielk1977 static void unlock_notify_cb(void **apArg, int nArg){
416404ca075Sdanielk1977   int i;
417404ca075Sdanielk1977   for(i=0; i<nArg; i++){
418404ca075Sdanielk1977     UnlockNotification *p = (UnlockNotification *)apArg[i];
419404ca075Sdanielk1977     pthread_mutex_lock(&p->mutex);
420404ca075Sdanielk1977     p->fired = 1;
421404ca075Sdanielk1977     pthread_cond_signal(&p->cond);
422404ca075Sdanielk1977     pthread_mutex_unlock(&p->mutex);
423404ca075Sdanielk1977   }
424404ca075Sdanielk1977 }
425404ca075Sdanielk1977 
426404ca075Sdanielk1977 /*
42765a2ea11Sdanielk1977 ** This function assumes that an SQLite API call (either sqlite3_prepare_v2()
42865a2ea11Sdanielk1977 ** or sqlite3_step()) has just returned SQLITE_LOCKED. The argument is the
42965a2ea11Sdanielk1977 ** associated database connection.
43065a2ea11Sdanielk1977 **
43165a2ea11Sdanielk1977 ** This function calls sqlite3_unlock_notify() to register for an
43265a2ea11Sdanielk1977 ** unlock-notify callback, then blocks until that callback is delivered
43365a2ea11Sdanielk1977 ** and returns SQLITE_OK. The caller should then retry the failed operation.
43465a2ea11Sdanielk1977 **
43565a2ea11Sdanielk1977 ** Or, if sqlite3_unlock_notify() indicates that to block would deadlock
43665a2ea11Sdanielk1977 ** the system, then this function returns SQLITE_LOCKED immediately. In
43765a2ea11Sdanielk1977 ** this case the caller should not retry the operation and should roll
43865a2ea11Sdanielk1977 ** back the current transaction (if any).
43965a2ea11Sdanielk1977 */
44065a2ea11Sdanielk1977 static int wait_for_unlock_notify(sqlite3 *db){
44165a2ea11Sdanielk1977   int rc;
44265a2ea11Sdanielk1977   UnlockNotification un;
44365a2ea11Sdanielk1977 
44465a2ea11Sdanielk1977   /* Initialize the UnlockNotification structure. */
44565a2ea11Sdanielk1977   un.fired = 0;
44665a2ea11Sdanielk1977   pthread_mutex_init(&un.mutex, 0);
44765a2ea11Sdanielk1977   pthread_cond_init(&un.cond, 0);
44865a2ea11Sdanielk1977 
44965a2ea11Sdanielk1977   /* Register for an unlock-notify callback. */
45065a2ea11Sdanielk1977   rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un);
45165a2ea11Sdanielk1977   assert( rc==SQLITE_LOCKED || rc==SQLITE_OK );
45265a2ea11Sdanielk1977 
45365a2ea11Sdanielk1977   /* The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED
45465a2ea11Sdanielk1977   ** or SQLITE_OK.
45565a2ea11Sdanielk1977   **
45665a2ea11Sdanielk1977   ** If SQLITE_LOCKED was returned, then the system is deadlocked. In this
45765a2ea11Sdanielk1977   ** case this function needs to return SQLITE_LOCKED to the caller so
45865a2ea11Sdanielk1977   ** that the current transaction can be rolled back. Otherwise, block
45965a2ea11Sdanielk1977   ** until the unlock-notify callback is invoked, then return SQLITE_OK.
46065a2ea11Sdanielk1977   */
46165a2ea11Sdanielk1977   if( rc==SQLITE_OK ){
46265a2ea11Sdanielk1977     pthread_mutex_lock(&un.mutex);
46365a2ea11Sdanielk1977     if( !un.fired ){
46465a2ea11Sdanielk1977       pthread_cond_wait(&un.cond, &un.mutex);
46565a2ea11Sdanielk1977     }
46665a2ea11Sdanielk1977     pthread_mutex_unlock(&un.mutex);
46765a2ea11Sdanielk1977   }
46865a2ea11Sdanielk1977 
46965a2ea11Sdanielk1977   /* Destroy the mutex and condition variables. */
47065a2ea11Sdanielk1977   pthread_cond_destroy(&un.cond);
47165a2ea11Sdanielk1977   pthread_mutex_destroy(&un.mutex);
47265a2ea11Sdanielk1977 
47365a2ea11Sdanielk1977   return rc;
47465a2ea11Sdanielk1977 }
47565a2ea11Sdanielk1977 
47665a2ea11Sdanielk1977 /*
477404ca075Sdanielk1977 ** This function is a wrapper around the SQLite function sqlite3_step().
478404ca075Sdanielk1977 ** It functions in the same way as step(), except that if a required
479404ca075Sdanielk1977 ** shared-cache lock cannot be obtained, this function may block waiting for
480404ca075Sdanielk1977 ** the lock to become available. In this scenario the normal API step()
481404ca075Sdanielk1977 ** function always returns SQLITE_LOCKED.
482404ca075Sdanielk1977 **
483404ca075Sdanielk1977 ** If this function returns SQLITE_LOCKED, the caller should rollback
484404ca075Sdanielk1977 ** the current transaction (if any) and try again later. Otherwise, the
485404ca075Sdanielk1977 ** system may become deadlocked.
486404ca075Sdanielk1977 */
487404ca075Sdanielk1977 int sqlite3_blocking_step(sqlite3_stmt *pStmt){
48865a2ea11Sdanielk1977   int rc;
48965a2ea11Sdanielk1977   while( SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){
49065a2ea11Sdanielk1977     rc = wait_for_unlock_notify(sqlite3_db_handle(pStmt));
49165a2ea11Sdanielk1977     if( rc!=SQLITE_OK ) break;
492404ca075Sdanielk1977     sqlite3_reset(pStmt);
493404ca075Sdanielk1977   }
49465a2ea11Sdanielk1977   return rc;
495404ca075Sdanielk1977 }
496404ca075Sdanielk1977 
49765a2ea11Sdanielk1977 /*
49865a2ea11Sdanielk1977 ** This function is a wrapper around the SQLite function sqlite3_prepare_v2().
49965a2ea11Sdanielk1977 ** It functions in the same way as prepare_v2(), except that if a required
50065a2ea11Sdanielk1977 ** shared-cache lock cannot be obtained, this function may block waiting for
50165a2ea11Sdanielk1977 ** the lock to become available. In this scenario the normal API prepare_v2()
50265a2ea11Sdanielk1977 ** function always returns SQLITE_LOCKED.
50365a2ea11Sdanielk1977 **
50465a2ea11Sdanielk1977 ** If this function returns SQLITE_LOCKED, the caller should rollback
50565a2ea11Sdanielk1977 ** the current transaction (if any) and try again later. Otherwise, the
50665a2ea11Sdanielk1977 ** system may become deadlocked.
50765a2ea11Sdanielk1977 */
50865a2ea11Sdanielk1977 int sqlite3_blocking_prepare_v2(
50965a2ea11Sdanielk1977   sqlite3 *db,              /* Database handle. */
51065a2ea11Sdanielk1977   const char *zSql,         /* UTF-8 encoded SQL statement. */
51165a2ea11Sdanielk1977   int nSql,                 /* Length of zSql in bytes. */
51265a2ea11Sdanielk1977   sqlite3_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
51365a2ea11Sdanielk1977   const char **pz           /* OUT: End of parsed string */
51465a2ea11Sdanielk1977 ){
51565a2ea11Sdanielk1977   int rc;
51665a2ea11Sdanielk1977   while( SQLITE_LOCKED==(rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, pz)) ){
51765a2ea11Sdanielk1977     rc = wait_for_unlock_notify(db);
51865a2ea11Sdanielk1977     if( rc!=SQLITE_OK ) break;
519404ca075Sdanielk1977   }
520404ca075Sdanielk1977   return rc;
521404ca075Sdanielk1977 }
522404ca075Sdanielk1977 /* END_SQLITE_BLOCKING_STEP */
523404ca075Sdanielk1977 
524404ca075Sdanielk1977 /*
525404ca075Sdanielk1977 ** Usage: sqlite3_blocking_step STMT
526404ca075Sdanielk1977 **
527404ca075Sdanielk1977 ** Advance the statement to the next row.
528404ca075Sdanielk1977 */
529404ca075Sdanielk1977 static int blocking_step_proc(
530404ca075Sdanielk1977   void * clientData,
531404ca075Sdanielk1977   Tcl_Interp *interp,
532404ca075Sdanielk1977   int objc,
533404ca075Sdanielk1977   Tcl_Obj *CONST objv[]
534404ca075Sdanielk1977 ){
535404ca075Sdanielk1977 
536404ca075Sdanielk1977   sqlite3_stmt *pStmt;
537404ca075Sdanielk1977   int rc;
538404ca075Sdanielk1977 
539404ca075Sdanielk1977   if( objc!=2 ){
540404ca075Sdanielk1977     Tcl_WrongNumArgs(interp, 1, objv, "STMT");
541404ca075Sdanielk1977     return TCL_ERROR;
542404ca075Sdanielk1977   }
543404ca075Sdanielk1977 
544404ca075Sdanielk1977   pStmt = (sqlite3_stmt*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
545404ca075Sdanielk1977   rc = sqlite3_blocking_step(pStmt);
546404ca075Sdanielk1977 
547404ca075Sdanielk1977   Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), 0);
548404ca075Sdanielk1977   return TCL_OK;
549404ca075Sdanielk1977 }
550404ca075Sdanielk1977 
55165a2ea11Sdanielk1977 /*
55265a2ea11Sdanielk1977 ** Usage: sqlite3_blocking_prepare_v2 DB sql bytes ?tailvar?
553a8bbef84Sdanielk1977 ** Usage: sqlite3_nonblocking_prepare_v2 DB sql bytes ?tailvar?
55465a2ea11Sdanielk1977 */
55565a2ea11Sdanielk1977 static int blocking_prepare_v2_proc(
55665a2ea11Sdanielk1977   void * clientData,
55765a2ea11Sdanielk1977   Tcl_Interp *interp,
55865a2ea11Sdanielk1977   int objc,
55965a2ea11Sdanielk1977   Tcl_Obj *CONST objv[]
56065a2ea11Sdanielk1977 ){
56165a2ea11Sdanielk1977   sqlite3 *db;
56265a2ea11Sdanielk1977   const char *zSql;
56365a2ea11Sdanielk1977   int bytes;
56465a2ea11Sdanielk1977   const char *zTail = 0;
56565a2ea11Sdanielk1977   sqlite3_stmt *pStmt = 0;
56665a2ea11Sdanielk1977   char zBuf[50];
56765a2ea11Sdanielk1977   int rc;
568a8bbef84Sdanielk1977   int isBlocking = !(clientData==0);
56965a2ea11Sdanielk1977 
57065a2ea11Sdanielk1977   if( objc!=5 && objc!=4 ){
57165a2ea11Sdanielk1977     Tcl_AppendResult(interp, "wrong # args: should be \"",
57265a2ea11Sdanielk1977        Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
57365a2ea11Sdanielk1977     return TCL_ERROR;
57465a2ea11Sdanielk1977   }
57565a2ea11Sdanielk1977   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
57665a2ea11Sdanielk1977   zSql = Tcl_GetString(objv[2]);
57765a2ea11Sdanielk1977   if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
57865a2ea11Sdanielk1977 
579a8bbef84Sdanielk1977   if( isBlocking ){
580a8bbef84Sdanielk1977     rc = sqlite3_blocking_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
581a8bbef84Sdanielk1977   }else{
582a8bbef84Sdanielk1977     rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
583a8bbef84Sdanielk1977   }
584a8bbef84Sdanielk1977 
58565a2ea11Sdanielk1977   assert(rc==SQLITE_OK || pStmt==0);
58665a2ea11Sdanielk1977   if( zTail && objc>=5 ){
58765a2ea11Sdanielk1977     if( bytes>=0 ){
58865a2ea11Sdanielk1977       bytes = bytes - (zTail-zSql);
58965a2ea11Sdanielk1977     }
59065a2ea11Sdanielk1977     Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
59165a2ea11Sdanielk1977   }
59265a2ea11Sdanielk1977   if( rc!=SQLITE_OK ){
59365a2ea11Sdanielk1977     assert( pStmt==0 );
59465a2ea11Sdanielk1977     sprintf(zBuf, "%s ", (char *)sqlite3TestErrorName(rc));
59565a2ea11Sdanielk1977     Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
59665a2ea11Sdanielk1977     return TCL_ERROR;
59765a2ea11Sdanielk1977   }
59865a2ea11Sdanielk1977 
59965a2ea11Sdanielk1977   if( pStmt ){
60065a2ea11Sdanielk1977     if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
60165a2ea11Sdanielk1977     Tcl_AppendResult(interp, zBuf, 0);
60265a2ea11Sdanielk1977   }
60365a2ea11Sdanielk1977   return TCL_OK;
60465a2ea11Sdanielk1977 }
60565a2ea11Sdanielk1977 
60669910da9Sdrh #endif /* SQLITE_OS_UNIX && SQLITE_ENABLE_UNLOCK_NOTIFY */
607404ca075Sdanielk1977 /*
608404ca075Sdanielk1977 ** End of implementation of [sqlite3_blocking_step].
609404ca075Sdanielk1977 ************************************************************************/
610404ca075Sdanielk1977 
61181fa193aSdanielk1977 /*
61244918fa0Sdanielk1977 ** Register commands with the TCL interpreter.
61344918fa0Sdanielk1977 */
61444918fa0Sdanielk1977 int SqlitetestThread_Init(Tcl_Interp *interp){
61544918fa0Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, 0, 0);
61681fa193aSdanielk1977   Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
6173a2d29f8Sshaneh #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
618404ca075Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
619a8bbef84Sdanielk1977   Tcl_CreateObjCommand(interp,
620a8bbef84Sdanielk1977       "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
621a8bbef84Sdanielk1977   Tcl_CreateObjCommand(interp,
622a8bbef84Sdanielk1977       "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
623404ca075Sdanielk1977 #endif
62444918fa0Sdanielk1977   return TCL_OK;
62544918fa0Sdanielk1977 }
62644918fa0Sdanielk1977 #else
62744918fa0Sdanielk1977 int SqlitetestThread_Init(Tcl_Interp *interp){
62844918fa0Sdanielk1977   return TCL_OK;
62944918fa0Sdanielk1977 }
63044918fa0Sdanielk1977 #endif
631