/ Check-in [f68e05cb]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add tests and fix bugs in the new cross-thread lock resolution code. When an unlock fails, do not leak file descriptors (ticket #1611). But we really ought to report SQLITE_MISUSE or some other error instead of just returning SQLITE_OK. (CVS 2945)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: f68e05cb2be65fad43fac823b2a9c53b6d2e797d
User & Date: drh 2006-01-15 02:30:58
Context
2006-01-15
02:43
Closing a file from the wrong thread is harmless on most systems. (See ticket #1611) But on systems like RedHat9 with broken fcntl() locks, it leaks file descriptors. That is better than the alternative of prematurely breaking locks and causing database corruption. Nevertheless, it would be good if we could figure out a way to report errors when closing a file from the wrong thread. (CVS 2946) check-in: ad8f12ca user: drh tags: trunk
02:30
Add tests and fix bugs in the new cross-thread lock resolution code. When an unlock fails, do not leak file descriptors (ticket #1611). But we really ought to report SQLITE_MISUSE or some other error instead of just returning SQLITE_OK. (CVS 2945) check-in: f68e05cb user: drh tags: trunk
00:13
Documentation updates. Fix to date.c. But most importantly: database connections are now allowed to change threads as long as they are not holding a lock. (CVS 2944) check-in: 03c422ec user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/os_unix.c.

   482    482   ** Release a openCnt structure previously allocated by findLockInfo().
   483    483   */
   484    484   static void releaseOpenCnt(struct openCnt *pOpen){
   485    485     assert( sqlite3OsInMutex() );
   486    486     pOpen->nRef--;
   487    487     if( pOpen->nRef==0 ){
   488    488       sqlite3HashInsert(&openHash, &pOpen->key, sizeof(pOpen->key), 0);
   489         -    sqliteFree(pOpen->aPending);
          489  +    free(pOpen->aPending);
   490    490       sqliteFree(pOpen);
   491    491     }
   492    492   }
   493    493   
   494    494   /*
   495    495   ** Given a file descriptor, locate lockInfo and openCnt structures that
   496    496   ** describes that file descriptor.  Create new ones if necessary.  The
................................................................................
   577    577       *ppOpen = pOpen;
   578    578     }
   579    579   
   580    580   exit_findlockinfo:
   581    581     return rc;
   582    582   }
   583    583   
          584  +#ifdef SQLITE_DEBUG
          585  +/*
          586  +** Helper function for printing out trace information from debugging
          587  +** binaries. This returns the string represetation of the supplied
          588  +** integer lock-type.
          589  +*/
          590  +static const char *locktypeName(int locktype){
          591  +  switch( locktype ){
          592  +  case NO_LOCK: return "NONE";
          593  +  case SHARED_LOCK: return "SHARED";
          594  +  case RESERVED_LOCK: return "RESERVED";
          595  +  case PENDING_LOCK: return "PENDING";
          596  +  case EXCLUSIVE_LOCK: return "EXCLUSIVE";
          597  +  }
          598  +  return "ERROR";
          599  +}
          600  +#endif
          601  +
   584    602   /*
   585    603   ** If we are currently in a different thread than the thread that the
   586    604   ** unixFile argument belongs to, then transfer ownership of the unixFile
   587    605   ** over to the current thread.
   588    606   **
   589    607   ** A unixFile is only owned by a thread on systems where one thread is
   590    608   ** unable to override locks created by a different thread.  RedHat9 is
................................................................................
   592    610   **
   593    611   ** Ownership transfer is only allowed if the unixFile is currently unlocked.
   594    612   ** If the unixFile is locked and an ownership is wrong, then return
   595    613   ** SQLITE_MISUSE.  Otherwise return SQLITE_OK.
   596    614   */
   597    615   #ifdef SQLITE_UNIX_THREADS
   598    616   static int transferOwnership(unixFile *pFile){
          617  +  int rc;
   599    618     pthread_t hSelf;
   600    619     if( threadsOverrideEachOthersLocks ){
   601    620       /* Ownership transfers not needed on this system */
   602    621       return SQLITE_OK;
   603    622     }
   604    623     hSelf = pthread_self();
   605    624     if( pthread_equal(pFile->tid, hSelf) ){
   606    625       /* We are still in the same thread */
          626  +    TRACE1("No-transfer, same thread\n");
   607    627       return SQLITE_OK;
   608    628     }
   609    629     if( pFile->locktype!=NO_LOCK ){
   610    630       /* We cannot change ownership while we are holding a lock! */
   611    631       return SQLITE_MISUSE;
   612    632     }
          633  +  TRACE4("Transfer ownership of %d from %d to %d\n", pFile->h,pFile->tid,hSelf);
   613    634     pFile->tid = hSelf;
   614    635     releaseLockInfo(pFile->pLock);
   615         -  return findLockInfo(pFile->h, &pFile->pLock, 0);
          636  +  rc = findLockInfo(pFile->h, &pFile->pLock, 0);
          637  +  TRACE5("LOCK    %d is now %s(%s,%d)\n", pFile->h,
          638  +     locktypeName(pFile->locktype),
          639  +     locktypeName(pFile->pLock->locktype), pFile->pLock->cnt);
          640  +  return rc;
   616    641   }
   617    642   #else
   618    643   # define transferOwnership(X) SQLITE_OK
   619    644   #endif
   620    645   
   621    646   /*
   622    647   ** Delete the named file
................................................................................
  1117   1142     
  1118   1143     sqlite3OsLeaveMutex();
  1119   1144     TRACE3("TEST WR-LOCK %d %d\n", pFile->h, r);
  1120   1145   
  1121   1146     return r;
  1122   1147   }
  1123   1148   
  1124         -#ifdef SQLITE_DEBUG
  1125         -/*
  1126         -** Helper function for printing out trace information from debugging
  1127         -** binaries. This returns the string represetation of the supplied
  1128         -** integer lock-type.
  1129         -*/
  1130         -static const char *locktypeName(int locktype){
  1131         -  switch( locktype ){
  1132         -  case NO_LOCK: return "NONE";
  1133         -  case SHARED_LOCK: return "SHARED";
  1134         -  case RESERVED_LOCK: return "RESERVED";
  1135         -  case PENDING_LOCK: return "PENDING";
  1136         -  case EXCLUSIVE_LOCK: return "EXCLUSIVE";
  1137         -  }
  1138         -  return "ERROR";
  1139         -}
  1140         -#endif
  1141         -
  1142   1149   /*
  1143   1150   ** Lock the file with the lock specified by parameter locktype - one
  1144   1151   ** of the following:
  1145   1152   **
  1146   1153   **     (1) SHARED_LOCK
  1147   1154   **     (2) RESERVED_LOCK
  1148   1155   **     (3) PENDING_LOCK
................................................................................
  1236   1243     /* Make sure the current thread owns the pFile.
  1237   1244     */
  1238   1245     rc = transferOwnership(pFile);
  1239   1246     if( rc!=SQLITE_OK ){
  1240   1247       sqlite3OsLeaveMutex();
  1241   1248       return rc;
  1242   1249     }
         1250  +  pLock = pFile->pLock;
  1243   1251   
  1244   1252     /* If some thread using this PID has a lock via a different OsFile*
  1245   1253     ** handle that precludes the requested lock, return BUSY.
  1246   1254     */
  1247   1255     if( (pFile->locktype!=pLock->locktype && 
  1248   1256             (pLock->locktype>=PENDING_LOCK || locktype>SHARED_LOCK))
  1249   1257     ){
................................................................................
  1435   1443       pOpen->nLock--;
  1436   1444       assert( pOpen->nLock>=0 );
  1437   1445       if( pOpen->nLock==0 && pOpen->nPending>0 ){
  1438   1446         int i;
  1439   1447         for(i=0; i<pOpen->nPending; i++){
  1440   1448           close(pOpen->aPending[i]);
  1441   1449         }
  1442         -      sqliteFree(pOpen->aPending);
         1450  +      free(pOpen->aPending);
  1443   1451         pOpen->nPending = 0;
  1444   1452         pOpen->aPending = 0;
  1445   1453       }
  1446   1454     }
  1447   1455     sqlite3OsLeaveMutex();
  1448   1456     pFile->locktype = locktype;
  1449   1457     return rc;
................................................................................
  1454   1462   */
  1455   1463   static int unixClose(OsFile **pId){
  1456   1464     unixFile *id = (unixFile*)*pId;
  1457   1465     int rc;
  1458   1466   
  1459   1467     if( !id ) return SQLITE_OK;
  1460   1468     rc = unixUnlock(*pId, NO_LOCK);
  1461         -  if( rc ) return rc;
  1462   1469     if( id->dirfd>=0 ) close(id->dirfd);
  1463   1470     id->dirfd = -1;
  1464   1471     sqlite3OsEnterMutex();
  1465   1472   
  1466         -  if( id->pOpen->nLock ){
         1473  +  if( id->pOpen->nLock && rc==SQLITE_OK ){
  1467   1474       /* If there are outstanding locks, do not actually close the file just
  1468   1475       ** yet because that would clear those locks.  Instead, add the file
  1469   1476       ** descriptor to pOpen->aPending.  It will be automatically closed when
  1470   1477       ** the last lock is cleared.
  1471   1478       */
  1472   1479       int *aNew;
  1473   1480       struct openCnt *pOpen = id->pOpen;
  1474         -    aNew = sqliteRealloc( pOpen->aPending, (pOpen->nPending+1)*sizeof(int) );
         1481  +    aNew = realloc( pOpen->aPending, (pOpen->nPending+1)*sizeof(int) );
  1475   1482       if( aNew==0 ){
  1476   1483         /* If a malloc fails, just leak the file descriptor */
  1477   1484       }else{
  1478   1485         pOpen->aPending = aNew;
  1479   1486         pOpen->aPending[pOpen->nPending] = id->h;
  1480   1487         pOpen->nPending++;
  1481   1488       }

Changes to src/test1.c.

     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** Code for testing the printf() interface to SQLite.  This code
    13     13   ** is not included in the SQLite library.  It is used for automated
    14     14   ** testing of the SQLite library.
    15     15   **
    16         -** $Id: test1.c,v 1.190 2006/01/15 00:13:16 drh Exp $
           16  +** $Id: test1.c,v 1.191 2006/01/15 02:30:58 drh Exp $
    17     17   */
    18     18   #include "sqliteInt.h"
    19     19   #include "tcl.h"
    20     20   #include "os.h"
    21     21   #include <stdlib.h>
    22     22   #include <string.h>
    23     23   
................................................................................
   162    162   ** get your pointer back.  You have to prepend a "0x" before it will
   163    163   ** work.  Or at least that is what is reported to me (drh).  But this
   164    164   ** behavior varies from machine to machine.  The solution used her is
   165    165   ** to test the string right after it is generated to see if it can be
   166    166   ** understood by scanf, and if not, try prepending an "0x" to see if
   167    167   ** that helps.  If nothing works, a fatal error is generated.
   168    168   */
   169         -static int makePointerStr(Tcl_Interp *interp, char *zPtr, void *p){
          169  +int sqlite3TestMakePointerStr(Tcl_Interp *interp, char *zPtr, void *p){
   170    170     sqlite3_snprintf(100, zPtr, "%p", p);
   171    171     return TCL_OK;
   172    172   }
   173    173   
   174    174   /*
   175    175   ** The callback routine for sqlite3_exec_printf().
   176    176   */
................................................................................
  2092   2092       assert( pStmt==0 );
  2093   2093       sprintf(zBuf, "(%d) ", rc);
  2094   2094       Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
  2095   2095       return TCL_ERROR;
  2096   2096     }
  2097   2097   
  2098   2098     if( pStmt ){
  2099         -    if( makePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
         2099  +    if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
  2100   2100       Tcl_AppendResult(interp, zBuf, 0);
  2101   2101     }
  2102   2102     return TCL_OK;
  2103   2103   }
  2104   2104   
  2105   2105   /*
  2106   2106   ** Usage: sqlite3_prepare DB sql bytes tailvar
................................................................................
  2149   2149     }
  2150   2150     pTail = Tcl_NewByteArrayObj((u8 *)zTail, objlen);
  2151   2151     Tcl_IncrRefCount(pTail);
  2152   2152     Tcl_ObjSetVar2(interp, objv[4], 0, pTail, 0);
  2153   2153     Tcl_DecrRefCount(pTail);
  2154   2154   
  2155   2155     if( pStmt ){
  2156         -    if( makePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
         2156  +    if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
  2157   2157     }
  2158   2158     Tcl_AppendResult(interp, zBuf, 0);
  2159   2159   #endif /* SQLITE_OMIT_UTF16 */
  2160   2160     return TCL_OK;
  2161   2161   }
  2162   2162   
  2163   2163   /*
................................................................................
  2179   2179          Tcl_GetString(objv[0]), " filename options-list", 0);
  2180   2180       return TCL_ERROR;
  2181   2181     }
  2182   2182   
  2183   2183     zFilename = Tcl_GetString(objv[1]);
  2184   2184     rc = sqlite3_open(zFilename, &db);
  2185   2185     
  2186         -  if( makePointerStr(interp, zBuf, db) ) return TCL_ERROR;
         2186  +  if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
  2187   2187     Tcl_AppendResult(interp, zBuf, 0);
  2188   2188     return TCL_OK;
  2189   2189   }
  2190   2190   
  2191   2191   /*
  2192   2192   ** Usage: sqlite3_open16 filename options
  2193   2193   */
................................................................................
  2208   2208          Tcl_GetString(objv[0]), " filename options-list", 0);
  2209   2209       return TCL_ERROR;
  2210   2210     }
  2211   2211   
  2212   2212     zFilename = Tcl_GetByteArrayFromObj(objv[1], 0);
  2213   2213     rc = sqlite3_open16(zFilename, &db);
  2214   2214     
  2215         -  if( makePointerStr(interp, zBuf, db) ) return TCL_ERROR;
         2215  +  if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
  2216   2216     Tcl_AppendResult(interp, zBuf, 0);
  2217   2217   #endif /* SQLITE_OMIT_UTF16 */
  2218   2218     return TCL_OK;
  2219   2219   }
  2220   2220   
  2221   2221   /*
  2222   2222   ** Usage: sqlite3_complete16 <UTF-16 string>
................................................................................
  2602   2602     }
  2603   2603   
  2604   2604     rc = sqlite3OsOpenReadWrite(Tcl_GetString(objv[1]), &pFile, &dummy);
  2605   2605     if( rc!=SQLITE_OK ){
  2606   2606       Tcl_SetResult(interp, (char *)errorName(rc), TCL_STATIC);
  2607   2607       return TCL_ERROR;
  2608   2608     }
  2609         -  makePointerStr(interp, zBuf, pFile);
         2609  +  sqlite3TestMakePointerStr(interp, zBuf, pFile);
  2610   2610     Tcl_SetResult(interp, zBuf, 0);
  2611   2611     return TCL_ERROR;
  2612   2612   }
  2613   2613   
  2614   2614   /*
  2615   2615   ** Usage:  sqlite3OsClose <file handle>
  2616   2616   */

Changes to src/test4.c.

     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** Code for testing the the SQLite library in a multithreaded environment.
    13     13   **
    14         -** $Id: test4.c,v 1.14 2006/01/11 23:40:34 drh Exp $
           14  +** $Id: test4.c,v 1.15 2006/01/15 02:30:58 drh Exp $
    15     15   */
    16     16   #include "sqliteInt.h"
    17     17   #include "tcl.h"
    18     18   #include "os.h"
    19     19   #if defined(OS_UNIX) && OS_UNIX==1 && defined(THREADSAFE) && THREADSAFE==1
    20     20   #include <stdlib.h>
    21     21   #include <string.h>
................................................................................
   610    610     }
   611    611     thread_wait(&threadset[j]);
   612    612     temp = threadset[i].db;
   613    613     threadset[i].db = threadset[j].db;
   614    614     threadset[j].db = temp;
   615    615     return TCL_OK;
   616    616   }
          617  +
          618  +/*
          619  +** Usage: thread_db_get ID
          620  +**
          621  +** Return the database connection pointer for the given thread.  Then
          622  +** remove the pointer from the thread itself.  Afterwards, the thread
          623  +** can be stopped and the connection can be used by the main thread.
          624  +*/
          625  +static int tcl_thread_db_get(
          626  +  void *NotUsed,
          627  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
          628  +  int argc,              /* Number of arguments */
          629  +  const char **argv      /* Text of each argument */
          630  +){
          631  +  int i;
          632  +  char zBuf[100];
          633  +  extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
          634  +  if( argc!=2 ){
          635  +    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
          636  +       " ID", 0);
          637  +    return TCL_ERROR;
          638  +  }
          639  +  i = parse_thread_id(interp, argv[1]);
          640  +  if( i<0 ) return TCL_ERROR;
          641  +  if( !threadset[i].busy ){
          642  +    Tcl_AppendResult(interp, "no such thread", 0);
          643  +    return TCL_ERROR;
          644  +  }
          645  +  thread_wait(&threadset[i]);
          646  +  sqlite3TestMakePointerStr(interp, zBuf, threadset[i].db);
          647  +  threadset[i].db = 0;
          648  +  Tcl_SetResult(interp, zBuf, 0);
          649  +  return TCL_OK;
          650  +}
          651  +
          652  +/*
          653  +** Usage: thread_stmt_get ID
          654  +**
          655  +** Return the database stmt pointer for the given thread.  Then
          656  +** remove the pointer from the thread itself. 
          657  +*/
          658  +static int tcl_thread_stmt_get(
          659  +  void *NotUsed,
          660  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
          661  +  int argc,              /* Number of arguments */
          662  +  const char **argv      /* Text of each argument */
          663  +){
          664  +  int i;
          665  +  char zBuf[100];
          666  +  extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
          667  +  if( argc!=2 ){
          668  +    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
          669  +       " ID", 0);
          670  +    return TCL_ERROR;
          671  +  }
          672  +  i = parse_thread_id(interp, argv[1]);
          673  +  if( i<0 ) return TCL_ERROR;
          674  +  if( !threadset[i].busy ){
          675  +    Tcl_AppendResult(interp, "no such thread", 0);
          676  +    return TCL_ERROR;
          677  +  }
          678  +  thread_wait(&threadset[i]);
          679  +  sqlite3TestMakePointerStr(interp, zBuf, threadset[i].pStmt);
          680  +  threadset[i].pStmt = 0;
          681  +  Tcl_SetResult(interp, zBuf, 0);
          682  +  return TCL_OK;
          683  +}
   617    684   
   618    685   /*
   619    686   ** Register commands with the TCL interpreter.
   620    687   */
   621    688   int Sqlitetest4_Init(Tcl_Interp *interp){
   622    689     static struct {
   623    690        char *zName;
................................................................................
   631    698        { "thread_colname",    (Tcl_CmdProc*)tcl_thread_colname    },
   632    699        { "thread_result",     (Tcl_CmdProc*)tcl_thread_result     },
   633    700        { "thread_error",      (Tcl_CmdProc*)tcl_thread_error      },
   634    701        { "thread_compile",    (Tcl_CmdProc*)tcl_thread_compile    },
   635    702        { "thread_step",       (Tcl_CmdProc*)tcl_thread_step       },
   636    703        { "thread_finalize",   (Tcl_CmdProc*)tcl_thread_finalize   },
   637    704        { "thread_swap",       (Tcl_CmdProc*)tcl_thread_swap       },
          705  +     { "thread_db_get",     (Tcl_CmdProc*)tcl_thread_db_get     },
          706  +     { "thread_stmt_get",   (Tcl_CmdProc*)tcl_thread_stmt_get   },
   638    707     };
   639    708     int i;
   640    709   
   641    710     for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
   642    711       Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
   643    712     }
   644    713     return TCL_OK;
   645    714   }
   646    715   #else
   647    716   int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; }
   648    717   #endif /* OS_UNIX */

