xref: /sqlite-3.40.0/src/test_thread.c (revision a8bbef84)
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 **
17*a8bbef84Sdanielk1977 ** $Id: test_thread.c,v 1.13 2009/03/23 17:11:27 danielk1977 Exp $
1844918fa0Sdanielk1977 */
1944918fa0Sdanielk1977 
2044918fa0Sdanielk1977 #include "sqliteInt.h"
21f78fbdedSdrh #include <tcl.h>
22570f7e25Sdanielk1977 
23b8613ab1Sdrh #if SQLITE_THREADSAFE
2444918fa0Sdanielk1977 
2544918fa0Sdanielk1977 #include <errno.h>
2639070edbSshane 
2739070edbSshane #if !defined(_MSC_VER)
2844918fa0Sdanielk1977 #include <unistd.h>
2939070edbSshane #endif
3044918fa0Sdanielk1977 
3144918fa0Sdanielk1977 /*
3244918fa0Sdanielk1977 ** One of these is allocated for each thread created by [sqlthread spawn].
3344918fa0Sdanielk1977 */
3444918fa0Sdanielk1977 typedef struct SqlThread SqlThread;
3544918fa0Sdanielk1977 struct SqlThread {
36570f7e25Sdanielk1977   Tcl_ThreadId parent;     /* Thread id of parent thread */
37570f7e25Sdanielk1977   Tcl_Interp *interp;      /* Parent interpreter */
3844918fa0Sdanielk1977   char *zScript;           /* The script to execute. */
3944918fa0Sdanielk1977   char *zVarname;          /* Varname in parent script */
4044918fa0Sdanielk1977 };
4144918fa0Sdanielk1977 
42570f7e25Sdanielk1977 /*
43570f7e25Sdanielk1977 ** A custom Tcl_Event type used by this module. When the event is
44570f7e25Sdanielk1977 ** handled, script zScript is evaluated in interpreter interp. If
45570f7e25Sdanielk1977 ** the evaluation throws an exception (returns TCL_ERROR), then the
46570f7e25Sdanielk1977 ** error is handled by Tcl_BackgroundError(). If no error occurs,
47570f7e25Sdanielk1977 ** the result is simply discarded.
48570f7e25Sdanielk1977 */
49570f7e25Sdanielk1977 typedef struct EvalEvent EvalEvent;
50570f7e25Sdanielk1977 struct EvalEvent {
51570f7e25Sdanielk1977   Tcl_Event base;          /* Base class of type Tcl_Event */
52570f7e25Sdanielk1977   char *zScript;           /* The script to execute. */
53570f7e25Sdanielk1977   Tcl_Interp *interp;      /* The interpreter to execute it in. */
5444918fa0Sdanielk1977 };
5544918fa0Sdanielk1977 
5644918fa0Sdanielk1977 static Tcl_ObjCmdProc sqlthread_proc;
5781fa193aSdanielk1977 static Tcl_ObjCmdProc clock_seconds_proc;
58404ca075Sdanielk1977 static Tcl_ObjCmdProc blocking_step_proc;
5965a2ea11Sdanielk1977 static Tcl_ObjCmdProc blocking_prepare_v2_proc;
60570f7e25Sdanielk1977 int Sqlitetest1_Init(Tcl_Interp *);
6144918fa0Sdanielk1977 
6265a2ea11Sdanielk1977 /* Functions from test1.c */
6365a2ea11Sdanielk1977 void *sqlite3TestTextToPtr(const char *);
6465a2ea11Sdanielk1977 const char *sqlite3TestErrorName(int);
6565a2ea11Sdanielk1977 int getDbPointer(Tcl_Interp *, const char *, sqlite3 **);
6665a2ea11Sdanielk1977 int sqlite3TestMakePointerStr(Tcl_Interp *, char *, void *);
6765a2ea11Sdanielk1977 int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int);
6865a2ea11Sdanielk1977 
69570f7e25Sdanielk1977 /*
70570f7e25Sdanielk1977 ** Handler for events of type EvalEvent.
71570f7e25Sdanielk1977 */
72570f7e25Sdanielk1977 static int tclScriptEvent(Tcl_Event *evPtr, int flags){
73570f7e25Sdanielk1977   int rc;
74570f7e25Sdanielk1977   EvalEvent *p = (EvalEvent *)evPtr;
75570f7e25Sdanielk1977   rc = Tcl_Eval(p->interp, p->zScript);
76570f7e25Sdanielk1977   if( rc!=TCL_OK ){
77570f7e25Sdanielk1977     Tcl_BackgroundError(p->interp);
78570f7e25Sdanielk1977   }
7939070edbSshane   UNUSED_PARAMETER(flags);
80570f7e25Sdanielk1977   return 1;
81570f7e25Sdanielk1977 }
82570f7e25Sdanielk1977 
83570f7e25Sdanielk1977 /*
84570f7e25Sdanielk1977 ** Register an EvalEvent to evaluate the script pScript in the
85570f7e25Sdanielk1977 ** parent interpreter/thread of SqlThread p.
86570f7e25Sdanielk1977 */
87570f7e25Sdanielk1977 static void postToParent(SqlThread *p, Tcl_Obj *pScript){
88570f7e25Sdanielk1977   EvalEvent *pEvent;
89570f7e25Sdanielk1977   char *zMsg;
90570f7e25Sdanielk1977   int nMsg;
91570f7e25Sdanielk1977 
92570f7e25Sdanielk1977   zMsg = Tcl_GetStringFromObj(pScript, &nMsg);
93570f7e25Sdanielk1977   pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
94570f7e25Sdanielk1977   pEvent->base.nextPtr = 0;
95570f7e25Sdanielk1977   pEvent->base.proc = tclScriptEvent;
96570f7e25Sdanielk1977   pEvent->zScript = (char *)&pEvent[1];
97570f7e25Sdanielk1977   memcpy(pEvent->zScript, zMsg, nMsg+1);
98570f7e25Sdanielk1977   pEvent->interp = p->interp;
99570f7e25Sdanielk1977 
100570f7e25Sdanielk1977   Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
101570f7e25Sdanielk1977   Tcl_ThreadAlert(p->parent);
102570f7e25Sdanielk1977 }
103570f7e25Sdanielk1977 
104570f7e25Sdanielk1977 /*
105570f7e25Sdanielk1977 ** The main function for threads created with [sqlthread spawn].
106570f7e25Sdanielk1977 */
107570f7e25Sdanielk1977 static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){
10844918fa0Sdanielk1977   Tcl_Interp *interp;
10944918fa0Sdanielk1977   Tcl_Obj *pRes;
11044918fa0Sdanielk1977   Tcl_Obj *pList;
11144918fa0Sdanielk1977   int rc;
11244918fa0Sdanielk1977   SqlThread *p = (SqlThread *)pSqlThread;
113b8613ab1Sdrh   extern int Sqlitetest_mutex_Init(Tcl_Interp*);
11444918fa0Sdanielk1977 
11544918fa0Sdanielk1977   interp = Tcl_CreateInterp();
11681fa193aSdanielk1977   Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
11744918fa0Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, pSqlThread, 0);
118404ca075Sdanielk1977 #if defined(OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
119404ca075Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
120*a8bbef84Sdanielk1977   Tcl_CreateObjCommand(interp,
121*a8bbef84Sdanielk1977       "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
122*a8bbef84Sdanielk1977   Tcl_CreateObjCommand(interp,
123*a8bbef84Sdanielk1977       "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
124404ca075Sdanielk1977 #endif
12544918fa0Sdanielk1977   Sqlitetest1_Init(interp);
126b8613ab1Sdrh   Sqlitetest_mutex_Init(interp);
12744918fa0Sdanielk1977 
12844918fa0Sdanielk1977   rc = Tcl_Eval(interp, p->zScript);
12944918fa0Sdanielk1977   pRes = Tcl_GetObjResult(interp);
13044918fa0Sdanielk1977   pList = Tcl_NewObj();
13144918fa0Sdanielk1977   Tcl_IncrRefCount(pList);
132570f7e25Sdanielk1977   Tcl_IncrRefCount(pRes);
13344918fa0Sdanielk1977 
134d9b5b117Sdanielk1977   if( rc!=TCL_OK ){
135d9b5b117Sdanielk1977     Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("error", -1));
136d9b5b117Sdanielk1977     Tcl_ListObjAppendElement(interp, pList, pRes);
137d9b5b117Sdanielk1977     postToParent(p, pList);
138d9b5b117Sdanielk1977     Tcl_DecrRefCount(pList);
139d9b5b117Sdanielk1977     pList = Tcl_NewObj();
140d9b5b117Sdanielk1977   }
141d9b5b117Sdanielk1977 
14244918fa0Sdanielk1977   Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("set", -1));
14344918fa0Sdanielk1977   Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(p->zVarname, -1));
14444918fa0Sdanielk1977   Tcl_ListObjAppendElement(interp, pList, pRes);
145570f7e25Sdanielk1977   postToParent(p, pList);
146570f7e25Sdanielk1977 
147570f7e25Sdanielk1977   ckfree((void *)p);
14844918fa0Sdanielk1977   Tcl_DecrRefCount(pList);
149570f7e25Sdanielk1977   Tcl_DecrRefCount(pRes);
15044918fa0Sdanielk1977   Tcl_DeleteInterp(interp);
15139070edbSshane   TCL_THREAD_CREATE_RETURN;
15244918fa0Sdanielk1977 }
15344918fa0Sdanielk1977 
15444918fa0Sdanielk1977 /*
15544918fa0Sdanielk1977 ** sqlthread spawn VARNAME SCRIPT
15644918fa0Sdanielk1977 **
15785b623f2Sdrh **     Spawn a new thread with its own Tcl interpreter and run the
15844918fa0Sdanielk1977 **     specified SCRIPT(s) in it. The thread terminates after running
15944918fa0Sdanielk1977 **     the script. The result of the script is stored in the variable
16044918fa0Sdanielk1977 **     VARNAME.
16144918fa0Sdanielk1977 **
16244918fa0Sdanielk1977 **     The caller can wait for the script to terminate using [vwait VARNAME].
16344918fa0Sdanielk1977 */
16444918fa0Sdanielk1977 static int sqlthread_spawn(
16544918fa0Sdanielk1977   ClientData clientData,
16644918fa0Sdanielk1977   Tcl_Interp *interp,
16744918fa0Sdanielk1977   int objc,
16844918fa0Sdanielk1977   Tcl_Obj *CONST objv[]
16944918fa0Sdanielk1977 ){
170570f7e25Sdanielk1977   Tcl_ThreadId x;
17144918fa0Sdanielk1977   SqlThread *pNew;
17244918fa0Sdanielk1977   int rc;
17344918fa0Sdanielk1977 
17444918fa0Sdanielk1977   int nVarname; char *zVarname;
17544918fa0Sdanielk1977   int nScript; char *zScript;
17644918fa0Sdanielk1977 
177570f7e25Sdanielk1977   /* Parameters for thread creation */
178570f7e25Sdanielk1977   const int nStack = TCL_THREAD_STACK_DEFAULT;
179570f7e25Sdanielk1977   const int flags = TCL_THREAD_NOFLAGS;
180570f7e25Sdanielk1977 
18144918fa0Sdanielk1977   assert(objc==4);
18239070edbSshane   UNUSED_PARAMETER(clientData);
18339070edbSshane   UNUSED_PARAMETER(objc);
18444918fa0Sdanielk1977 
18544918fa0Sdanielk1977   zVarname = Tcl_GetStringFromObj(objv[2], &nVarname);
18644918fa0Sdanielk1977   zScript = Tcl_GetStringFromObj(objv[3], &nScript);
187570f7e25Sdanielk1977 
188570f7e25Sdanielk1977   pNew = (SqlThread *)ckalloc(sizeof(SqlThread)+nVarname+nScript+2);
18944918fa0Sdanielk1977   pNew->zVarname = (char *)&pNew[1];
19044918fa0Sdanielk1977   pNew->zScript = (char *)&pNew->zVarname[nVarname+1];
19144918fa0Sdanielk1977   memcpy(pNew->zVarname, zVarname, nVarname+1);
19244918fa0Sdanielk1977   memcpy(pNew->zScript, zScript, nScript+1);
193570f7e25Sdanielk1977   pNew->parent = Tcl_GetCurrentThread();
194570f7e25Sdanielk1977   pNew->interp = interp;
19544918fa0Sdanielk1977 
196570f7e25Sdanielk1977   rc = Tcl_CreateThread(&x, tclScriptThread, (void *)pNew, nStack, flags);
197570f7e25Sdanielk1977   if( rc!=TCL_OK ){
198570f7e25Sdanielk1977     Tcl_AppendResult(interp, "Error in Tcl_CreateThread()", 0);
19981fa193aSdanielk1977     ckfree((char *)pNew);
20044918fa0Sdanielk1977     return TCL_ERROR;
20144918fa0Sdanielk1977   }
20244918fa0Sdanielk1977 
20344918fa0Sdanielk1977   return TCL_OK;
20444918fa0Sdanielk1977 }
20544918fa0Sdanielk1977 
20644918fa0Sdanielk1977 /*
20744918fa0Sdanielk1977 ** sqlthread parent SCRIPT
20844918fa0Sdanielk1977 **
20944918fa0Sdanielk1977 **     This can be called by spawned threads only. It sends the specified
21044918fa0Sdanielk1977 **     script back to the parent thread for execution. The result of
21144918fa0Sdanielk1977 **     evaluating the SCRIPT is returned. The parent thread must enter
21244918fa0Sdanielk1977 **     the event loop for this to work - otherwise the caller will
21344918fa0Sdanielk1977 **     block indefinitely.
21444918fa0Sdanielk1977 **
21544918fa0Sdanielk1977 **     NOTE: At the moment, this doesn't work. FIXME.
21644918fa0Sdanielk1977 */
21744918fa0Sdanielk1977 static int sqlthread_parent(
21844918fa0Sdanielk1977   ClientData clientData,
21944918fa0Sdanielk1977   Tcl_Interp *interp,
22044918fa0Sdanielk1977   int objc,
22144918fa0Sdanielk1977   Tcl_Obj *CONST objv[]
22244918fa0Sdanielk1977 ){
223570f7e25Sdanielk1977   EvalEvent *pEvent;
22444918fa0Sdanielk1977   char *zMsg;
22544918fa0Sdanielk1977   int nMsg;
22644918fa0Sdanielk1977   SqlThread *p = (SqlThread *)clientData;
22744918fa0Sdanielk1977 
22844918fa0Sdanielk1977   assert(objc==3);
22939070edbSshane   UNUSED_PARAMETER(objc);
23039070edbSshane 
23144918fa0Sdanielk1977   if( p==0 ){
23244918fa0Sdanielk1977     Tcl_AppendResult(interp, "no parent thread", 0);
23344918fa0Sdanielk1977     return TCL_ERROR;
23444918fa0Sdanielk1977   }
23544918fa0Sdanielk1977 
23644918fa0Sdanielk1977   zMsg = Tcl_GetStringFromObj(objv[2], &nMsg);
237570f7e25Sdanielk1977   pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
238570f7e25Sdanielk1977   pEvent->base.nextPtr = 0;
239570f7e25Sdanielk1977   pEvent->base.proc = tclScriptEvent;
240570f7e25Sdanielk1977   pEvent->zScript = (char *)&pEvent[1];
241570f7e25Sdanielk1977   memcpy(pEvent->zScript, zMsg, nMsg+1);
242570f7e25Sdanielk1977   pEvent->interp = p->interp;
243570f7e25Sdanielk1977   Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
244570f7e25Sdanielk1977   Tcl_ThreadAlert(p->parent);
24544918fa0Sdanielk1977 
24644918fa0Sdanielk1977   return TCL_OK;
24744918fa0Sdanielk1977 }
24844918fa0Sdanielk1977 
249d9b5b117Sdanielk1977 static int xBusy(void *pArg, int nBusy){
25039070edbSshane   UNUSED_PARAMETER(pArg);
25139070edbSshane   UNUSED_PARAMETER(nBusy);
252d9b5b117Sdanielk1977   sqlite3_sleep(50);
253d9b5b117Sdanielk1977   return 1;             /* Try again... */
254d9b5b117Sdanielk1977 }
255d9b5b117Sdanielk1977 
256e9dcd5e6Sdanielk1977 /*
257e9dcd5e6Sdanielk1977 ** sqlthread open
258e9dcd5e6Sdanielk1977 **
259e9dcd5e6Sdanielk1977 **     Open a database handle and return the string representation of
260e9dcd5e6Sdanielk1977 **     the pointer value.
261e9dcd5e6Sdanielk1977 */
262d9b5b117Sdanielk1977 static int sqlthread_open(
263d9b5b117Sdanielk1977   ClientData clientData,
264d9b5b117Sdanielk1977   Tcl_Interp *interp,
265d9b5b117Sdanielk1977   int objc,
266d9b5b117Sdanielk1977   Tcl_Obj *CONST objv[]
267d9b5b117Sdanielk1977 ){
268d9b5b117Sdanielk1977   int sqlite3TestMakePointerStr(Tcl_Interp *interp, char *zPtr, void *p);
269d9b5b117Sdanielk1977 
270d9b5b117Sdanielk1977   const char *zFilename;
271d9b5b117Sdanielk1977   sqlite3 *db;
272d9b5b117Sdanielk1977   int rc;
273d9b5b117Sdanielk1977   char zBuf[100];
274d9b5b117Sdanielk1977   extern void Md5_Register(sqlite3*);
275d9b5b117Sdanielk1977 
27639070edbSshane   UNUSED_PARAMETER(clientData);
27739070edbSshane   UNUSED_PARAMETER(objc);
27839070edbSshane 
279d9b5b117Sdanielk1977   zFilename = Tcl_GetString(objv[2]);
280d9b5b117Sdanielk1977   rc = sqlite3_open(zFilename, &db);
281d9b5b117Sdanielk1977   Md5_Register(db);
282d9b5b117Sdanielk1977   sqlite3_busy_handler(db, xBusy, 0);
283d9b5b117Sdanielk1977 
284d9b5b117Sdanielk1977   if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
285d9b5b117Sdanielk1977   Tcl_AppendResult(interp, zBuf, 0);
286d9b5b117Sdanielk1977 
287d9b5b117Sdanielk1977   return TCL_OK;
288d9b5b117Sdanielk1977 }
289d9b5b117Sdanielk1977 
290d9b5b117Sdanielk1977 
29144918fa0Sdanielk1977 /*
292e9dcd5e6Sdanielk1977 ** sqlthread open
293e9dcd5e6Sdanielk1977 **
294e9dcd5e6Sdanielk1977 **     Return the current thread-id (Tcl_GetCurrentThread()) cast to
295e9dcd5e6Sdanielk1977 **     an integer.
296e9dcd5e6Sdanielk1977 */
297e9dcd5e6Sdanielk1977 static int sqlthread_id(
298e9dcd5e6Sdanielk1977   ClientData clientData,
299e9dcd5e6Sdanielk1977   Tcl_Interp *interp,
300e9dcd5e6Sdanielk1977   int objc,
301e9dcd5e6Sdanielk1977   Tcl_Obj *CONST objv[]
302e9dcd5e6Sdanielk1977 ){
303e9dcd5e6Sdanielk1977   Tcl_ThreadId id = Tcl_GetCurrentThread();
304e9dcd5e6Sdanielk1977   Tcl_SetObjResult(interp, Tcl_NewIntObj((int)id));
30539070edbSshane   UNUSED_PARAMETER(clientData);
30639070edbSshane   UNUSED_PARAMETER(objc);
30739070edbSshane   UNUSED_PARAMETER(objv);
308e9dcd5e6Sdanielk1977   return TCL_OK;
309e9dcd5e6Sdanielk1977 }
310e9dcd5e6Sdanielk1977 
311e9dcd5e6Sdanielk1977 
312e9dcd5e6Sdanielk1977 /*
31344918fa0Sdanielk1977 ** Dispatch routine for the sub-commands of [sqlthread].
31444918fa0Sdanielk1977 */
31544918fa0Sdanielk1977 static int sqlthread_proc(
31644918fa0Sdanielk1977   ClientData clientData,
31744918fa0Sdanielk1977   Tcl_Interp *interp,
31844918fa0Sdanielk1977   int objc,
31944918fa0Sdanielk1977   Tcl_Obj *CONST objv[]
32044918fa0Sdanielk1977 ){
32144918fa0Sdanielk1977   struct SubCommand {
32244918fa0Sdanielk1977     char *zName;
32344918fa0Sdanielk1977     Tcl_ObjCmdProc *xProc;
32444918fa0Sdanielk1977     int nArg;
32544918fa0Sdanielk1977     char *zUsage;
32644918fa0Sdanielk1977   } aSub[] = {
32744918fa0Sdanielk1977     {"parent", sqlthread_parent, 1, "SCRIPT"},
32844918fa0Sdanielk1977     {"spawn",  sqlthread_spawn,  2, "VARNAME SCRIPT"},
329d9b5b117Sdanielk1977     {"open",   sqlthread_open,   1, "DBNAME"},
330e9dcd5e6Sdanielk1977     {"id",     sqlthread_id,     0, ""},
33144918fa0Sdanielk1977     {0, 0, 0}
33244918fa0Sdanielk1977   };
33344918fa0Sdanielk1977   struct SubCommand *pSub;
33444918fa0Sdanielk1977   int rc;
33544918fa0Sdanielk1977   int iIndex;
33644918fa0Sdanielk1977 
33744918fa0Sdanielk1977   if( objc<2 ){
33844918fa0Sdanielk1977     Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND");
33944918fa0Sdanielk1977     return TCL_ERROR;
34044918fa0Sdanielk1977   }
34144918fa0Sdanielk1977 
34244918fa0Sdanielk1977   rc = Tcl_GetIndexFromObjStruct(
34344918fa0Sdanielk1977       interp, objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iIndex
34444918fa0Sdanielk1977   );
34544918fa0Sdanielk1977   if( rc!=TCL_OK ) return rc;
34644918fa0Sdanielk1977   pSub = &aSub[iIndex];
34744918fa0Sdanielk1977 
34844918fa0Sdanielk1977   if( objc!=(pSub->nArg+2) ){
34944918fa0Sdanielk1977     Tcl_WrongNumArgs(interp, 2, objv, pSub->zUsage);
35044918fa0Sdanielk1977     return TCL_ERROR;
35144918fa0Sdanielk1977   }
35244918fa0Sdanielk1977 
35344918fa0Sdanielk1977   return pSub->xProc(clientData, interp, objc, objv);
35444918fa0Sdanielk1977 }
35544918fa0Sdanielk1977 
35644918fa0Sdanielk1977 /*
35781fa193aSdanielk1977 ** The [clock_seconds] command. This is more or less the same as the
35881fa193aSdanielk1977 ** regular tcl [clock seconds], except that it is available in testfixture
35981fa193aSdanielk1977 ** when linked against both Tcl 8.4 and 8.5. Because [clock seconds] is
36081fa193aSdanielk1977 ** implemented as a script in Tcl 8.5, it is not usually available to
36181fa193aSdanielk1977 ** testfixture.
36281fa193aSdanielk1977 */
36381fa193aSdanielk1977 static int clock_seconds_proc(
36481fa193aSdanielk1977   ClientData clientData,
36581fa193aSdanielk1977   Tcl_Interp *interp,
36681fa193aSdanielk1977   int objc,
36781fa193aSdanielk1977   Tcl_Obj *CONST objv[]
36881fa193aSdanielk1977 ){
36981fa193aSdanielk1977   Tcl_Time now;
37081fa193aSdanielk1977   Tcl_GetTime(&now);
37181fa193aSdanielk1977   Tcl_SetObjResult(interp, Tcl_NewIntObj(now.sec));
37239070edbSshane   UNUSED_PARAMETER(clientData);
37339070edbSshane   UNUSED_PARAMETER(objc);
37439070edbSshane   UNUSED_PARAMETER(objv);
37581fa193aSdanielk1977   return TCL_OK;
37681fa193aSdanielk1977 }
37781fa193aSdanielk1977 
378404ca075Sdanielk1977 /*************************************************************************
379404ca075Sdanielk1977 ** This block contains the implementation of the [sqlite3_blocking_step]
380404ca075Sdanielk1977 ** command available to threads created by [sqlthread spawn] commands. It
381404ca075Sdanielk1977 ** is only available on UNIX for now. This is because pthread condition
382404ca075Sdanielk1977 ** variables are used.
383404ca075Sdanielk1977 **
384404ca075Sdanielk1977 ** The source code for the C functions sqlite3_blocking_step(),
385404ca075Sdanielk1977 ** blocking_step_notify() and the structure UnlockNotification is
386404ca075Sdanielk1977 ** automatically extracted from this file and used as part of the
387404ca075Sdanielk1977 ** documentation for the sqlite3_unlock_notify() API function. This
388404ca075Sdanielk1977 ** should be considered if these functions are to be extended (i.e. to
389404ca075Sdanielk1977 ** support windows) in the future.
390404ca075Sdanielk1977 */
391404ca075Sdanielk1977 #if defined(OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
392404ca075Sdanielk1977 
393404ca075Sdanielk1977 /* BEGIN_SQLITE_BLOCKING_STEP */
394404ca075Sdanielk1977 /* This example uses the pthreads API */
395404ca075Sdanielk1977 #include <pthread.h>
396404ca075Sdanielk1977 
397404ca075Sdanielk1977 /*
398404ca075Sdanielk1977 ** A pointer to an instance of this structure is passed as the user-context
399404ca075Sdanielk1977 ** pointer when registering for an unlock-notify callback.
400404ca075Sdanielk1977 */
401404ca075Sdanielk1977 typedef struct UnlockNotification UnlockNotification;
402404ca075Sdanielk1977 struct UnlockNotification {
403404ca075Sdanielk1977   int fired;                           /* True after unlock event has occured */
404404ca075Sdanielk1977   pthread_cond_t cond;                 /* Condition variable to wait on */
405404ca075Sdanielk1977   pthread_mutex_t mutex;               /* Mutex to protect structure */
406404ca075Sdanielk1977 };
407404ca075Sdanielk1977 
408404ca075Sdanielk1977 /*
409404ca075Sdanielk1977 ** This function is an unlock-notify callback registered with SQLite.
410404ca075Sdanielk1977 */
41165a2ea11Sdanielk1977 static void unlock_notify_cb(void **apArg, int nArg){
412404ca075Sdanielk1977   int i;
413404ca075Sdanielk1977   for(i=0; i<nArg; i++){
414404ca075Sdanielk1977     UnlockNotification *p = (UnlockNotification *)apArg[i];
415404ca075Sdanielk1977     pthread_mutex_lock(&p->mutex);
416404ca075Sdanielk1977     p->fired = 1;
417404ca075Sdanielk1977     pthread_cond_signal(&p->cond);
418404ca075Sdanielk1977     pthread_mutex_unlock(&p->mutex);
419404ca075Sdanielk1977   }
420404ca075Sdanielk1977 }
421404ca075Sdanielk1977 
422404ca075Sdanielk1977 /*
42365a2ea11Sdanielk1977 ** This function assumes that an SQLite API call (either sqlite3_prepare_v2()
42465a2ea11Sdanielk1977 ** or sqlite3_step()) has just returned SQLITE_LOCKED. The argument is the
42565a2ea11Sdanielk1977 ** associated database connection.
42665a2ea11Sdanielk1977 **
42765a2ea11Sdanielk1977 ** This function calls sqlite3_unlock_notify() to register for an
42865a2ea11Sdanielk1977 ** unlock-notify callback, then blocks until that callback is delivered
42965a2ea11Sdanielk1977 ** and returns SQLITE_OK. The caller should then retry the failed operation.
43065a2ea11Sdanielk1977 **
43165a2ea11Sdanielk1977 ** Or, if sqlite3_unlock_notify() indicates that to block would deadlock
43265a2ea11Sdanielk1977 ** the system, then this function returns SQLITE_LOCKED immediately. In
43365a2ea11Sdanielk1977 ** this case the caller should not retry the operation and should roll
43465a2ea11Sdanielk1977 ** back the current transaction (if any).
43565a2ea11Sdanielk1977 */
43665a2ea11Sdanielk1977 static int wait_for_unlock_notify(sqlite3 *db){
43765a2ea11Sdanielk1977   int rc;
43865a2ea11Sdanielk1977   UnlockNotification un;
43965a2ea11Sdanielk1977 
44065a2ea11Sdanielk1977   /* Initialize the UnlockNotification structure. */
44165a2ea11Sdanielk1977   un.fired = 0;
44265a2ea11Sdanielk1977   pthread_mutex_init(&un.mutex, 0);
44365a2ea11Sdanielk1977   pthread_cond_init(&un.cond, 0);
44465a2ea11Sdanielk1977 
44565a2ea11Sdanielk1977   /* Register for an unlock-notify callback. */
44665a2ea11Sdanielk1977   rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un);
44765a2ea11Sdanielk1977   assert( rc==SQLITE_LOCKED || rc==SQLITE_OK );
44865a2ea11Sdanielk1977 
44965a2ea11Sdanielk1977   /* The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED
45065a2ea11Sdanielk1977   ** or SQLITE_OK.
45165a2ea11Sdanielk1977   **
45265a2ea11Sdanielk1977   ** If SQLITE_LOCKED was returned, then the system is deadlocked. In this
45365a2ea11Sdanielk1977   ** case this function needs to return SQLITE_LOCKED to the caller so
45465a2ea11Sdanielk1977   ** that the current transaction can be rolled back. Otherwise, block
45565a2ea11Sdanielk1977   ** until the unlock-notify callback is invoked, then return SQLITE_OK.
45665a2ea11Sdanielk1977   */
45765a2ea11Sdanielk1977   if( rc==SQLITE_OK ){
45865a2ea11Sdanielk1977     pthread_mutex_lock(&un.mutex);
45965a2ea11Sdanielk1977     if( !un.fired ){
46065a2ea11Sdanielk1977       pthread_cond_wait(&un.cond, &un.mutex);
46165a2ea11Sdanielk1977     }
46265a2ea11Sdanielk1977     pthread_mutex_unlock(&un.mutex);
46365a2ea11Sdanielk1977   }
46465a2ea11Sdanielk1977 
46565a2ea11Sdanielk1977   /* Destroy the mutex and condition variables. */
46665a2ea11Sdanielk1977   pthread_cond_destroy(&un.cond);
46765a2ea11Sdanielk1977   pthread_mutex_destroy(&un.mutex);
46865a2ea11Sdanielk1977 
46965a2ea11Sdanielk1977   return rc;
47065a2ea11Sdanielk1977 }
47165a2ea11Sdanielk1977 
47265a2ea11Sdanielk1977 /*
473404ca075Sdanielk1977 ** This function is a wrapper around the SQLite function sqlite3_step().
474404ca075Sdanielk1977 ** It functions in the same way as step(), except that if a required
475404ca075Sdanielk1977 ** shared-cache lock cannot be obtained, this function may block waiting for
476404ca075Sdanielk1977 ** the lock to become available. In this scenario the normal API step()
477404ca075Sdanielk1977 ** function always returns SQLITE_LOCKED.
478404ca075Sdanielk1977 **
479404ca075Sdanielk1977 ** If this function returns SQLITE_LOCKED, the caller should rollback
480404ca075Sdanielk1977 ** the current transaction (if any) and try again later. Otherwise, the
481404ca075Sdanielk1977 ** system may become deadlocked.
482404ca075Sdanielk1977 */
483404ca075Sdanielk1977 int sqlite3_blocking_step(sqlite3_stmt *pStmt){
48465a2ea11Sdanielk1977   int rc;
48565a2ea11Sdanielk1977   while( SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){
48665a2ea11Sdanielk1977     rc = wait_for_unlock_notify(sqlite3_db_handle(pStmt));
48765a2ea11Sdanielk1977     if( rc!=SQLITE_OK ) break;
488404ca075Sdanielk1977     sqlite3_reset(pStmt);
489404ca075Sdanielk1977   }
49065a2ea11Sdanielk1977   return rc;
491404ca075Sdanielk1977 }
492404ca075Sdanielk1977 
49365a2ea11Sdanielk1977 /*
49465a2ea11Sdanielk1977 ** This function is a wrapper around the SQLite function sqlite3_prepare_v2().
49565a2ea11Sdanielk1977 ** It functions in the same way as prepare_v2(), except that if a required
49665a2ea11Sdanielk1977 ** shared-cache lock cannot be obtained, this function may block waiting for
49765a2ea11Sdanielk1977 ** the lock to become available. In this scenario the normal API prepare_v2()
49865a2ea11Sdanielk1977 ** function always returns SQLITE_LOCKED.
49965a2ea11Sdanielk1977 **
50065a2ea11Sdanielk1977 ** If this function returns SQLITE_LOCKED, the caller should rollback
50165a2ea11Sdanielk1977 ** the current transaction (if any) and try again later. Otherwise, the
50265a2ea11Sdanielk1977 ** system may become deadlocked.
50365a2ea11Sdanielk1977 */
50465a2ea11Sdanielk1977 int sqlite3_blocking_prepare_v2(
50565a2ea11Sdanielk1977   sqlite3 *db,              /* Database handle. */
50665a2ea11Sdanielk1977   const char *zSql,         /* UTF-8 encoded SQL statement. */
50765a2ea11Sdanielk1977   int nSql,                 /* Length of zSql in bytes. */
50865a2ea11Sdanielk1977   sqlite3_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
50965a2ea11Sdanielk1977   const char **pz           /* OUT: End of parsed string */
51065a2ea11Sdanielk1977 ){
51165a2ea11Sdanielk1977   int rc;
51265a2ea11Sdanielk1977   while( SQLITE_LOCKED==(rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, pz)) ){
51365a2ea11Sdanielk1977     rc = wait_for_unlock_notify(db);
51465a2ea11Sdanielk1977     if( rc!=SQLITE_OK ) break;
515404ca075Sdanielk1977   }
516404ca075Sdanielk1977   return rc;
517404ca075Sdanielk1977 }
518404ca075Sdanielk1977 /* END_SQLITE_BLOCKING_STEP */
519404ca075Sdanielk1977 
520404ca075Sdanielk1977 /*
521404ca075Sdanielk1977 ** Usage: sqlite3_blocking_step STMT
522404ca075Sdanielk1977 **
523404ca075Sdanielk1977 ** Advance the statement to the next row.
524404ca075Sdanielk1977 */
525404ca075Sdanielk1977 static int blocking_step_proc(
526404ca075Sdanielk1977   void * clientData,
527404ca075Sdanielk1977   Tcl_Interp *interp,
528404ca075Sdanielk1977   int objc,
529404ca075Sdanielk1977   Tcl_Obj *CONST objv[]
530404ca075Sdanielk1977 ){
531404ca075Sdanielk1977 
532404ca075Sdanielk1977   sqlite3_stmt *pStmt;
533404ca075Sdanielk1977   int rc;
534404ca075Sdanielk1977 
535404ca075Sdanielk1977   if( objc!=2 ){
536404ca075Sdanielk1977     Tcl_WrongNumArgs(interp, 1, objv, "STMT");
537404ca075Sdanielk1977     return TCL_ERROR;
538404ca075Sdanielk1977   }
539404ca075Sdanielk1977 
540404ca075Sdanielk1977   pStmt = (sqlite3_stmt*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
541404ca075Sdanielk1977   rc = sqlite3_blocking_step(pStmt);
542404ca075Sdanielk1977 
543404ca075Sdanielk1977   Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), 0);
544404ca075Sdanielk1977   return TCL_OK;
545404ca075Sdanielk1977 }
546404ca075Sdanielk1977 
54765a2ea11Sdanielk1977 /*
54865a2ea11Sdanielk1977 ** Usage: sqlite3_blocking_prepare_v2 DB sql bytes ?tailvar?
549*a8bbef84Sdanielk1977 ** Usage: sqlite3_nonblocking_prepare_v2 DB sql bytes ?tailvar?
55065a2ea11Sdanielk1977 */
55165a2ea11Sdanielk1977 static int blocking_prepare_v2_proc(
55265a2ea11Sdanielk1977   void * clientData,
55365a2ea11Sdanielk1977   Tcl_Interp *interp,
55465a2ea11Sdanielk1977   int objc,
55565a2ea11Sdanielk1977   Tcl_Obj *CONST objv[]
55665a2ea11Sdanielk1977 ){
55765a2ea11Sdanielk1977   sqlite3 *db;
55865a2ea11Sdanielk1977   const char *zSql;
55965a2ea11Sdanielk1977   int bytes;
56065a2ea11Sdanielk1977   const char *zTail = 0;
56165a2ea11Sdanielk1977   sqlite3_stmt *pStmt = 0;
56265a2ea11Sdanielk1977   char zBuf[50];
56365a2ea11Sdanielk1977   int rc;
564*a8bbef84Sdanielk1977   int isBlocking = !(clientData==0);
56565a2ea11Sdanielk1977 
56665a2ea11Sdanielk1977   if( objc!=5 && objc!=4 ){
56765a2ea11Sdanielk1977     Tcl_AppendResult(interp, "wrong # args: should be \"",
56865a2ea11Sdanielk1977        Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
56965a2ea11Sdanielk1977     return TCL_ERROR;
57065a2ea11Sdanielk1977   }
57165a2ea11Sdanielk1977   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
57265a2ea11Sdanielk1977   zSql = Tcl_GetString(objv[2]);
57365a2ea11Sdanielk1977   if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
57465a2ea11Sdanielk1977 
575*a8bbef84Sdanielk1977   if( isBlocking ){
576*a8bbef84Sdanielk1977     rc = sqlite3_blocking_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
577*a8bbef84Sdanielk1977   }else{
578*a8bbef84Sdanielk1977     rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
579*a8bbef84Sdanielk1977   }
580*a8bbef84Sdanielk1977 
58165a2ea11Sdanielk1977   assert(rc==SQLITE_OK || pStmt==0);
58265a2ea11Sdanielk1977   if( zTail && objc>=5 ){
58365a2ea11Sdanielk1977     if( bytes>=0 ){
58465a2ea11Sdanielk1977       bytes = bytes - (zTail-zSql);
58565a2ea11Sdanielk1977     }
58665a2ea11Sdanielk1977     Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
58765a2ea11Sdanielk1977   }
58865a2ea11Sdanielk1977   if( rc!=SQLITE_OK ){
58965a2ea11Sdanielk1977     assert( pStmt==0 );
59065a2ea11Sdanielk1977     sprintf(zBuf, "%s ", (char *)sqlite3TestErrorName(rc));
59165a2ea11Sdanielk1977     Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
59265a2ea11Sdanielk1977     return TCL_ERROR;
59365a2ea11Sdanielk1977   }
59465a2ea11Sdanielk1977 
59565a2ea11Sdanielk1977   if( pStmt ){
59665a2ea11Sdanielk1977     if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
59765a2ea11Sdanielk1977     Tcl_AppendResult(interp, zBuf, 0);
59865a2ea11Sdanielk1977   }
59965a2ea11Sdanielk1977   return TCL_OK;
60065a2ea11Sdanielk1977 }
60165a2ea11Sdanielk1977 
602404ca075Sdanielk1977 #endif
603404ca075Sdanielk1977 /*
604404ca075Sdanielk1977 ** End of implementation of [sqlite3_blocking_step].
605404ca075Sdanielk1977 ************************************************************************/
606404ca075Sdanielk1977 
60781fa193aSdanielk1977 /*
60844918fa0Sdanielk1977 ** Register commands with the TCL interpreter.
60944918fa0Sdanielk1977 */
61044918fa0Sdanielk1977 int SqlitetestThread_Init(Tcl_Interp *interp){
61144918fa0Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, 0, 0);
61281fa193aSdanielk1977   Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
613404ca075Sdanielk1977 #if defined(OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
614404ca075Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
615*a8bbef84Sdanielk1977   Tcl_CreateObjCommand(interp,
616*a8bbef84Sdanielk1977       "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
617*a8bbef84Sdanielk1977   Tcl_CreateObjCommand(interp,
618*a8bbef84Sdanielk1977       "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
619404ca075Sdanielk1977 #endif
62044918fa0Sdanielk1977   return TCL_OK;
62144918fa0Sdanielk1977 }
62244918fa0Sdanielk1977 #else
62344918fa0Sdanielk1977 int SqlitetestThread_Init(Tcl_Interp *interp){
62444918fa0Sdanielk1977   return TCL_OK;
62544918fa0Sdanielk1977 }
62644918fa0Sdanielk1977 #endif
627