xref: /sqlite-3.40.0/src/test_thread.c (revision 7aa3ebee)
1 /*
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.
16 */
17 
18 #include "sqliteInt.h"
19 #include <tcl.h>
20 
21 #if SQLITE_THREADSAFE
22 
23 #include <errno.h>
24 
25 #if !defined(_MSC_VER)
26 #include <unistd.h>
27 #endif
28 
29 /*
30 ** One of these is allocated for each thread created by [sqlthread spawn].
31 */
32 typedef struct SqlThread SqlThread;
33 struct SqlThread {
34   Tcl_ThreadId parent;     /* Thread id of parent thread */
35   Tcl_Interp *interp;      /* Parent interpreter */
36   char *zScript;           /* The script to execute. */
37   char *zVarname;          /* Varname in parent script */
38 };
39 
40 /*
41 ** A custom Tcl_Event type used by this module. When the event is
42 ** handled, script zScript is evaluated in interpreter interp. If
43 ** the evaluation throws an exception (returns TCL_ERROR), then the
44 ** error is handled by Tcl_BackgroundError(). If no error occurs,
45 ** the result is simply discarded.
46 */
47 typedef struct EvalEvent EvalEvent;
48 struct EvalEvent {
49   Tcl_Event base;          /* Base class of type Tcl_Event */
50   char *zScript;           /* The script to execute. */
51   Tcl_Interp *interp;      /* The interpreter to execute it in. */
52 };
53 
54 static Tcl_ObjCmdProc sqlthread_proc;
55 static Tcl_ObjCmdProc clock_seconds_proc;
56 #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
57 static Tcl_ObjCmdProc blocking_step_proc;
58 static Tcl_ObjCmdProc blocking_prepare_v2_proc;
59 #endif
60 int Sqlitetest1_Init(Tcl_Interp *);
61 int Sqlite3_Init(Tcl_Interp *);
62 
63 /* Functions from main.c */
64 extern const char *sqlite3ErrName(int);
65 
66 /* Functions from test1.c */
67 extern void *sqlite3TestTextToPtr(const char *);
68 extern int getDbPointer(Tcl_Interp *, const char *, sqlite3 **);
69 extern int sqlite3TestMakePointerStr(Tcl_Interp *, char *, void *);
70 extern int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int);
71 
72 /*
73 ** Handler for events of type EvalEvent.
74 */
75 static int tclScriptEvent(Tcl_Event *evPtr, int flags){
76   int rc;
77   EvalEvent *p = (EvalEvent *)evPtr;
78   rc = Tcl_Eval(p->interp, p->zScript);
79   if( rc!=TCL_OK ){
80     Tcl_BackgroundError(p->interp);
81   }
82   UNUSED_PARAMETER(flags);
83   return 1;
84 }
85 
86 /*
87 ** Register an EvalEvent to evaluate the script pScript in the
88 ** parent interpreter/thread of SqlThread p.
89 */
90 static void postToParent(SqlThread *p, Tcl_Obj *pScript){
91   EvalEvent *pEvent;
92   char *zMsg;
93   int nMsg;
94 
95   zMsg = Tcl_GetStringFromObj(pScript, &nMsg);
96   pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
97   pEvent->base.nextPtr = 0;
98   pEvent->base.proc = tclScriptEvent;
99   pEvent->zScript = (char *)&pEvent[1];
100   memcpy(pEvent->zScript, zMsg, nMsg+1);
101   pEvent->interp = p->interp;
102 
103   Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
104   Tcl_ThreadAlert(p->parent);
105 }
106 
107 /*
108 ** The main function for threads created with [sqlthread spawn].
109 */
110 static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){
111   Tcl_Interp *interp;
112   Tcl_Obj *pRes;
113   Tcl_Obj *pList;
114   int rc;
115   SqlThread *p = (SqlThread *)pSqlThread;
116   extern int Sqlitetest_mutex_Init(Tcl_Interp*);
117 
118   interp = Tcl_CreateInterp();
119   Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
120   Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, pSqlThread, 0);
121 #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
122   Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
123   Tcl_CreateObjCommand(interp,
124       "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
125   Tcl_CreateObjCommand(interp,
126       "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
127 #endif
128   Sqlitetest1_Init(interp);
129   Sqlitetest_mutex_Init(interp);
130   Sqlite3_Init(interp);
131 
132   rc = Tcl_Eval(interp, p->zScript);
133   pRes = Tcl_GetObjResult(interp);
134   pList = Tcl_NewObj();
135   Tcl_IncrRefCount(pList);
136   Tcl_IncrRefCount(pRes);
137 
138   if( rc!=TCL_OK ){
139     Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("error", -1));
140     Tcl_ListObjAppendElement(interp, pList, pRes);
141     postToParent(p, pList);
142     Tcl_DecrRefCount(pList);
143     pList = Tcl_NewObj();
144   }
145 
146   Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("set", -1));
147   Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(p->zVarname, -1));
148   Tcl_ListObjAppendElement(interp, pList, pRes);
149   postToParent(p, pList);
150 
151   ckfree((void *)p);
152   Tcl_DecrRefCount(pList);
153   Tcl_DecrRefCount(pRes);
154   Tcl_DeleteInterp(interp);
155   while( Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT) );
156   Tcl_ExitThread(0);
157   TCL_THREAD_CREATE_RETURN;
158 }
159 
160 /*
161 ** sqlthread spawn VARNAME SCRIPT
162 **
163 **     Spawn a new thread with its own Tcl interpreter and run the
164 **     specified SCRIPT(s) in it. The thread terminates after running
165 **     the script. The result of the script is stored in the variable
166 **     VARNAME.
167 **
168 **     The caller can wait for the script to terminate using [vwait VARNAME].
169 */
170 static int sqlthread_spawn(
171   ClientData clientData,
172   Tcl_Interp *interp,
173   int objc,
174   Tcl_Obj *CONST objv[]
175 ){
176   Tcl_ThreadId x;
177   SqlThread *pNew;
178   int rc;
179 
180   int nVarname; char *zVarname;
181   int nScript; char *zScript;
182 
183   /* Parameters for thread creation */
184   const int nStack = TCL_THREAD_STACK_DEFAULT;
185   const int flags = TCL_THREAD_NOFLAGS;
186 
187   assert(objc==4);
188   UNUSED_PARAMETER(clientData);
189   UNUSED_PARAMETER(objc);
190 
191   zVarname = Tcl_GetStringFromObj(objv[2], &nVarname);
192   zScript = Tcl_GetStringFromObj(objv[3], &nScript);
193 
194   pNew = (SqlThread *)ckalloc(sizeof(SqlThread)+nVarname+nScript+2);
195   pNew->zVarname = (char *)&pNew[1];
196   pNew->zScript = (char *)&pNew->zVarname[nVarname+1];
197   memcpy(pNew->zVarname, zVarname, nVarname+1);
198   memcpy(pNew->zScript, zScript, nScript+1);
199   pNew->parent = Tcl_GetCurrentThread();
200   pNew->interp = interp;
201 
202   rc = Tcl_CreateThread(&x, tclScriptThread, (void *)pNew, nStack, flags);
203   if( rc!=TCL_OK ){
204     Tcl_AppendResult(interp, "Error in Tcl_CreateThread()", 0);
205     ckfree((char *)pNew);
206     return TCL_ERROR;
207   }
208 
209   return TCL_OK;
210 }
211 
212 /*
213 ** sqlthread parent SCRIPT
214 **
215 **     This can be called by spawned threads only. It sends the specified
216 **     script back to the parent thread for execution. The result of
217 **     evaluating the SCRIPT is returned. The parent thread must enter
218 **     the event loop for this to work - otherwise the caller will
219 **     block indefinitely.
220 **
221 **     NOTE: At the moment, this doesn't work. FIXME.
222 */
223 static int sqlthread_parent(
224   ClientData clientData,
225   Tcl_Interp *interp,
226   int objc,
227   Tcl_Obj *CONST objv[]
228 ){
229   EvalEvent *pEvent;
230   char *zMsg;
231   int nMsg;
232   SqlThread *p = (SqlThread *)clientData;
233 
234   assert(objc==3);
235   UNUSED_PARAMETER(objc);
236 
237   if( p==0 ){
238     Tcl_AppendResult(interp, "no parent thread", 0);
239     return TCL_ERROR;
240   }
241 
242   zMsg = Tcl_GetStringFromObj(objv[2], &nMsg);
243   pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
244   pEvent->base.nextPtr = 0;
245   pEvent->base.proc = tclScriptEvent;
246   pEvent->zScript = (char *)&pEvent[1];
247   memcpy(pEvent->zScript, zMsg, nMsg+1);
248   pEvent->interp = p->interp;
249   Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
250   Tcl_ThreadAlert(p->parent);
251 
252   return TCL_OK;
253 }
254 
255 static int xBusy(void *pArg, int nBusy){
256   UNUSED_PARAMETER(pArg);
257   UNUSED_PARAMETER(nBusy);
258   sqlite3_sleep(50);
259   return 1;             /* Try again... */
260 }
261 
262 /*
263 ** sqlthread open
264 **
265 **     Open a database handle and return the string representation of
266 **     the pointer value.
267 */
268 static int sqlthread_open(
269   ClientData clientData,
270   Tcl_Interp *interp,
271   int objc,
272   Tcl_Obj *CONST objv[]
273 ){
274   int sqlite3TestMakePointerStr(Tcl_Interp *interp, char *zPtr, void *p);
275 
276   const char *zFilename;
277   sqlite3 *db;
278   char zBuf[100];
279   extern void Md5_Register(sqlite3*);
280 
281   UNUSED_PARAMETER(clientData);
282   UNUSED_PARAMETER(objc);
283 
284   zFilename = Tcl_GetString(objv[2]);
285   sqlite3_open(zFilename, &db);
286 #ifdef SQLITE_HAS_CODEC
287   if( db && objc>=4 ){
288     const char *zKey;
289     int nKey;
290     int rc;
291     zKey = Tcl_GetStringFromObj(objv[3], &nKey);
292     rc = sqlite3_key(db, zKey, nKey);
293     if( rc!=SQLITE_OK ){
294       char *zErrMsg = sqlite3_mprintf("error %d: %s", rc, sqlite3_errmsg(db));
295       sqlite3_close(db);
296       Tcl_AppendResult(interp, zErrMsg, (char*)0);
297       sqlite3_free(zErrMsg);
298       return TCL_ERROR;
299     }
300   }
301 #endif
302   Md5_Register(db);
303   sqlite3_busy_handler(db, xBusy, 0);
304 
305   if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
306   Tcl_AppendResult(interp, zBuf, 0);
307 
308   return TCL_OK;
309 }
310 
311 
312 /*
313 ** sqlthread open
314 **
315 **     Return the current thread-id (Tcl_GetCurrentThread()) cast to
316 **     an integer.
317 */
318 static int sqlthread_id(
319   ClientData clientData,
320   Tcl_Interp *interp,
321   int objc,
322   Tcl_Obj *CONST objv[]
323 ){
324   Tcl_ThreadId id = Tcl_GetCurrentThread();
325   Tcl_SetObjResult(interp, Tcl_NewIntObj(SQLITE_PTR_TO_INT(id)));
326   UNUSED_PARAMETER(clientData);
327   UNUSED_PARAMETER(objc);
328   UNUSED_PARAMETER(objv);
329   return TCL_OK;
330 }
331 
332 
333 /*
334 ** Dispatch routine for the sub-commands of [sqlthread].
335 */
336 static int sqlthread_proc(
337   ClientData clientData,
338   Tcl_Interp *interp,
339   int objc,
340   Tcl_Obj *CONST objv[]
341 ){
342   struct SubCommand {
343     char *zName;
344     Tcl_ObjCmdProc *xProc;
345     int nArg;
346     char *zUsage;
347   } aSub[] = {
348     {"parent", sqlthread_parent, 1, "SCRIPT"},
349     {"spawn",  sqlthread_spawn,  2, "VARNAME SCRIPT"},
350     {"open",   sqlthread_open,   1, "DBNAME"},
351     {"id",     sqlthread_id,     0, ""},
352     {0, 0, 0}
353   };
354   struct SubCommand *pSub;
355   int rc;
356   int iIndex;
357 
358   if( objc<2 ){
359     Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND");
360     return TCL_ERROR;
361   }
362 
363   rc = Tcl_GetIndexFromObjStruct(
364       interp, objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iIndex
365   );
366   if( rc!=TCL_OK ) return rc;
367   pSub = &aSub[iIndex];
368 
369   if( objc<(pSub->nArg+2) ){
370     Tcl_WrongNumArgs(interp, 2, objv, pSub->zUsage);
371     return TCL_ERROR;
372   }
373 
374   return pSub->xProc(clientData, interp, objc, objv);
375 }
376 
377 /*
378 ** The [clock_seconds] command. This is more or less the same as the
379 ** regular tcl [clock seconds], except that it is available in testfixture
380 ** when linked against both Tcl 8.4 and 8.5. Because [clock seconds] is
381 ** implemented as a script in Tcl 8.5, it is not usually available to
382 ** testfixture.
383 */
384 static int clock_seconds_proc(
385   ClientData clientData,
386   Tcl_Interp *interp,
387   int objc,
388   Tcl_Obj *CONST objv[]
389 ){
390   Tcl_Time now;
391   Tcl_GetTime(&now);
392   Tcl_SetObjResult(interp, Tcl_NewIntObj(now.sec));
393   UNUSED_PARAMETER(clientData);
394   UNUSED_PARAMETER(objc);
395   UNUSED_PARAMETER(objv);
396   return TCL_OK;
397 }
398 
399 /*************************************************************************
400 ** This block contains the implementation of the [sqlite3_blocking_step]
401 ** command available to threads created by [sqlthread spawn] commands. It
402 ** is only available on UNIX for now. This is because pthread condition
403 ** variables are used.
404 **
405 ** The source code for the C functions sqlite3_blocking_step(),
406 ** blocking_step_notify() and the structure UnlockNotification is
407 ** automatically extracted from this file and used as part of the
408 ** documentation for the sqlite3_unlock_notify() API function. This
409 ** should be considered if these functions are to be extended (i.e. to
410 ** support windows) in the future.
411 */
412 #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
413 
414 /* BEGIN_SQLITE_BLOCKING_STEP */
415 /* This example uses the pthreads API */
416 #include <pthread.h>
417 
418 /*
419 ** A pointer to an instance of this structure is passed as the user-context
420 ** pointer when registering for an unlock-notify callback.
421 */
422 typedef struct UnlockNotification UnlockNotification;
423 struct UnlockNotification {
424   int fired;                         /* True after unlock event has occurred */
425   pthread_cond_t cond;               /* Condition variable to wait on */
426   pthread_mutex_t mutex;             /* Mutex to protect structure */
427 };
428 
429 /*
430 ** This function is an unlock-notify callback registered with SQLite.
431 */
432 static void unlock_notify_cb(void **apArg, int nArg){
433   int i;
434   for(i=0; i<nArg; i++){
435     UnlockNotification *p = (UnlockNotification *)apArg[i];
436     pthread_mutex_lock(&p->mutex);
437     p->fired = 1;
438     pthread_cond_signal(&p->cond);
439     pthread_mutex_unlock(&p->mutex);
440   }
441 }
442 
443 /*
444 ** This function assumes that an SQLite API call (either sqlite3_prepare_v2()
445 ** or sqlite3_step()) has just returned SQLITE_LOCKED. The argument is the
446 ** associated database connection.
447 **
448 ** This function calls sqlite3_unlock_notify() to register for an
449 ** unlock-notify callback, then blocks until that callback is delivered
450 ** and returns SQLITE_OK. The caller should then retry the failed operation.
451 **
452 ** Or, if sqlite3_unlock_notify() indicates that to block would deadlock
453 ** the system, then this function returns SQLITE_LOCKED immediately. In
454 ** this case the caller should not retry the operation and should roll
455 ** back the current transaction (if any).
456 */
457 static int wait_for_unlock_notify(sqlite3 *db){
458   int rc;
459   UnlockNotification un;
460 
461   /* Initialize the UnlockNotification structure. */
462   un.fired = 0;
463   pthread_mutex_init(&un.mutex, 0);
464   pthread_cond_init(&un.cond, 0);
465 
466   /* Register for an unlock-notify callback. */
467   rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un);
468   assert( rc==SQLITE_LOCKED || rc==SQLITE_OK );
469 
470   /* The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED
471   ** or SQLITE_OK.
472   **
473   ** If SQLITE_LOCKED was returned, then the system is deadlocked. In this
474   ** case this function needs to return SQLITE_LOCKED to the caller so
475   ** that the current transaction can be rolled back. Otherwise, block
476   ** until the unlock-notify callback is invoked, then return SQLITE_OK.
477   */
478   if( rc==SQLITE_OK ){
479     pthread_mutex_lock(&un.mutex);
480     if( !un.fired ){
481       pthread_cond_wait(&un.cond, &un.mutex);
482     }
483     pthread_mutex_unlock(&un.mutex);
484   }
485 
486   /* Destroy the mutex and condition variables. */
487   pthread_cond_destroy(&un.cond);
488   pthread_mutex_destroy(&un.mutex);
489 
490   return rc;
491 }
492 
493 /*
494 ** This function is a wrapper around the SQLite function sqlite3_step().
495 ** It functions in the same way as step(), except that if a required
496 ** shared-cache lock cannot be obtained, this function may block waiting for
497 ** the lock to become available. In this scenario the normal API step()
498 ** function always returns SQLITE_LOCKED.
499 **
500 ** If this function returns SQLITE_LOCKED, the caller should rollback
501 ** the current transaction (if any) and try again later. Otherwise, the
502 ** system may become deadlocked.
503 */
504 int sqlite3_blocking_step(sqlite3_stmt *pStmt){
505   int rc;
506   while( SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){
507     rc = wait_for_unlock_notify(sqlite3_db_handle(pStmt));
508     if( rc!=SQLITE_OK ) break;
509     sqlite3_reset(pStmt);
510   }
511   return rc;
512 }
513 
514 /*
515 ** This function is a wrapper around the SQLite function sqlite3_prepare_v2().
516 ** It functions in the same way as prepare_v2(), except that if a required
517 ** shared-cache lock cannot be obtained, this function may block waiting for
518 ** the lock to become available. In this scenario the normal API prepare_v2()
519 ** function always returns SQLITE_LOCKED.
520 **
521 ** If this function returns SQLITE_LOCKED, the caller should rollback
522 ** the current transaction (if any) and try again later. Otherwise, the
523 ** system may become deadlocked.
524 */
525 int sqlite3_blocking_prepare_v2(
526   sqlite3 *db,              /* Database handle. */
527   const char *zSql,         /* UTF-8 encoded SQL statement. */
528   int nSql,                 /* Length of zSql in bytes. */
529   sqlite3_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
530   const char **pz           /* OUT: End of parsed string */
531 ){
532   int rc;
533   while( SQLITE_LOCKED==(rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, pz)) ){
534     rc = wait_for_unlock_notify(db);
535     if( rc!=SQLITE_OK ) break;
536   }
537   return rc;
538 }
539 /* END_SQLITE_BLOCKING_STEP */
540 
541 /*
542 ** Usage: sqlite3_blocking_step STMT
543 **
544 ** Advance the statement to the next row.
545 */
546 static int blocking_step_proc(
547   void * clientData,
548   Tcl_Interp *interp,
549   int objc,
550   Tcl_Obj *CONST objv[]
551 ){
552 
553   sqlite3_stmt *pStmt;
554   int rc;
555 
556   if( objc!=2 ){
557     Tcl_WrongNumArgs(interp, 1, objv, "STMT");
558     return TCL_ERROR;
559   }
560 
561   pStmt = (sqlite3_stmt*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
562   rc = sqlite3_blocking_step(pStmt);
563 
564   Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), 0);
565   return TCL_OK;
566 }
567 
568 /*
569 ** Usage: sqlite3_blocking_prepare_v2 DB sql bytes ?tailvar?
570 ** Usage: sqlite3_nonblocking_prepare_v2 DB sql bytes ?tailvar?
571 */
572 static int blocking_prepare_v2_proc(
573   void * clientData,
574   Tcl_Interp *interp,
575   int objc,
576   Tcl_Obj *CONST objv[]
577 ){
578   sqlite3 *db;
579   const char *zSql;
580   int bytes;
581   const char *zTail = 0;
582   sqlite3_stmt *pStmt = 0;
583   char zBuf[50];
584   int rc;
585   int isBlocking = !(clientData==0);
586 
587   if( objc!=5 && objc!=4 ){
588     Tcl_AppendResult(interp, "wrong # args: should be \"",
589        Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
590     return TCL_ERROR;
591   }
592   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
593   zSql = Tcl_GetString(objv[2]);
594   if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
595 
596   if( isBlocking ){
597     rc = sqlite3_blocking_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
598   }else{
599     rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
600   }
601 
602   assert(rc==SQLITE_OK || pStmt==0);
603   if( zTail && objc>=5 ){
604     if( bytes>=0 ){
605       bytes = bytes - (zTail-zSql);
606     }
607     Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
608   }
609   if( rc!=SQLITE_OK ){
610     assert( pStmt==0 );
611     sqlite3_snprintf(sizeof(zBuf), zBuf, "%s ", (char *)sqlite3ErrName(rc));
612     Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
613     return TCL_ERROR;
614   }
615 
616   if( pStmt ){
617     if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
618     Tcl_AppendResult(interp, zBuf, 0);
619   }
620   return TCL_OK;
621 }
622 
623 #endif /* SQLITE_OS_UNIX && SQLITE_ENABLE_UNLOCK_NOTIFY */
624 /*
625 ** End of implementation of [sqlite3_blocking_step].
626 ************************************************************************/
627 
628 /*
629 ** Register commands with the TCL interpreter.
630 */
631 int SqlitetestThread_Init(Tcl_Interp *interp){
632   Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, 0, 0);
633   Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
634 #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
635   Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
636   Tcl_CreateObjCommand(interp,
637       "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
638   Tcl_CreateObjCommand(interp,
639       "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
640 #endif
641   return TCL_OK;
642 }
643 #else
644 int SqlitetestThread_Init(Tcl_Interp *interp){
645   return TCL_OK;
646 }
647 #endif
648