Added test/thread2.test.

            1  +# 2006 January 14
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# This file implements regression tests for SQLite library.  The
           12  +# focus of this script is multithreading behavior
           13  +#
           14  +# $Id: thread2.test,v 1.1 2006/01/15 02:30:58 drh Exp $
           15  +
           16  +
           17  +set testdir [file dirname $argv0]
           18  +source $testdir/tester.tcl
           19  +
           20  +# Skip this whole file if the thread testing code is not enabled
           21  +#
           22  +if {[llength [info command thread_step]]==0 || [sqlite3 -has-codec]} {
           23  +  finish_test
           24  +  return
           25  +}
           26  +if {![info exists threadsOverrideEachOthersLocks]} {
           27  +  finish_test
           28  +  return
           29  +}
           30  +
           31  +# Create some data to work with
           32  +#
           33  +do_test thread1-1.1 {
           34  +  execsql {
           35  +    CREATE TABLE t1(a,b);
           36  +    INSERT INTO t1 VALUES(1,'abcdefgh');
           37  +    INSERT INTO t1 SELECT a+1, b||b FROM t1;
           38  +    INSERT INTO t1 SELECT a+2, b||b FROM t1;
           39  +    INSERT INTO t1 SELECT a+4, b||b FROM t1;
           40  +    SELECT count(*), max(length(b)) FROM t1;
           41  +  }
           42  +} {8 64}
           43  +
           44  +# Use the thread_swap command to move the database connections between
           45  +# threads, then verify that they still work.
           46  +#
           47  +do_test thread2-1.2 {
           48  +  db close
           49  +  thread_create A test.db
           50  +  thread_create B test.db
           51  +  thread_swap A B
           52  +  thread_compile A {SELECT a FROM t1 LIMIT 1}
           53  +  thread_result A
           54  +} {SQLITE_OK}
           55  +do_test thread2-1.3 {
           56  +  thread_step A
           57  +  thread_result A
           58  +} {SQLITE_ROW}
           59  +do_test thread2-1.4 {
           60  +  thread_argv A 0
           61  +} {1}
           62  +do_test thread2-1.5 {
           63  +  thread_finalize A
           64  +  thread_result A
           65  +} {SQLITE_OK}
           66  +do_test thread2-1.6 {
           67  +  thread_compile B {SELECT a FROM t1 LIMIT 1}
           68  +  thread_result B
           69  +} {SQLITE_OK}
           70  +do_test thread2-1.7 {
           71  +  thread_step B
           72  +  thread_result B
           73  +} {SQLITE_ROW}
           74  +do_test thread2-1.8 {
           75  +  thread_argv B 0
           76  +} {1}
           77  +do_test thread2-1.9 {
           78  +  thread_finalize B
           79  +  thread_result B
           80  +} {SQLITE_OK}
           81  +
           82  +# Swap them again.
           83  +#
           84  +do_test thread2-2.2 {
           85  +  thread_swap A B
           86  +  thread_compile A {SELECT a FROM t1 LIMIT 1}
           87  +  thread_result A
           88  +} {SQLITE_OK}
           89  +do_test thread2-2.3 {
           90  +  thread_step A
           91  +  thread_result A
           92  +} {SQLITE_ROW}
           93  +do_test thread2-2.4 {
           94  +  thread_argv A 0
           95  +} {1}
           96  +do_test thread2-2.5 {
           97  +  thread_finalize A
           98  +  thread_result A
           99  +} {SQLITE_OK}
          100  +do_test thread2-2.6 {
          101  +  thread_compile B {SELECT a FROM t1 LIMIT 1}
          102  +  thread_result B
          103  +} {SQLITE_OK}
          104  +do_test thread2-2.7 {
          105  +  thread_step B
          106  +  thread_result B
          107  +} {SQLITE_ROW}
          108  +do_test thread2-2.8 {
          109  +  thread_argv B 0
          110  +} {1}
          111  +do_test thread2-2.9 {
          112  +  thread_finalize B
          113  +  thread_result B
          114  +} {SQLITE_OK}
          115  +thread_halt A
          116  +thread_halt B
          117  +
          118  +# Save the original (correct) value of threadsOverrideEachOthersLocks
          119  +# so that it can be restored.  If this value is left set incorrectly, lots
          120  +# of things will go wrong in future tests.
          121  +#
          122  +set orig_threadOverride $threadsOverrideEachOthersLocks
          123  +
          124  +# Pretend we are on a system (like RedHat9) were threads do not
          125  +# override each others locks.
          126  +#
          127  +set threadsOverrideEachOthersLocks 0
          128  +
          129  +# Verify that we can move database connections between threads as
          130  +# long as no locks are held.
          131  +#
          132  +do_test thread2-3.1 {
          133  +  thread_create A test.db
          134  +  set DB [thread_db_get A]
          135  +  thread_halt A
          136  +} {}
          137  +do_test thread2-3.2 {
          138  +  set STMT [sqlite3_prepare $DB {SELECT a FROM t1 LIMIT 1} -1 TAIL]
          139  +  sqlite3_step $STMT
          140  +} SQLITE_ROW
          141  +do_test thread2-3.3 {
          142  +  sqlite3_column_int $STMT 0
          143  +} 1
          144  +do_test thread2-3.4 {
          145  +  sqlite3_finalize $STMT
          146  +} SQLITE_OK
          147  +do_test thread2-3.5 {
          148  +  set STMT [sqlite3_prepare $DB {SELECT max(a) FROM t1} -1 TAIL]
          149  +  sqlite3_step $STMT
          150  +} SQLITE_ROW
          151  +do_test thread2-3.6 {
          152  +  sqlite3_column_int $STMT 0
          153  +} 8
          154  +do_test thread2-3.7 {
          155  +  sqlite3_finalize $STMT
          156  +} SQLITE_OK
          157  +do_test thread2-3.8 {
          158  +  sqlite3_close $DB
          159  +} {SQLITE_OK}
          160  +
          161  +do_test thread2-3.10 {
          162  +  thread_create A test.db
          163  +  thread_compile A {SELECT a FROM t1 LIMIT 1}
          164  +  thread_step A
          165  +  thread_finalize A
          166  +  set DB [thread_db_get A]
          167  +  thread_halt A
          168  +} {}
          169  +do_test thread2-3.11 {
          170  +  set STMT [sqlite3_prepare $DB {SELECT a FROM t1 LIMIT 1} -1 TAIL]
          171  +  sqlite3_step $STMT
          172  +} SQLITE_ROW
          173  +do_test thread2-3.12 {
          174  +  sqlite3_column_int $STMT 0
          175  +} 1
          176  +do_test thread2-3.13 {
          177  +  sqlite3_finalize $STMT
          178  +} SQLITE_OK
          179  +do_test thread2-3.14 {
          180  +  sqlite3_close $DB
          181  +} SQLITE_OK
          182  +
          183  +do_test thread2-3.20 {
          184  +  thread_create A test.db
          185  +  thread_compile A {SELECT a FROM t1 LIMIT 3}
          186  +  thread_step A
          187  +  set STMT [thread_stmt_get A]
          188  +  set DB [thread_db_get A]
          189  +  thread_halt A
          190  +} {}
          191  +do_test thread2-3.21 {
          192  +  sqlite3_step $STMT
          193  +} SQLITE_ROW
          194  +do_test thread2-3.22 {
          195  +  sqlite3_column_int $STMT 0
          196  +} 2
          197  +do_test thread2-3.23 {
          198  +  # The unlock fails here.  But because we never check the return
          199  +  # code from sqlite3OsUnlock (because we cannot do anything about it
          200  +  # if it fails) we do not realize that an error has occurred.
          201  +  sqlite3_finalize $STMT
          202  +} SQLITE_OK
          203  +do_test thread2-3.25 {
          204  +  sqlite3_close $DB
          205  +} SQLITE_OK
          206  +
          207  +do_test thread2-3.30 {
          208  +  thread_create A test.db
          209  +  thread_compile A {BEGIN}
          210  +  thread_step A
          211  +  thread_finalize A
          212  +  thread_compile A {SELECT a FROM t1 LIMIT 1}
          213  +  thread_step A
          214  +  thread_finalize A
          215  +  set DB [thread_db_get A]
          216  +  thread_halt A
          217  +} {}
          218  +do_test thread2-3.31 {
          219  +  set STMT [sqlite3_prepare $DB {INSERT INTO t1 VALUES(99,'error')} -1 TAIL]
          220  +  sqlite3_step $STMT
          221  +} SQLITE_ERROR
          222  +do_test thread2-3.32 {
          223  +  sqlite3_finalize $STMT
          224  +} SQLITE_MISUSE
          225  +do_test thread2-3.33 {
          226  +  sqlite3_close $DB
          227  +} SQLITE_OK
          228  +
          229  +# VERY important to set the override flag back to its true value.
          230  +#
          231  +set threadsOverrideEachOthersLocks $orig_threadOverride
          232  +
          233  +# Also important to halt the worker threads, which are using spin
          234  +# locks and eating away CPU cycles.
          235  +#
          236  +thread_halt *   
          237  +finish_test