blob: 20b4cf148bfee6c0a6b709aa308b496aeb7b0c13 [file] [log] [blame]
danielk197744918fa2007-09-07 11:29:25 +00001/*
2** 2007 September 9
3**
4** The author disclaims copyright to this source code. In place of
5** a legal notice, here is a blessing:
6**
7** May you do good and not evil.
8** May you find forgiveness for yourself and forgive others.
9** May you share freely, never taking more than you give.
10**
11*************************************************************************
12**
13** This file contains the implementation of some Tcl commands used to
14** test that sqlite3 database handles may be concurrently accessed by
15** multiple threads. Right now this only works on unix.
danielk197744918fa2007-09-07 11:29:25 +000016*/
17
18#include "sqliteInt.h"
mistachkin52b1dbb2016-07-28 14:37:04 +000019#if defined(INCLUDE_SQLITE_TCL_H)
20# include "sqlite_tcl.h"
21#else
22# include "tcl.h"
23#endif
danielk1977570f7e22007-09-07 18:40:38 +000024
drhb8613ab2009-01-19 17:40:12 +000025#if SQLITE_THREADSAFE
danielk197744918fa2007-09-07 11:29:25 +000026
danielk197744918fa2007-09-07 11:29:25 +000027#include <errno.h>
shane39070ed2009-02-03 19:55:20 +000028
29#if !defined(_MSC_VER)
danielk197744918fa2007-09-07 11:29:25 +000030#include <unistd.h>
shane39070ed2009-02-03 19:55:20 +000031#endif
danielk197744918fa2007-09-07 11:29:25 +000032
33/*
34** One of these is allocated for each thread created by [sqlthread spawn].
35*/
36typedef struct SqlThread SqlThread;
37struct SqlThread {
danielk1977570f7e22007-09-07 18:40:38 +000038 Tcl_ThreadId parent; /* Thread id of parent thread */
39 Tcl_Interp *interp; /* Parent interpreter */
40 char *zScript; /* The script to execute. */
41 char *zVarname; /* Varname in parent script */
danielk197744918fa2007-09-07 11:29:25 +000042};
43
danielk1977570f7e22007-09-07 18:40:38 +000044/*
45** A custom Tcl_Event type used by this module. When the event is
46** handled, script zScript is evaluated in interpreter interp. If
47** the evaluation throws an exception (returns TCL_ERROR), then the
48** error is handled by Tcl_BackgroundError(). If no error occurs,
49** the result is simply discarded.
50*/
51typedef struct EvalEvent EvalEvent;
52struct EvalEvent {
53 Tcl_Event base; /* Base class of type Tcl_Event */
54 char *zScript; /* The script to execute. */
55 Tcl_Interp *interp; /* The interpreter to execute it in. */
danielk197744918fa2007-09-07 11:29:25 +000056};
57
58static Tcl_ObjCmdProc sqlthread_proc;
danielk197781fa1932008-08-28 13:55:10 +000059static Tcl_ObjCmdProc clock_seconds_proc;
shaneh3a2d29f2011-04-04 21:48:01 +000060#if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
danielk1977404ca072009-03-16 13:19:36 +000061static Tcl_ObjCmdProc blocking_step_proc;
danielk197765a2ea12009-03-19 07:58:31 +000062static Tcl_ObjCmdProc blocking_prepare_v2_proc;
drh69910da2009-03-27 12:32:54 +000063#endif
danielk1977570f7e22007-09-07 18:40:38 +000064int Sqlitetest1_Init(Tcl_Interp *);
dan5e0ce872010-04-28 17:48:44 +000065int Sqlite3_Init(Tcl_Interp *);
danielk197744918fa2007-09-07 11:29:25 +000066
mistachkine84d8d32013-04-29 03:09:10 +000067/* Functions from main.c */
68extern const char *sqlite3ErrName(int);
69
danielk197765a2ea12009-03-19 07:58:31 +000070/* Functions from test1.c */
mistachkine84d8d32013-04-29 03:09:10 +000071extern void *sqlite3TestTextToPtr(const char *);
72extern int getDbPointer(Tcl_Interp *, const char *, sqlite3 **);
73extern int sqlite3TestMakePointerStr(Tcl_Interp *, char *, void *);
74extern int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int);
danielk197765a2ea12009-03-19 07:58:31 +000075
danielk1977570f7e22007-09-07 18:40:38 +000076/*
77** Handler for events of type EvalEvent.
78*/
mistachkin7617e4a2016-07-28 17:11:20 +000079static int SQLITE_TCLAPI tclScriptEvent(Tcl_Event *evPtr, int flags){
danielk1977570f7e22007-09-07 18:40:38 +000080 int rc;
81 EvalEvent *p = (EvalEvent *)evPtr;
82 rc = Tcl_Eval(p->interp, p->zScript);
83 if( rc!=TCL_OK ){
84 Tcl_BackgroundError(p->interp);
85 }
shane39070ed2009-02-03 19:55:20 +000086 UNUSED_PARAMETER(flags);
danielk1977570f7e22007-09-07 18:40:38 +000087 return 1;
88}
89
90/*
91** Register an EvalEvent to evaluate the script pScript in the
92** parent interpreter/thread of SqlThread p.
93*/
94static void postToParent(SqlThread *p, Tcl_Obj *pScript){
95 EvalEvent *pEvent;
96 char *zMsg;
97 int nMsg;
98
99 zMsg = Tcl_GetStringFromObj(pScript, &nMsg);
100 pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
101 pEvent->base.nextPtr = 0;
102 pEvent->base.proc = tclScriptEvent;
103 pEvent->zScript = (char *)&pEvent[1];
104 memcpy(pEvent->zScript, zMsg, nMsg+1);
105 pEvent->interp = p->interp;
106
107 Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
108 Tcl_ThreadAlert(p->parent);
109}
110
111/*
112** The main function for threads created with [sqlthread spawn].
113*/
114static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){
danielk197744918fa2007-09-07 11:29:25 +0000115 Tcl_Interp *interp;
116 Tcl_Obj *pRes;
117 Tcl_Obj *pList;
danielk197744918fa2007-09-07 11:29:25 +0000118 int rc;
danielk197744918fa2007-09-07 11:29:25 +0000119 SqlThread *p = (SqlThread *)pSqlThread;
drhb8613ab2009-01-19 17:40:12 +0000120 extern int Sqlitetest_mutex_Init(Tcl_Interp*);
danielk197744918fa2007-09-07 11:29:25 +0000121
122 interp = Tcl_CreateInterp();
danielk197781fa1932008-08-28 13:55:10 +0000123 Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
danielk197744918fa2007-09-07 11:29:25 +0000124 Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, pSqlThread, 0);
shaneh3a2d29f2011-04-04 21:48:01 +0000125#if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
danielk1977404ca072009-03-16 13:19:36 +0000126 Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
danielk1977a8bbef82009-03-23 17:11:26 +0000127 Tcl_CreateObjCommand(interp,
128 "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
129 Tcl_CreateObjCommand(interp,
130 "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
danielk1977404ca072009-03-16 13:19:36 +0000131#endif
danielk197744918fa2007-09-07 11:29:25 +0000132 Sqlitetest1_Init(interp);
drhb8613ab2009-01-19 17:40:12 +0000133 Sqlitetest_mutex_Init(interp);
dan5e0ce872010-04-28 17:48:44 +0000134 Sqlite3_Init(interp);
danielk197744918fa2007-09-07 11:29:25 +0000135
136 rc = Tcl_Eval(interp, p->zScript);
137 pRes = Tcl_GetObjResult(interp);
138 pList = Tcl_NewObj();
139 Tcl_IncrRefCount(pList);
danielk1977570f7e22007-09-07 18:40:38 +0000140 Tcl_IncrRefCount(pRes);
danielk197744918fa2007-09-07 11:29:25 +0000141
danielk1977d9b5b112007-09-10 06:23:53 +0000142 if( rc!=TCL_OK ){
danielk197744918fa2007-09-07 11:29:25 +0000143 Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("error", -1));
danielk1977d9b5b112007-09-10 06:23:53 +0000144 Tcl_ListObjAppendElement(interp, pList, pRes);
145 postToParent(p, pList);
146 Tcl_DecrRefCount(pList);
147 pList = Tcl_NewObj();
danielk197744918fa2007-09-07 11:29:25 +0000148 }
danielk197744918fa2007-09-07 11:29:25 +0000149
danielk1977d9b5b112007-09-10 06:23:53 +0000150 Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("set", -1));
151 Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(p->zVarname, -1));
152 Tcl_ListObjAppendElement(interp, pList, pRes);
danielk1977570f7e22007-09-07 18:40:38 +0000153 postToParent(p, pList);
154
155 ckfree((void *)p);
danielk197744918fa2007-09-07 11:29:25 +0000156 Tcl_DecrRefCount(pList);
danielk1977570f7e22007-09-07 18:40:38 +0000157 Tcl_DecrRefCount(pRes);
danielk197744918fa2007-09-07 11:29:25 +0000158 Tcl_DeleteInterp(interp);
dane1a2a992010-07-06 10:55:44 +0000159 while( Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT) );
dan3374f8a2010-07-05 12:54:08 +0000160 Tcl_ExitThread(0);
shane39070ed2009-02-03 19:55:20 +0000161 TCL_THREAD_CREATE_RETURN;
danielk197744918fa2007-09-07 11:29:25 +0000162}
163
164/*
165** sqlthread spawn VARNAME SCRIPT
166**
drh85b623f2007-12-13 21:54:09 +0000167** Spawn a new thread with its own Tcl interpreter and run the
danielk197744918fa2007-09-07 11:29:25 +0000168** specified SCRIPT(s) in it. The thread terminates after running
169** the script. The result of the script is stored in the variable
170** VARNAME.
171**
172** The caller can wait for the script to terminate using [vwait VARNAME].
173*/
mistachkin7617e4a2016-07-28 17:11:20 +0000174static int SQLITE_TCLAPI sqlthread_spawn(
danielk197744918fa2007-09-07 11:29:25 +0000175 ClientData clientData,
176 Tcl_Interp *interp,
177 int objc,
178 Tcl_Obj *CONST objv[]
179){
danielk1977570f7e22007-09-07 18:40:38 +0000180 Tcl_ThreadId x;
danielk197744918fa2007-09-07 11:29:25 +0000181 SqlThread *pNew;
danielk197744918fa2007-09-07 11:29:25 +0000182 int rc;
183
184 int nVarname; char *zVarname;
185 int nScript; char *zScript;
186
danielk1977570f7e22007-09-07 18:40:38 +0000187 /* Parameters for thread creation */
188 const int nStack = TCL_THREAD_STACK_DEFAULT;
189 const int flags = TCL_THREAD_NOFLAGS;
190
danielk197744918fa2007-09-07 11:29:25 +0000191 assert(objc==4);
shane39070ed2009-02-03 19:55:20 +0000192 UNUSED_PARAMETER(clientData);
193 UNUSED_PARAMETER(objc);
danielk197744918fa2007-09-07 11:29:25 +0000194
195 zVarname = Tcl_GetStringFromObj(objv[2], &nVarname);
196 zScript = Tcl_GetStringFromObj(objv[3], &nScript);
danielk1977570f7e22007-09-07 18:40:38 +0000197
198 pNew = (SqlThread *)ckalloc(sizeof(SqlThread)+nVarname+nScript+2);
danielk197744918fa2007-09-07 11:29:25 +0000199 pNew->zVarname = (char *)&pNew[1];
200 pNew->zScript = (char *)&pNew->zVarname[nVarname+1];
201 memcpy(pNew->zVarname, zVarname, nVarname+1);
202 memcpy(pNew->zScript, zScript, nScript+1);
danielk1977570f7e22007-09-07 18:40:38 +0000203 pNew->parent = Tcl_GetCurrentThread();
204 pNew->interp = interp;
danielk197744918fa2007-09-07 11:29:25 +0000205
danielk1977570f7e22007-09-07 18:40:38 +0000206 rc = Tcl_CreateThread(&x, tclScriptThread, (void *)pNew, nStack, flags);
207 if( rc!=TCL_OK ){
208 Tcl_AppendResult(interp, "Error in Tcl_CreateThread()", 0);
danielk197781fa1932008-08-28 13:55:10 +0000209 ckfree((char *)pNew);
danielk197744918fa2007-09-07 11:29:25 +0000210 return TCL_ERROR;
211 }
212
danielk197744918fa2007-09-07 11:29:25 +0000213 return TCL_OK;
214}
215
216/*
217** sqlthread parent SCRIPT
218**
219** This can be called by spawned threads only. It sends the specified
220** script back to the parent thread for execution. The result of
221** evaluating the SCRIPT is returned. The parent thread must enter
222** the event loop for this to work - otherwise the caller will
223** block indefinitely.
224**
225** NOTE: At the moment, this doesn't work. FIXME.
226*/
mistachkin7617e4a2016-07-28 17:11:20 +0000227static int SQLITE_TCLAPI sqlthread_parent(
danielk197744918fa2007-09-07 11:29:25 +0000228 ClientData clientData,
229 Tcl_Interp *interp,
230 int objc,
231 Tcl_Obj *CONST objv[]
232){
danielk1977570f7e22007-09-07 18:40:38 +0000233 EvalEvent *pEvent;
danielk197744918fa2007-09-07 11:29:25 +0000234 char *zMsg;
235 int nMsg;
236 SqlThread *p = (SqlThread *)clientData;
237
238 assert(objc==3);
shane39070ed2009-02-03 19:55:20 +0000239 UNUSED_PARAMETER(objc);
240
danielk197744918fa2007-09-07 11:29:25 +0000241 if( p==0 ){
242 Tcl_AppendResult(interp, "no parent thread", 0);
243 return TCL_ERROR;
244 }
245
246 zMsg = Tcl_GetStringFromObj(objv[2], &nMsg);
danielk1977570f7e22007-09-07 18:40:38 +0000247 pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
248 pEvent->base.nextPtr = 0;
249 pEvent->base.proc = tclScriptEvent;
250 pEvent->zScript = (char *)&pEvent[1];
251 memcpy(pEvent->zScript, zMsg, nMsg+1);
252 pEvent->interp = p->interp;
253 Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
254 Tcl_ThreadAlert(p->parent);
danielk197744918fa2007-09-07 11:29:25 +0000255
256 return TCL_OK;
257}
danielk197744918fa2007-09-07 11:29:25 +0000258
danielk1977d9b5b112007-09-10 06:23:53 +0000259static int xBusy(void *pArg, int nBusy){
shane39070ed2009-02-03 19:55:20 +0000260 UNUSED_PARAMETER(pArg);
261 UNUSED_PARAMETER(nBusy);
danielk1977d9b5b112007-09-10 06:23:53 +0000262 sqlite3_sleep(50);
263 return 1; /* Try again... */
264}
265
danielk1977e9dcd5e2007-09-10 10:53:01 +0000266/*
267** sqlthread open
268**
269** Open a database handle and return the string representation of
270** the pointer value.
271*/
mistachkin7617e4a2016-07-28 17:11:20 +0000272static int SQLITE_TCLAPI sqlthread_open(
danielk1977d9b5b112007-09-10 06:23:53 +0000273 ClientData clientData,
274 Tcl_Interp *interp,
275 int objc,
276 Tcl_Obj *CONST objv[]
277){
278 int sqlite3TestMakePointerStr(Tcl_Interp *interp, char *zPtr, void *p);
279
280 const char *zFilename;
281 sqlite3 *db;
danielk1977d9b5b112007-09-10 06:23:53 +0000282 char zBuf[100];
drh32c83c82016-08-01 14:35:48 +0000283 extern int Md5_Register(sqlite3*,char**,const sqlite3_api_routines*);
danielk1977d9b5b112007-09-10 06:23:53 +0000284
shane39070ed2009-02-03 19:55:20 +0000285 UNUSED_PARAMETER(clientData);
286 UNUSED_PARAMETER(objc);
287
danielk1977d9b5b112007-09-10 06:23:53 +0000288 zFilename = Tcl_GetString(objv[2]);
drhcaffb1a2012-01-30 18:00:31 +0000289 sqlite3_open(zFilename, &db);
drh0ee469c2011-08-30 19:52:32 +0000290#ifdef SQLITE_HAS_CODEC
291 if( db && objc>=4 ){
292 const char *zKey;
293 int nKey;
drhcaffb1a2012-01-30 18:00:31 +0000294 int rc;
drh0ee469c2011-08-30 19:52:32 +0000295 zKey = Tcl_GetStringFromObj(objv[3], &nKey);
296 rc = sqlite3_key(db, zKey, nKey);
297 if( rc!=SQLITE_OK ){
298 char *zErrMsg = sqlite3_mprintf("error %d: %s", rc, sqlite3_errmsg(db));
299 sqlite3_close(db);
300 Tcl_AppendResult(interp, zErrMsg, (char*)0);
301 sqlite3_free(zErrMsg);
302 return TCL_ERROR;
303 }
304 }
305#endif
mistachkin44e95d42016-07-28 22:23:26 +0000306 Md5_Register(db, 0, 0);
danielk1977d9b5b112007-09-10 06:23:53 +0000307 sqlite3_busy_handler(db, xBusy, 0);
308
309 if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
310 Tcl_AppendResult(interp, zBuf, 0);
311
312 return TCL_OK;
313}
314
315
danielk197744918fa2007-09-07 11:29:25 +0000316/*
danielk1977e9dcd5e2007-09-10 10:53:01 +0000317** sqlthread open
318**
319** Return the current thread-id (Tcl_GetCurrentThread()) cast to
320** an integer.
321*/
mistachkin7617e4a2016-07-28 17:11:20 +0000322static int SQLITE_TCLAPI sqlthread_id(
danielk1977e9dcd5e2007-09-10 10:53:01 +0000323 ClientData clientData,
324 Tcl_Interp *interp,
325 int objc,
326 Tcl_Obj *CONST objv[]
327){
328 Tcl_ThreadId id = Tcl_GetCurrentThread();
drh860e3322011-08-25 18:54:46 +0000329 Tcl_SetObjResult(interp, Tcl_NewIntObj(SQLITE_PTR_TO_INT(id)));
shane39070ed2009-02-03 19:55:20 +0000330 UNUSED_PARAMETER(clientData);
331 UNUSED_PARAMETER(objc);
332 UNUSED_PARAMETER(objv);
danielk1977e9dcd5e2007-09-10 10:53:01 +0000333 return TCL_OK;
334}
335
336
337/*
danielk197744918fa2007-09-07 11:29:25 +0000338** Dispatch routine for the sub-commands of [sqlthread].
339*/
mistachkin7617e4a2016-07-28 17:11:20 +0000340static int SQLITE_TCLAPI sqlthread_proc(
danielk197744918fa2007-09-07 11:29:25 +0000341 ClientData clientData,
342 Tcl_Interp *interp,
343 int objc,
344 Tcl_Obj *CONST objv[]
345){
346 struct SubCommand {
347 char *zName;
348 Tcl_ObjCmdProc *xProc;
349 int nArg;
350 char *zUsage;
351 } aSub[] = {
danielk197744918fa2007-09-07 11:29:25 +0000352 {"parent", sqlthread_parent, 1, "SCRIPT"},
danielk197744918fa2007-09-07 11:29:25 +0000353 {"spawn", sqlthread_spawn, 2, "VARNAME SCRIPT"},
danielk1977d9b5b112007-09-10 06:23:53 +0000354 {"open", sqlthread_open, 1, "DBNAME"},
danielk1977e9dcd5e2007-09-10 10:53:01 +0000355 {"id", sqlthread_id, 0, ""},
danielk197744918fa2007-09-07 11:29:25 +0000356 {0, 0, 0}
357 };
358 struct SubCommand *pSub;
359 int rc;
360 int iIndex;
361
362 if( objc<2 ){
363 Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND");
364 return TCL_ERROR;
365 }
366
367 rc = Tcl_GetIndexFromObjStruct(
danielk1977570f7e22007-09-07 18:40:38 +0000368 interp, objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iIndex
danielk197744918fa2007-09-07 11:29:25 +0000369 );
370 if( rc!=TCL_OK ) return rc;
371 pSub = &aSub[iIndex];
372
drh0ee469c2011-08-30 19:52:32 +0000373 if( objc<(pSub->nArg+2) ){
danielk197744918fa2007-09-07 11:29:25 +0000374 Tcl_WrongNumArgs(interp, 2, objv, pSub->zUsage);
375 return TCL_ERROR;
376 }
377
378 return pSub->xProc(clientData, interp, objc, objv);
379}
380
381/*
danielk197781fa1932008-08-28 13:55:10 +0000382** The [clock_seconds] command. This is more or less the same as the
383** regular tcl [clock seconds], except that it is available in testfixture
384** when linked against both Tcl 8.4 and 8.5. Because [clock seconds] is
385** implemented as a script in Tcl 8.5, it is not usually available to
386** testfixture.
387*/
mistachkin7617e4a2016-07-28 17:11:20 +0000388static int SQLITE_TCLAPI clock_seconds_proc(
danielk197781fa1932008-08-28 13:55:10 +0000389 ClientData clientData,
390 Tcl_Interp *interp,
391 int objc,
392 Tcl_Obj *CONST objv[]
393){
394 Tcl_Time now;
395 Tcl_GetTime(&now);
396 Tcl_SetObjResult(interp, Tcl_NewIntObj(now.sec));
shane39070ed2009-02-03 19:55:20 +0000397 UNUSED_PARAMETER(clientData);
398 UNUSED_PARAMETER(objc);
399 UNUSED_PARAMETER(objv);
danielk197781fa1932008-08-28 13:55:10 +0000400 return TCL_OK;
401}
402
danielk1977404ca072009-03-16 13:19:36 +0000403/*************************************************************************
404** This block contains the implementation of the [sqlite3_blocking_step]
405** command available to threads created by [sqlthread spawn] commands. It
406** is only available on UNIX for now. This is because pthread condition
407** variables are used.
408**
409** The source code for the C functions sqlite3_blocking_step(),
410** blocking_step_notify() and the structure UnlockNotification is
411** automatically extracted from this file and used as part of the
412** documentation for the sqlite3_unlock_notify() API function. This
413** should be considered if these functions are to be extended (i.e. to
414** support windows) in the future.
415*/
shaneh3a2d29f2011-04-04 21:48:01 +0000416#if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
danielk1977404ca072009-03-16 13:19:36 +0000417
418/* BEGIN_SQLITE_BLOCKING_STEP */
419/* This example uses the pthreads API */
420#include <pthread.h>
421
422/*
423** A pointer to an instance of this structure is passed as the user-context
424** pointer when registering for an unlock-notify callback.
425*/
426typedef struct UnlockNotification UnlockNotification;
427struct UnlockNotification {
drhef8662b2011-06-20 21:47:58 +0000428 int fired; /* True after unlock event has occurred */
429 pthread_cond_t cond; /* Condition variable to wait on */
430 pthread_mutex_t mutex; /* Mutex to protect structure */
danielk1977404ca072009-03-16 13:19:36 +0000431};
432
433/*
434** This function is an unlock-notify callback registered with SQLite.
435*/
danielk197765a2ea12009-03-19 07:58:31 +0000436static void unlock_notify_cb(void **apArg, int nArg){
danielk1977404ca072009-03-16 13:19:36 +0000437 int i;
438 for(i=0; i<nArg; i++){
439 UnlockNotification *p = (UnlockNotification *)apArg[i];
440 pthread_mutex_lock(&p->mutex);
441 p->fired = 1;
442 pthread_cond_signal(&p->cond);
443 pthread_mutex_unlock(&p->mutex);
444 }
445}
446
447/*
danielk197765a2ea12009-03-19 07:58:31 +0000448** This function assumes that an SQLite API call (either sqlite3_prepare_v2()
449** or sqlite3_step()) has just returned SQLITE_LOCKED. The argument is the
450** associated database connection.
451**
452** This function calls sqlite3_unlock_notify() to register for an
453** unlock-notify callback, then blocks until that callback is delivered
454** and returns SQLITE_OK. The caller should then retry the failed operation.
455**
456** Or, if sqlite3_unlock_notify() indicates that to block would deadlock
457** the system, then this function returns SQLITE_LOCKED immediately. In
458** this case the caller should not retry the operation and should roll
459** back the current transaction (if any).
460*/
461static int wait_for_unlock_notify(sqlite3 *db){
462 int rc;
463 UnlockNotification un;
464
465 /* Initialize the UnlockNotification structure. */
466 un.fired = 0;
467 pthread_mutex_init(&un.mutex, 0);
468 pthread_cond_init(&un.cond, 0);
469
470 /* Register for an unlock-notify callback. */
471 rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un);
472 assert( rc==SQLITE_LOCKED || rc==SQLITE_OK );
473
474 /* The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED
475 ** or SQLITE_OK.
476 **
477 ** If SQLITE_LOCKED was returned, then the system is deadlocked. In this
478 ** case this function needs to return SQLITE_LOCKED to the caller so
479 ** that the current transaction can be rolled back. Otherwise, block
480 ** until the unlock-notify callback is invoked, then return SQLITE_OK.
481 */
482 if( rc==SQLITE_OK ){
483 pthread_mutex_lock(&un.mutex);
484 if( !un.fired ){
485 pthread_cond_wait(&un.cond, &un.mutex);
486 }
487 pthread_mutex_unlock(&un.mutex);
488 }
489
490 /* Destroy the mutex and condition variables. */
491 pthread_cond_destroy(&un.cond);
492 pthread_mutex_destroy(&un.mutex);
493
494 return rc;
495}
496
497/*
danielk1977404ca072009-03-16 13:19:36 +0000498** This function is a wrapper around the SQLite function sqlite3_step().
499** It functions in the same way as step(), except that if a required
500** shared-cache lock cannot be obtained, this function may block waiting for
501** the lock to become available. In this scenario the normal API step()
502** function always returns SQLITE_LOCKED.
503**
504** If this function returns SQLITE_LOCKED, the caller should rollback
505** the current transaction (if any) and try again later. Otherwise, the
506** system may become deadlocked.
507*/
508int sqlite3_blocking_step(sqlite3_stmt *pStmt){
danielk197765a2ea12009-03-19 07:58:31 +0000509 int rc;
510 while( SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){
511 rc = wait_for_unlock_notify(sqlite3_db_handle(pStmt));
512 if( rc!=SQLITE_OK ) break;
513 sqlite3_reset(pStmt);
danielk1977404ca072009-03-16 13:19:36 +0000514 }
danielk197765a2ea12009-03-19 07:58:31 +0000515 return rc;
516}
danielk1977404ca072009-03-16 13:19:36 +0000517
danielk197765a2ea12009-03-19 07:58:31 +0000518/*
519** This function is a wrapper around the SQLite function sqlite3_prepare_v2().
520** It functions in the same way as prepare_v2(), except that if a required
521** shared-cache lock cannot be obtained, this function may block waiting for
522** the lock to become available. In this scenario the normal API prepare_v2()
523** function always returns SQLITE_LOCKED.
524**
525** If this function returns SQLITE_LOCKED, the caller should rollback
526** the current transaction (if any) and try again later. Otherwise, the
527** system may become deadlocked.
528*/
529int sqlite3_blocking_prepare_v2(
530 sqlite3 *db, /* Database handle. */
531 const char *zSql, /* UTF-8 encoded SQL statement. */
532 int nSql, /* Length of zSql in bytes. */
533 sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
534 const char **pz /* OUT: End of parsed string */
535){
536 int rc;
537 while( SQLITE_LOCKED==(rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, pz)) ){
538 rc = wait_for_unlock_notify(db);
539 if( rc!=SQLITE_OK ) break;
540 }
danielk1977404ca072009-03-16 13:19:36 +0000541 return rc;
542}
543/* END_SQLITE_BLOCKING_STEP */
544
545/*
546** Usage: sqlite3_blocking_step STMT
547**
548** Advance the statement to the next row.
549*/
mistachkin7617e4a2016-07-28 17:11:20 +0000550static int SQLITE_TCLAPI blocking_step_proc(
danielk1977404ca072009-03-16 13:19:36 +0000551 void * clientData,
552 Tcl_Interp *interp,
553 int objc,
554 Tcl_Obj *CONST objv[]
555){
danielk1977404ca072009-03-16 13:19:36 +0000556
557 sqlite3_stmt *pStmt;
558 int rc;
559
560 if( objc!=2 ){
561 Tcl_WrongNumArgs(interp, 1, objv, "STMT");
562 return TCL_ERROR;
563 }
564
565 pStmt = (sqlite3_stmt*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
566 rc = sqlite3_blocking_step(pStmt);
567
mistachkine84d8d32013-04-29 03:09:10 +0000568 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), 0);
danielk1977404ca072009-03-16 13:19:36 +0000569 return TCL_OK;
570}
571
danielk197765a2ea12009-03-19 07:58:31 +0000572/*
573** Usage: sqlite3_blocking_prepare_v2 DB sql bytes ?tailvar?
danielk1977a8bbef82009-03-23 17:11:26 +0000574** Usage: sqlite3_nonblocking_prepare_v2 DB sql bytes ?tailvar?
danielk197765a2ea12009-03-19 07:58:31 +0000575*/
mistachkin7617e4a2016-07-28 17:11:20 +0000576static int SQLITE_TCLAPI blocking_prepare_v2_proc(
danielk197765a2ea12009-03-19 07:58:31 +0000577 void * clientData,
578 Tcl_Interp *interp,
579 int objc,
580 Tcl_Obj *CONST objv[]
581){
582 sqlite3 *db;
583 const char *zSql;
584 int bytes;
585 const char *zTail = 0;
586 sqlite3_stmt *pStmt = 0;
587 char zBuf[50];
588 int rc;
danielk1977a8bbef82009-03-23 17:11:26 +0000589 int isBlocking = !(clientData==0);
danielk197765a2ea12009-03-19 07:58:31 +0000590
591 if( objc!=5 && objc!=4 ){
592 Tcl_AppendResult(interp, "wrong # args: should be \"",
593 Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
594 return TCL_ERROR;
595 }
596 if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
597 zSql = Tcl_GetString(objv[2]);
598 if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
599
danielk1977a8bbef82009-03-23 17:11:26 +0000600 if( isBlocking ){
601 rc = sqlite3_blocking_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
602 }else{
603 rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
604 }
605
danielk197765a2ea12009-03-19 07:58:31 +0000606 assert(rc==SQLITE_OK || pStmt==0);
607 if( zTail && objc>=5 ){
608 if( bytes>=0 ){
609 bytes = bytes - (zTail-zSql);
610 }
611 Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
612 }
613 if( rc!=SQLITE_OK ){
614 assert( pStmt==0 );
drh65545b52015-01-19 00:35:53 +0000615 sqlite3_snprintf(sizeof(zBuf), zBuf, "%s ", (char *)sqlite3ErrName(rc));
danielk197765a2ea12009-03-19 07:58:31 +0000616 Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
617 return TCL_ERROR;
618 }
619
620 if( pStmt ){
621 if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
622 Tcl_AppendResult(interp, zBuf, 0);
623 }
624 return TCL_OK;
625}
626
drh69910da2009-03-27 12:32:54 +0000627#endif /* SQLITE_OS_UNIX && SQLITE_ENABLE_UNLOCK_NOTIFY */
danielk1977404ca072009-03-16 13:19:36 +0000628/*
629** End of implementation of [sqlite3_blocking_step].
630************************************************************************/
631
danielk197781fa1932008-08-28 13:55:10 +0000632/*
danielk197744918fa2007-09-07 11:29:25 +0000633** Register commands with the TCL interpreter.
634*/
635int SqlitetestThread_Init(Tcl_Interp *interp){
636 Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, 0, 0);
danielk197781fa1932008-08-28 13:55:10 +0000637 Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
shaneh3a2d29f2011-04-04 21:48:01 +0000638#if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
danielk1977404ca072009-03-16 13:19:36 +0000639 Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
danielk1977a8bbef82009-03-23 17:11:26 +0000640 Tcl_CreateObjCommand(interp,
641 "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
642 Tcl_CreateObjCommand(interp,
643 "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
danielk1977404ca072009-03-16 13:19:36 +0000644#endif
danielk197744918fa2007-09-07 11:29:25 +0000645 return TCL_OK;
646}
647#else
648int SqlitetestThread_Init(Tcl_Interp *interp){
649 return TCL_OK;
650}
651#endif