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