xref: /sqlite-3.40.0/src/test_thread.c (revision 404ca075)
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*404ca075Sdanielk1977 ** $Id: test_thread.c,v 1.11 2009/03/16 13:19:36 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;
58*404ca075Sdanielk1977 static Tcl_ObjCmdProc blocking_step_proc;
59570f7e25Sdanielk1977 int Sqlitetest1_Init(Tcl_Interp *);
6044918fa0Sdanielk1977 
61570f7e25Sdanielk1977 /*
62570f7e25Sdanielk1977 ** Handler for events of type EvalEvent.
63570f7e25Sdanielk1977 */
64570f7e25Sdanielk1977 static int tclScriptEvent(Tcl_Event *evPtr, int flags){
65570f7e25Sdanielk1977   int rc;
66570f7e25Sdanielk1977   EvalEvent *p = (EvalEvent *)evPtr;
67570f7e25Sdanielk1977   rc = Tcl_Eval(p->interp, p->zScript);
68570f7e25Sdanielk1977   if( rc!=TCL_OK ){
69570f7e25Sdanielk1977     Tcl_BackgroundError(p->interp);
70570f7e25Sdanielk1977   }
7139070edbSshane   UNUSED_PARAMETER(flags);
72570f7e25Sdanielk1977   return 1;
73570f7e25Sdanielk1977 }
74570f7e25Sdanielk1977 
75570f7e25Sdanielk1977 /*
76570f7e25Sdanielk1977 ** Register an EvalEvent to evaluate the script pScript in the
77570f7e25Sdanielk1977 ** parent interpreter/thread of SqlThread p.
78570f7e25Sdanielk1977 */
79570f7e25Sdanielk1977 static void postToParent(SqlThread *p, Tcl_Obj *pScript){
80570f7e25Sdanielk1977   EvalEvent *pEvent;
81570f7e25Sdanielk1977   char *zMsg;
82570f7e25Sdanielk1977   int nMsg;
83570f7e25Sdanielk1977 
84570f7e25Sdanielk1977   zMsg = Tcl_GetStringFromObj(pScript, &nMsg);
85570f7e25Sdanielk1977   pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
86570f7e25Sdanielk1977   pEvent->base.nextPtr = 0;
87570f7e25Sdanielk1977   pEvent->base.proc = tclScriptEvent;
88570f7e25Sdanielk1977   pEvent->zScript = (char *)&pEvent[1];
89570f7e25Sdanielk1977   memcpy(pEvent->zScript, zMsg, nMsg+1);
90570f7e25Sdanielk1977   pEvent->interp = p->interp;
91570f7e25Sdanielk1977 
92570f7e25Sdanielk1977   Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
93570f7e25Sdanielk1977   Tcl_ThreadAlert(p->parent);
94570f7e25Sdanielk1977 }
95570f7e25Sdanielk1977 
96570f7e25Sdanielk1977 /*
97570f7e25Sdanielk1977 ** The main function for threads created with [sqlthread spawn].
98570f7e25Sdanielk1977 */
99570f7e25Sdanielk1977 static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){
10044918fa0Sdanielk1977   Tcl_Interp *interp;
10144918fa0Sdanielk1977   Tcl_Obj *pRes;
10244918fa0Sdanielk1977   Tcl_Obj *pList;
10344918fa0Sdanielk1977   int rc;
10444918fa0Sdanielk1977   SqlThread *p = (SqlThread *)pSqlThread;
105b8613ab1Sdrh   extern int Sqlitetest_mutex_Init(Tcl_Interp*);
10644918fa0Sdanielk1977 
10744918fa0Sdanielk1977   interp = Tcl_CreateInterp();
10881fa193aSdanielk1977   Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
10944918fa0Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, pSqlThread, 0);
110*404ca075Sdanielk1977 #if defined(OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
111*404ca075Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
112*404ca075Sdanielk1977 #endif
11344918fa0Sdanielk1977   Sqlitetest1_Init(interp);
114b8613ab1Sdrh   Sqlitetest_mutex_Init(interp);
11544918fa0Sdanielk1977 
11644918fa0Sdanielk1977   rc = Tcl_Eval(interp, p->zScript);
11744918fa0Sdanielk1977   pRes = Tcl_GetObjResult(interp);
11844918fa0Sdanielk1977   pList = Tcl_NewObj();
11944918fa0Sdanielk1977   Tcl_IncrRefCount(pList);
120570f7e25Sdanielk1977   Tcl_IncrRefCount(pRes);
12144918fa0Sdanielk1977 
122d9b5b117Sdanielk1977   if( rc!=TCL_OK ){
123d9b5b117Sdanielk1977     Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("error", -1));
124d9b5b117Sdanielk1977     Tcl_ListObjAppendElement(interp, pList, pRes);
125d9b5b117Sdanielk1977     postToParent(p, pList);
126d9b5b117Sdanielk1977     Tcl_DecrRefCount(pList);
127d9b5b117Sdanielk1977     pList = Tcl_NewObj();
128d9b5b117Sdanielk1977   }
129d9b5b117Sdanielk1977 
13044918fa0Sdanielk1977   Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("set", -1));
13144918fa0Sdanielk1977   Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(p->zVarname, -1));
13244918fa0Sdanielk1977   Tcl_ListObjAppendElement(interp, pList, pRes);
133570f7e25Sdanielk1977   postToParent(p, pList);
134570f7e25Sdanielk1977 
135570f7e25Sdanielk1977   ckfree((void *)p);
13644918fa0Sdanielk1977   Tcl_DecrRefCount(pList);
137570f7e25Sdanielk1977   Tcl_DecrRefCount(pRes);
13844918fa0Sdanielk1977   Tcl_DeleteInterp(interp);
13939070edbSshane   TCL_THREAD_CREATE_RETURN;
14044918fa0Sdanielk1977 }
14144918fa0Sdanielk1977 
14244918fa0Sdanielk1977 /*
14344918fa0Sdanielk1977 ** sqlthread spawn VARNAME SCRIPT
14444918fa0Sdanielk1977 **
14585b623f2Sdrh **     Spawn a new thread with its own Tcl interpreter and run the
14644918fa0Sdanielk1977 **     specified SCRIPT(s) in it. The thread terminates after running
14744918fa0Sdanielk1977 **     the script. The result of the script is stored in the variable
14844918fa0Sdanielk1977 **     VARNAME.
14944918fa0Sdanielk1977 **
15044918fa0Sdanielk1977 **     The caller can wait for the script to terminate using [vwait VARNAME].
15144918fa0Sdanielk1977 */
15244918fa0Sdanielk1977 static int sqlthread_spawn(
15344918fa0Sdanielk1977   ClientData clientData,
15444918fa0Sdanielk1977   Tcl_Interp *interp,
15544918fa0Sdanielk1977   int objc,
15644918fa0Sdanielk1977   Tcl_Obj *CONST objv[]
15744918fa0Sdanielk1977 ){
158570f7e25Sdanielk1977   Tcl_ThreadId x;
15944918fa0Sdanielk1977   SqlThread *pNew;
16044918fa0Sdanielk1977   int rc;
16144918fa0Sdanielk1977 
16244918fa0Sdanielk1977   int nVarname; char *zVarname;
16344918fa0Sdanielk1977   int nScript; char *zScript;
16444918fa0Sdanielk1977 
165570f7e25Sdanielk1977   /* Parameters for thread creation */
166570f7e25Sdanielk1977   const int nStack = TCL_THREAD_STACK_DEFAULT;
167570f7e25Sdanielk1977   const int flags = TCL_THREAD_NOFLAGS;
168570f7e25Sdanielk1977 
16944918fa0Sdanielk1977   assert(objc==4);
17039070edbSshane   UNUSED_PARAMETER(clientData);
17139070edbSshane   UNUSED_PARAMETER(objc);
17244918fa0Sdanielk1977 
17344918fa0Sdanielk1977   zVarname = Tcl_GetStringFromObj(objv[2], &nVarname);
17444918fa0Sdanielk1977   zScript = Tcl_GetStringFromObj(objv[3], &nScript);
175570f7e25Sdanielk1977 
176570f7e25Sdanielk1977   pNew = (SqlThread *)ckalloc(sizeof(SqlThread)+nVarname+nScript+2);
17744918fa0Sdanielk1977   pNew->zVarname = (char *)&pNew[1];
17844918fa0Sdanielk1977   pNew->zScript = (char *)&pNew->zVarname[nVarname+1];
17944918fa0Sdanielk1977   memcpy(pNew->zVarname, zVarname, nVarname+1);
18044918fa0Sdanielk1977   memcpy(pNew->zScript, zScript, nScript+1);
181570f7e25Sdanielk1977   pNew->parent = Tcl_GetCurrentThread();
182570f7e25Sdanielk1977   pNew->interp = interp;
18344918fa0Sdanielk1977 
184570f7e25Sdanielk1977   rc = Tcl_CreateThread(&x, tclScriptThread, (void *)pNew, nStack, flags);
185570f7e25Sdanielk1977   if( rc!=TCL_OK ){
186570f7e25Sdanielk1977     Tcl_AppendResult(interp, "Error in Tcl_CreateThread()", 0);
18781fa193aSdanielk1977     ckfree((char *)pNew);
18844918fa0Sdanielk1977     return TCL_ERROR;
18944918fa0Sdanielk1977   }
19044918fa0Sdanielk1977 
19144918fa0Sdanielk1977   return TCL_OK;
19244918fa0Sdanielk1977 }
19344918fa0Sdanielk1977 
19444918fa0Sdanielk1977 /*
19544918fa0Sdanielk1977 ** sqlthread parent SCRIPT
19644918fa0Sdanielk1977 **
19744918fa0Sdanielk1977 **     This can be called by spawned threads only. It sends the specified
19844918fa0Sdanielk1977 **     script back to the parent thread for execution. The result of
19944918fa0Sdanielk1977 **     evaluating the SCRIPT is returned. The parent thread must enter
20044918fa0Sdanielk1977 **     the event loop for this to work - otherwise the caller will
20144918fa0Sdanielk1977 **     block indefinitely.
20244918fa0Sdanielk1977 **
20344918fa0Sdanielk1977 **     NOTE: At the moment, this doesn't work. FIXME.
20444918fa0Sdanielk1977 */
20544918fa0Sdanielk1977 static int sqlthread_parent(
20644918fa0Sdanielk1977   ClientData clientData,
20744918fa0Sdanielk1977   Tcl_Interp *interp,
20844918fa0Sdanielk1977   int objc,
20944918fa0Sdanielk1977   Tcl_Obj *CONST objv[]
21044918fa0Sdanielk1977 ){
211570f7e25Sdanielk1977   EvalEvent *pEvent;
21244918fa0Sdanielk1977   char *zMsg;
21344918fa0Sdanielk1977   int nMsg;
21444918fa0Sdanielk1977   SqlThread *p = (SqlThread *)clientData;
21544918fa0Sdanielk1977 
21644918fa0Sdanielk1977   assert(objc==3);
21739070edbSshane   UNUSED_PARAMETER(objc);
21839070edbSshane 
21944918fa0Sdanielk1977   if( p==0 ){
22044918fa0Sdanielk1977     Tcl_AppendResult(interp, "no parent thread", 0);
22144918fa0Sdanielk1977     return TCL_ERROR;
22244918fa0Sdanielk1977   }
22344918fa0Sdanielk1977 
22444918fa0Sdanielk1977   zMsg = Tcl_GetStringFromObj(objv[2], &nMsg);
225570f7e25Sdanielk1977   pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
226570f7e25Sdanielk1977   pEvent->base.nextPtr = 0;
227570f7e25Sdanielk1977   pEvent->base.proc = tclScriptEvent;
228570f7e25Sdanielk1977   pEvent->zScript = (char *)&pEvent[1];
229570f7e25Sdanielk1977   memcpy(pEvent->zScript, zMsg, nMsg+1);
230570f7e25Sdanielk1977   pEvent->interp = p->interp;
231570f7e25Sdanielk1977   Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
232570f7e25Sdanielk1977   Tcl_ThreadAlert(p->parent);
23344918fa0Sdanielk1977 
23444918fa0Sdanielk1977   return TCL_OK;
23544918fa0Sdanielk1977 }
23644918fa0Sdanielk1977 
237d9b5b117Sdanielk1977 static int xBusy(void *pArg, int nBusy){
23839070edbSshane   UNUSED_PARAMETER(pArg);
23939070edbSshane   UNUSED_PARAMETER(nBusy);
240d9b5b117Sdanielk1977   sqlite3_sleep(50);
241d9b5b117Sdanielk1977   return 1;             /* Try again... */
242d9b5b117Sdanielk1977 }
243d9b5b117Sdanielk1977 
244e9dcd5e6Sdanielk1977 /*
245e9dcd5e6Sdanielk1977 ** sqlthread open
246e9dcd5e6Sdanielk1977 **
247e9dcd5e6Sdanielk1977 **     Open a database handle and return the string representation of
248e9dcd5e6Sdanielk1977 **     the pointer value.
249e9dcd5e6Sdanielk1977 */
250d9b5b117Sdanielk1977 static int sqlthread_open(
251d9b5b117Sdanielk1977   ClientData clientData,
252d9b5b117Sdanielk1977   Tcl_Interp *interp,
253d9b5b117Sdanielk1977   int objc,
254d9b5b117Sdanielk1977   Tcl_Obj *CONST objv[]
255d9b5b117Sdanielk1977 ){
256d9b5b117Sdanielk1977   int sqlite3TestMakePointerStr(Tcl_Interp *interp, char *zPtr, void *p);
257d9b5b117Sdanielk1977 
258d9b5b117Sdanielk1977   const char *zFilename;
259d9b5b117Sdanielk1977   sqlite3 *db;
260d9b5b117Sdanielk1977   int rc;
261d9b5b117Sdanielk1977   char zBuf[100];
262d9b5b117Sdanielk1977   extern void Md5_Register(sqlite3*);
263d9b5b117Sdanielk1977 
26439070edbSshane   UNUSED_PARAMETER(clientData);
26539070edbSshane   UNUSED_PARAMETER(objc);
26639070edbSshane 
267d9b5b117Sdanielk1977   zFilename = Tcl_GetString(objv[2]);
268d9b5b117Sdanielk1977   rc = sqlite3_open(zFilename, &db);
269d9b5b117Sdanielk1977   Md5_Register(db);
270d9b5b117Sdanielk1977   sqlite3_busy_handler(db, xBusy, 0);
271d9b5b117Sdanielk1977 
272d9b5b117Sdanielk1977   if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
273d9b5b117Sdanielk1977   Tcl_AppendResult(interp, zBuf, 0);
274d9b5b117Sdanielk1977 
275d9b5b117Sdanielk1977   return TCL_OK;
276d9b5b117Sdanielk1977 }
277d9b5b117Sdanielk1977 
278d9b5b117Sdanielk1977 
27944918fa0Sdanielk1977 /*
280e9dcd5e6Sdanielk1977 ** sqlthread open
281e9dcd5e6Sdanielk1977 **
282e9dcd5e6Sdanielk1977 **     Return the current thread-id (Tcl_GetCurrentThread()) cast to
283e9dcd5e6Sdanielk1977 **     an integer.
284e9dcd5e6Sdanielk1977 */
285e9dcd5e6Sdanielk1977 static int sqlthread_id(
286e9dcd5e6Sdanielk1977   ClientData clientData,
287e9dcd5e6Sdanielk1977   Tcl_Interp *interp,
288e9dcd5e6Sdanielk1977   int objc,
289e9dcd5e6Sdanielk1977   Tcl_Obj *CONST objv[]
290e9dcd5e6Sdanielk1977 ){
291e9dcd5e6Sdanielk1977   Tcl_ThreadId id = Tcl_GetCurrentThread();
292e9dcd5e6Sdanielk1977   Tcl_SetObjResult(interp, Tcl_NewIntObj((int)id));
29339070edbSshane   UNUSED_PARAMETER(clientData);
29439070edbSshane   UNUSED_PARAMETER(objc);
29539070edbSshane   UNUSED_PARAMETER(objv);
296e9dcd5e6Sdanielk1977   return TCL_OK;
297e9dcd5e6Sdanielk1977 }
298e9dcd5e6Sdanielk1977 
299e9dcd5e6Sdanielk1977 
300e9dcd5e6Sdanielk1977 /*
30144918fa0Sdanielk1977 ** Dispatch routine for the sub-commands of [sqlthread].
30244918fa0Sdanielk1977 */
30344918fa0Sdanielk1977 static int sqlthread_proc(
30444918fa0Sdanielk1977   ClientData clientData,
30544918fa0Sdanielk1977   Tcl_Interp *interp,
30644918fa0Sdanielk1977   int objc,
30744918fa0Sdanielk1977   Tcl_Obj *CONST objv[]
30844918fa0Sdanielk1977 ){
30944918fa0Sdanielk1977   struct SubCommand {
31044918fa0Sdanielk1977     char *zName;
31144918fa0Sdanielk1977     Tcl_ObjCmdProc *xProc;
31244918fa0Sdanielk1977     int nArg;
31344918fa0Sdanielk1977     char *zUsage;
31444918fa0Sdanielk1977   } aSub[] = {
31544918fa0Sdanielk1977     {"parent", sqlthread_parent, 1, "SCRIPT"},
31644918fa0Sdanielk1977     {"spawn",  sqlthread_spawn,  2, "VARNAME SCRIPT"},
317d9b5b117Sdanielk1977     {"open",   sqlthread_open,   1, "DBNAME"},
318e9dcd5e6Sdanielk1977     {"id",     sqlthread_id,     0, ""},
31944918fa0Sdanielk1977     {0, 0, 0}
32044918fa0Sdanielk1977   };
32144918fa0Sdanielk1977   struct SubCommand *pSub;
32244918fa0Sdanielk1977   int rc;
32344918fa0Sdanielk1977   int iIndex;
32444918fa0Sdanielk1977 
32544918fa0Sdanielk1977   if( objc<2 ){
32644918fa0Sdanielk1977     Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND");
32744918fa0Sdanielk1977     return TCL_ERROR;
32844918fa0Sdanielk1977   }
32944918fa0Sdanielk1977 
33044918fa0Sdanielk1977   rc = Tcl_GetIndexFromObjStruct(
33144918fa0Sdanielk1977       interp, objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iIndex
33244918fa0Sdanielk1977   );
33344918fa0Sdanielk1977   if( rc!=TCL_OK ) return rc;
33444918fa0Sdanielk1977   pSub = &aSub[iIndex];
33544918fa0Sdanielk1977 
33644918fa0Sdanielk1977   if( objc!=(pSub->nArg+2) ){
33744918fa0Sdanielk1977     Tcl_WrongNumArgs(interp, 2, objv, pSub->zUsage);
33844918fa0Sdanielk1977     return TCL_ERROR;
33944918fa0Sdanielk1977   }
34044918fa0Sdanielk1977 
34144918fa0Sdanielk1977   return pSub->xProc(clientData, interp, objc, objv);
34244918fa0Sdanielk1977 }
34344918fa0Sdanielk1977 
34444918fa0Sdanielk1977 /*
34581fa193aSdanielk1977 ** The [clock_seconds] command. This is more or less the same as the
34681fa193aSdanielk1977 ** regular tcl [clock seconds], except that it is available in testfixture
34781fa193aSdanielk1977 ** when linked against both Tcl 8.4 and 8.5. Because [clock seconds] is
34881fa193aSdanielk1977 ** implemented as a script in Tcl 8.5, it is not usually available to
34981fa193aSdanielk1977 ** testfixture.
35081fa193aSdanielk1977 */
35181fa193aSdanielk1977 static int clock_seconds_proc(
35281fa193aSdanielk1977   ClientData clientData,
35381fa193aSdanielk1977   Tcl_Interp *interp,
35481fa193aSdanielk1977   int objc,
35581fa193aSdanielk1977   Tcl_Obj *CONST objv[]
35681fa193aSdanielk1977 ){
35781fa193aSdanielk1977   Tcl_Time now;
35881fa193aSdanielk1977   Tcl_GetTime(&now);
35981fa193aSdanielk1977   Tcl_SetObjResult(interp, Tcl_NewIntObj(now.sec));
36039070edbSshane   UNUSED_PARAMETER(clientData);
36139070edbSshane   UNUSED_PARAMETER(objc);
36239070edbSshane   UNUSED_PARAMETER(objv);
36381fa193aSdanielk1977   return TCL_OK;
36481fa193aSdanielk1977 }
36581fa193aSdanielk1977 
366*404ca075Sdanielk1977 /*************************************************************************
367*404ca075Sdanielk1977 ** This block contains the implementation of the [sqlite3_blocking_step]
368*404ca075Sdanielk1977 ** command available to threads created by [sqlthread spawn] commands. It
369*404ca075Sdanielk1977 ** is only available on UNIX for now. This is because pthread condition
370*404ca075Sdanielk1977 ** variables are used.
371*404ca075Sdanielk1977 **
372*404ca075Sdanielk1977 ** The source code for the C functions sqlite3_blocking_step(),
373*404ca075Sdanielk1977 ** blocking_step_notify() and the structure UnlockNotification is
374*404ca075Sdanielk1977 ** automatically extracted from this file and used as part of the
375*404ca075Sdanielk1977 ** documentation for the sqlite3_unlock_notify() API function. This
376*404ca075Sdanielk1977 ** should be considered if these functions are to be extended (i.e. to
377*404ca075Sdanielk1977 ** support windows) in the future.
378*404ca075Sdanielk1977 */
379*404ca075Sdanielk1977 #if defined(OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
380*404ca075Sdanielk1977 
381*404ca075Sdanielk1977 /* BEGIN_SQLITE_BLOCKING_STEP */
382*404ca075Sdanielk1977 /* This example uses the pthreads API */
383*404ca075Sdanielk1977 #include <pthread.h>
384*404ca075Sdanielk1977 
385*404ca075Sdanielk1977 /*
386*404ca075Sdanielk1977 ** A pointer to an instance of this structure is passed as the user-context
387*404ca075Sdanielk1977 ** pointer when registering for an unlock-notify callback.
388*404ca075Sdanielk1977 */
389*404ca075Sdanielk1977 typedef struct UnlockNotification UnlockNotification;
390*404ca075Sdanielk1977 struct UnlockNotification {
391*404ca075Sdanielk1977   int fired;                           /* True after unlock event has occured */
392*404ca075Sdanielk1977   pthread_cond_t cond;                 /* Condition variable to wait on */
393*404ca075Sdanielk1977   pthread_mutex_t mutex;               /* Mutex to protect structure */
394*404ca075Sdanielk1977 };
395*404ca075Sdanielk1977 
396*404ca075Sdanielk1977 /*
397*404ca075Sdanielk1977 ** This function is an unlock-notify callback registered with SQLite.
398*404ca075Sdanielk1977 */
399*404ca075Sdanielk1977 static void blocking_step_notify(void **apArg, int nArg){
400*404ca075Sdanielk1977   int i;
401*404ca075Sdanielk1977   for(i=0; i<nArg; i++){
402*404ca075Sdanielk1977     UnlockNotification *p = (UnlockNotification *)apArg[i];
403*404ca075Sdanielk1977     pthread_mutex_lock(&p->mutex);
404*404ca075Sdanielk1977     p->fired = 1;
405*404ca075Sdanielk1977     pthread_cond_signal(&p->cond);
406*404ca075Sdanielk1977     pthread_mutex_unlock(&p->mutex);
407*404ca075Sdanielk1977   }
408*404ca075Sdanielk1977 }
409*404ca075Sdanielk1977 
410*404ca075Sdanielk1977 /*
411*404ca075Sdanielk1977 ** This function is a wrapper around the SQLite function sqlite3_step().
412*404ca075Sdanielk1977 ** It functions in the same way as step(), except that if a required
413*404ca075Sdanielk1977 ** shared-cache lock cannot be obtained, this function may block waiting for
414*404ca075Sdanielk1977 ** the lock to become available. In this scenario the normal API step()
415*404ca075Sdanielk1977 ** function always returns SQLITE_LOCKED.
416*404ca075Sdanielk1977 **
417*404ca075Sdanielk1977 ** If this function returns SQLITE_LOCKED, the caller should rollback
418*404ca075Sdanielk1977 ** the current transaction (if any) and try again later. Otherwise, the
419*404ca075Sdanielk1977 ** system may become deadlocked.
420*404ca075Sdanielk1977 */
421*404ca075Sdanielk1977 int sqlite3_blocking_step(sqlite3_stmt *pStmt){
422*404ca075Sdanielk1977   int rc = SQLITE_OK;
423*404ca075Sdanielk1977 
424*404ca075Sdanielk1977   while( rc==SQLITE_OK && SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){
425*404ca075Sdanielk1977     sqlite3 *db = sqlite3_db_handle(pStmt);
426*404ca075Sdanielk1977     UnlockNotification un;
427*404ca075Sdanielk1977 
428*404ca075Sdanielk1977     /* Initialize the UnlockNotification structure. */
429*404ca075Sdanielk1977     un.fired = 0;
430*404ca075Sdanielk1977     pthread_mutex_init(&un.mutex, 0);
431*404ca075Sdanielk1977     pthread_cond_init(&un.cond, 0);
432*404ca075Sdanielk1977 
433*404ca075Sdanielk1977     rc = sqlite3_unlock_notify(db, blocking_step_notify, (void *)&un);
434*404ca075Sdanielk1977     assert( rc==SQLITE_LOCKED || rc==SQLITE_OK );
435*404ca075Sdanielk1977 
436*404ca075Sdanielk1977     /* The call to sqlite3_unlock_notify() always returns either
437*404ca075Sdanielk1977     ** SQLITE_LOCKED or SQLITE_OK.
438*404ca075Sdanielk1977     **
439*404ca075Sdanielk1977     ** If SQLITE_LOCKED was returned, then the system is deadlocked. In this
440*404ca075Sdanielk1977     ** case this function needs to return SQLITE_LOCKED to the caller so
441*404ca075Sdanielk1977     ** that it can roll back the current transaction. Simply leaving rc
442*404ca075Sdanielk1977     ** as it is is enough to accomplish that, as the next test of the
443*404ca075Sdanielk1977     ** while() condition above will fail and the current value of rc
444*404ca075Sdanielk1977     ** (SQLITE_LOCKED) will be returned to the caller. sqlite3_reset() is
445*404ca075Sdanielk1977     ** not called on the statement handle, so the caller can still use either
446*404ca075Sdanielk1977     ** sqlite3_finalize() or reset() to collect the statement's error code
447*404ca075Sdanielk1977     ** after this function returns.
448*404ca075Sdanielk1977     **
449*404ca075Sdanielk1977     ** Otherwise, if SQLITE_OK was returned, do two things:
450*404ca075Sdanielk1977     **
451*404ca075Sdanielk1977     **   1) Reset the SQL statement.
452*404ca075Sdanielk1977     **   2) Block until the unlock-notify callback is invoked.
453*404ca075Sdanielk1977     */
454*404ca075Sdanielk1977     if( rc==SQLITE_OK ){
455*404ca075Sdanielk1977       sqlite3_reset(pStmt);
456*404ca075Sdanielk1977       pthread_mutex_lock(&un.mutex);
457*404ca075Sdanielk1977       if( !un.fired ){
458*404ca075Sdanielk1977         pthread_cond_wait(&un.cond, &un.mutex);
459*404ca075Sdanielk1977       }
460*404ca075Sdanielk1977       pthread_mutex_unlock(&un.mutex);
461*404ca075Sdanielk1977     }
462*404ca075Sdanielk1977 
463*404ca075Sdanielk1977     /* Destroy the mutex and condition variables created at the top of
464*404ca075Sdanielk1977     ** the while loop. */
465*404ca075Sdanielk1977     pthread_cond_destroy(&un.cond);
466*404ca075Sdanielk1977     pthread_mutex_destroy(&un.mutex);
467*404ca075Sdanielk1977   }
468*404ca075Sdanielk1977 
469*404ca075Sdanielk1977   return rc;
470*404ca075Sdanielk1977 }
471*404ca075Sdanielk1977 /* END_SQLITE_BLOCKING_STEP */
472*404ca075Sdanielk1977 
473*404ca075Sdanielk1977 /*
474*404ca075Sdanielk1977 ** Usage: sqlite3_blocking_step STMT
475*404ca075Sdanielk1977 **
476*404ca075Sdanielk1977 ** Advance the statement to the next row.
477*404ca075Sdanielk1977 */
478*404ca075Sdanielk1977 static int blocking_step_proc(
479*404ca075Sdanielk1977   void * clientData,
480*404ca075Sdanielk1977   Tcl_Interp *interp,
481*404ca075Sdanielk1977   int objc,
482*404ca075Sdanielk1977   Tcl_Obj *CONST objv[]
483*404ca075Sdanielk1977 ){
484*404ca075Sdanielk1977   /* Functions from test1.c */
485*404ca075Sdanielk1977   void *sqlite3TestTextToPtr(const char *);
486*404ca075Sdanielk1977   const char *sqlite3TestErrorName(int);
487*404ca075Sdanielk1977 
488*404ca075Sdanielk1977   sqlite3_stmt *pStmt;
489*404ca075Sdanielk1977   int rc;
490*404ca075Sdanielk1977 
491*404ca075Sdanielk1977   if( objc!=2 ){
492*404ca075Sdanielk1977     Tcl_WrongNumArgs(interp, 1, objv, "STMT");
493*404ca075Sdanielk1977     return TCL_ERROR;
494*404ca075Sdanielk1977   }
495*404ca075Sdanielk1977 
496*404ca075Sdanielk1977   pStmt = (sqlite3_stmt*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
497*404ca075Sdanielk1977   rc = sqlite3_blocking_step(pStmt);
498*404ca075Sdanielk1977 
499*404ca075Sdanielk1977   Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), 0);
500*404ca075Sdanielk1977   return TCL_OK;
501*404ca075Sdanielk1977 }
502*404ca075Sdanielk1977 
503*404ca075Sdanielk1977 #endif
504*404ca075Sdanielk1977 /*
505*404ca075Sdanielk1977 ** End of implementation of [sqlite3_blocking_step].
506*404ca075Sdanielk1977 ************************************************************************/
507*404ca075Sdanielk1977 
50881fa193aSdanielk1977 /*
50944918fa0Sdanielk1977 ** Register commands with the TCL interpreter.
51044918fa0Sdanielk1977 */
51144918fa0Sdanielk1977 int SqlitetestThread_Init(Tcl_Interp *interp){
51244918fa0Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, 0, 0);
51381fa193aSdanielk1977   Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
514*404ca075Sdanielk1977 #if defined(OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
515*404ca075Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
516*404ca075Sdanielk1977 #endif
51744918fa0Sdanielk1977   return TCL_OK;
51844918fa0Sdanielk1977 }
51944918fa0Sdanielk1977 #else
52044918fa0Sdanielk1977 int SqlitetestThread_Init(Tcl_Interp *interp){
52144918fa0Sdanielk1977   return TCL_OK;
52244918fa0Sdanielk1977 }
52344918fa0Sdanielk1977 #endif
524