1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

Add new thread-testing code and fix locking under Linux threads. Ticket #530. (CVS 1137)

FossilOrigin-Name: b36a4bb61094d539273c21a9e4042384f10a7806
This commit is contained in:
drh
2003-12-19 02:52:05 +00:00
parent 9c4dcca89e
commit a6064dcf3b
7 changed files with 722 additions and 29 deletions

View File

@@ -112,6 +112,7 @@ TESTSRC = \
$(TOP)/src/test1.c \
$(TOP)/src/test2.c \
$(TOP)/src/test3.c \
$(TOP)/src/test4.c \
$(TOP)/src/md5.c
# Header files used by all library source files.
@@ -312,7 +313,7 @@ tclsqlite: $(TOP)/src/tclsqlite.c libsqlite.a
testfixture$(EXE): $(TOP)/src/tclsqlite.c libsqlite.a $(TESTSRC)
$(TCCX) $(TCL_FLAGS) -DTCLSH=1 -DSQLITE_TEST=1 -o testfixture$(EXE) \
$(TESTSRC) $(TOP)/src/tclsqlite.c \
libsqlite.a $(LIBTCL)
libsqlite.a $(LIBTCL) $(THREADLIB)
fulltest: testfixture$(EXE) sqlite$(EXE)
./testfixture$(EXE) $(TOP)/test/all.test

View File

@@ -1,5 +1,5 @@
C Typo\son\sthe\s"datatypes.html"\sdocument.\s(CVS\s1136)
D 2003-12-18T14:19:41
C Add\snew\sthread-testing\scode\sand\sfix\slocking\sunder\sLinux\sthreads.\s\sTicket\s#530.\s(CVS\s1137)
D 2003-12-19T02:52:06
F Makefile.in 5cb273b7d0e945d47ee8b9ad1c2a04ce79927d2d
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -16,7 +16,7 @@ F doc/report1.txt a031aaf37b185e4fa540223cb516d3bccec7eeac
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
F libtool bbbea7d79c23323e4100103836028e4fad0d9242
F ltmain.sh abfb9387049fff6996afc6e325736597795baf11
F main.mk 3e200c199e46c2b7c3106fd2c3bfa11cd0aa22c9
F main.mk c97237fc95f38e9041a3fbf5128efa6e23a3de77
F publish.sh 86b5e8535830a2588f62ce1d5d1ef00e1dede23a
F spec.template a38492f1c1dd349fc24cb0565e08afc53045304b
F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
@@ -38,7 +38,7 @@ F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
F src/insert.c 01f66866f35c986eab4a57373ca689a3255ef2df
F src/main.c 3dd3cae00bade294011da5a3cf9ff660a610c545
F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
F src/os.c 226d32db1f36f8932b318b1757c8623be6e4244f
F src/os.c 000b62b95cad6e3518cb7b71ba9376be192b19dc
F src/os.h 729395fefcca4b81ae056aa9ff67b72bb40dd9e0
F src/pager.c ca24fced1ca4c2b8ea519d5fe8ec69a2d846276f
F src/pager.h 5da62c83443f26b1792cfd72c96c422f91aadd31
@@ -52,10 +52,11 @@ F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in e6cfff01fafc8a82ce82cd8c932af421dc9adb54
F src/sqliteInt.h f8549cf426920e43efb105a08484768cdb73c808
F src/table.c d845cb101b5afc1f7fea083c99e3d2fa7998d895
F src/tclsqlite.c 3efac6b5861ac149c41251d4d4c420c94be5ba6a
F src/tclsqlite.c 7425d6980a1d96d6d3af911f935ee699c2390db7
F src/test1.c f9d5816610f7ec4168ab7b098d5207a5708712b6
F src/test2.c 5014337d8576b731cce5b5a14bec4f0daf432700
F src/test3.c 30985ebdfaf3ee1462a9b0652d3efbdc8d9798f5
F src/test4.c 99d0b1d2736b0e61fed996a3e41614f626d7b56b
F src/threadtest.c d641a5219e718e18a1a80a50eb9bb549f451f42e
F src/tokenize.c d10e7f0b4d8634f6f37237b4e65314e3e5a3a34b
F src/trigger.c ce83e017b407d046e909d05373d7f8ee70f9f7f9
@@ -129,6 +130,7 @@ F test/tableapi.test d881e787779a175238b72f55b5e50d3a85ab47a6
F test/tclsqlite.test f141303e0f2e9a616b551813e2b21bd38c5dca50
F test/temptable.test c82bd6f800f10e8cf96921af6315e5f1c21e2692
F test/tester.tcl 2671536d3650c29e7c105219f277568b0884cb58
F test/thread1.test e98de6574910978017a621d0851fda13e257763d
F test/trans.test 75e7a171b5d2d94ee56766459113e2ad0e5f809d
F test/trigger1.test 3fe06daecf8334df840025e154e95fd4727600d7
F test/trigger2.test 0767ab30cb5a2c8402c8524f3d566b410b6f5263
@@ -177,7 +179,7 @@ F www/speed.tcl 2f6b1155b99d39adb185f900456d1d592c4832b3
F www/sqlite.tcl 3c83b08cf9f18aa2d69453ff441a36c40e431604
F www/tclsqlite.tcl b9271d44dcf147a93c98f8ecf28c927307abd6da
F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1
P a0451ccf2d0377536073ecaa2d6b70d05f5a5734
R 9e43f2ff205876924bdbd5d3204b97cb
P 80b1e277123c07b2db7441a9e600dd69ef55a0da
R ea1fab6d3d994f942c20af002987010c
U drh
Z 47f92686e837a5b11a2d636a008c6c95
Z b3907cc74f1a3c0fe5cfc79f0eafa427

View File

@@ -1 +1 @@
80b1e277123c07b2db7441a9e600dd69ef55a0da
b36a4bb61094d539273c21a9e4042384f10a7806

View File

@@ -61,6 +61,24 @@
# define fcntl(A,B,C) 0
#endif
/*
** Macros used to determine whether or not to use threads. The
** SQLITE_UNIX_THREADS macro is defined if we are synchronizing for
** Posix threads and SQLITE_W32_THREADS is defined if we are
** synchronizing using Win32 threads.
*/
#if OS_UNIX && defined(THREADSAFE) && THREADSAFE
# include <pthread.h>
# define SQLITE_UNIX_THREADS 1
#endif
#if OS_WIN && defined(THREADSAFE) && THREADSAFE
# define SQLITE_W32_THREADS 1
#endif
#if OS_MAC && defined(THREADSAFE) && THREADSAFE
# include <Multiprocessing.h>
# define SQLITE_MACOS_MULTITASKING 1
#endif
/*
** Macros for performance tracing. Normally turned off
*/
@@ -155,6 +173,9 @@ static unsigned int elapse;
struct inodeKey {
dev_t dev; /* Device number */
ino_t ino; /* Inode number */
#ifdef SQLITE_UNIX_THREADS
pthread_t thread_id; /* Which thread are we */
#endif
};
/*
@@ -190,6 +211,9 @@ static struct lockInfo *findLockInfo(int fd){
memset(&key, 0, sizeof(key));
key.dev = statbuf.st_dev;
key.ino = statbuf.st_ino;
#ifdef SQLITE_UNIX_THREADS
key.thread_id = pthread_self();
#endif
pInfo = (struct lockInfo*)sqliteHashFind(&lockHash, &key, sizeof(key));
if( pInfo==0 ){
struct lockInfo *pOld;
@@ -1467,24 +1491,6 @@ int sqliteOsSleep(int ms){
#endif
}
/*
** Macros used to determine whether or not to use threads. The
** SQLITE_UNIX_THREADS macro is defined if we are synchronizing for
** Posix threads and SQLITE_W32_THREADS is defined if we are
** synchronizing using Win32 threads.
*/
#if OS_UNIX && defined(THREADSAFE) && THREADSAFE
# include <pthread.h>
# define SQLITE_UNIX_THREADS 1
#endif
#if OS_WIN && defined(THREADSAFE) && THREADSAFE
# define SQLITE_W32_THREADS 1
#endif
#if OS_MAC && defined(THREADSAFE) && THREADSAFE
# include <Multiprocessing.h>
# define SQLITE_MACOS_MULTITASKING 1
#endif
/*
** Static variables used for thread synchronization
*/

View File

@@ -11,7 +11,7 @@
*************************************************************************
** A TCL Interface to SQLite
**
** $Id: tclsqlite.c,v 1.51 2003/10/18 09:37:26 danielk1977 Exp $
** $Id: tclsqlite.c,v 1.52 2003/12/19 02:52:09 drh Exp $
*/
#ifndef NO_TCL /* Omit this whole file if TCL is unavailable */
@@ -1074,10 +1074,12 @@ int TCLSH_MAIN(int argc, char **argv){
extern int Sqlitetest1_Init(Tcl_Interp*);
extern int Sqlitetest2_Init(Tcl_Interp*);
extern int Sqlitetest3_Init(Tcl_Interp*);
extern int Sqlitetest4_Init(Tcl_Interp*);
extern int Md5_Init(Tcl_Interp*);
Sqlitetest1_Init(interp);
Sqlitetest2_Init(interp);
Sqlitetest3_Init(interp);
Sqlitetest4_Init(interp);
Md5_Init(interp);
}
#endif

594
src/test4.c Normal file
View File

@@ -0,0 +1,594 @@
/*
** 2003 December 18
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the the SQLite library in a multithreaded environment.
**
** $Id: test4.c,v 1.1 2003/12/19 02:52:09 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#if defined(OS_UNIX) && defined(THREADSAFE) && THREADSAFE==1
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sched.h>
#include <ctype.h>
/*
** Each thread is controlled by an instance of the following
** structure.
*/
typedef struct Thread Thread;
struct Thread {
/* The first group of fields are writable by the master and read-only
** to the thread. */
char *zFilename; /* Name of database file */
void (*xOp)(Thread*); /* next operation to do */
char *zArg; /* argument usable by xOp */
int opnum; /* Operation number */
int busy; /* True if this thread is in use */
/* The next group of fields are writable by the thread but read-only to the
** master. */
int completed; /* Number of operations completed */
sqlite *db; /* Open database */
sqlite_vm *vm; /* Pending operation */
char *zErr; /* operation error */
char *zStaticErr; /* Static error message */
int rc; /* operation return code */
int argc; /* number of columns in result */
const char **argv; /* result columns */
const char **colv; /* result column names */
};
/*
** There can be as many as 26 threads running at once. Each is named
** by a capital letter: A, B, C, ..., Y, Z.
*/
#define N_THREAD 26
static Thread threadset[N_THREAD];
/*
** The main loop for a thread. Threads use busy waiting.
*/
static void *thread_main(void *pArg){
Thread *p = (Thread*)pArg;
if( p->db ){
sqlite_close(p->db);
}
p->db = sqlite_open(p->zFilename, 0, &p->zErr);
p->vm = 0;
p->completed = 1;
while( p->opnum<=p->completed ) sched_yield();
while( p->xOp ){
if( p->zErr && p->zErr!=p->zStaticErr ){
sqlite_freemem(p->zErr);
p->zErr = 0;
}
(*p->xOp)(p);
p->completed++;
while( p->opnum<=p->completed ) sched_yield();
}
if( p->vm ){
sqlite_finalize(p->vm, 0);
p->vm = 0;
}
if( p->db ){
sqlite_close(p->db);
p->db = 0;
}
if( p->zErr && p->zErr!=p->zStaticErr ){
sqlite_freemem(p->zErr);
p->zErr = 0;
}
p->completed++;
return 0;
}
/*
** Get a thread ID which is an upper case letter. Return the index.
** If the argument is not a valid thread ID put an error message in
** the interpreter and return -1.
*/
static int parse_thread_id(Tcl_Interp *interp, const char *zArg){
if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper(zArg[0]) ){
Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0);
return -1;
}
return zArg[0] - 'A';
}
/*
** Usage: thread_create NAME FILENAME
**
** NAME should be an upper case letter. Start the thread running with
** an open connection to the given database.
*/
static int tcl_thread_create(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
const char **argv /* Text of each argument */
){
int i;
pthread_t x;
int rc;
if( argc!=3 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID FILENAME", 0);
return TCL_ERROR;
}
i = parse_thread_id(interp, argv[1]);
if( i<0 ) return TCL_ERROR;
if( threadset[i].busy ){
Tcl_AppendResult(interp, "thread ", argv[1], " is already running", 0);
return TCL_ERROR;
}
threadset[i].busy = 1;
sqliteFree(threadset[i].zFilename);
threadset[i].zFilename = sqliteStrDup(argv[2]);
threadset[i].opnum = 1;
rc = pthread_create(&x, 0, thread_main, &threadset[i]);
if( rc ){
Tcl_AppendResult(interp, "failed to create the thread", 0);
sqliteFree(threadset[i].zFilename);
threadset[i].busy = 0;
return TCL_ERROR;
}
pthread_detach(x);
return TCL_OK;
}
/*
** Wait for a thread to reach its idle state.
*/
static void thread_wait(Thread *p){
while( p->opnum>p->completed ) sched_yield();
}
/*
** Usage: thread_wait ID
**
** Wait on thread ID to reach its idle state.
*/
static int tcl_thread_wait(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
const char **argv /* Text of each argument */
){
int i;
if( argc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID", 0);
return TCL_ERROR;
}
i = parse_thread_id(interp, argv[1]);
if( i<0 ) return TCL_ERROR;
if( !threadset[i].busy ){
Tcl_AppendResult(interp, "no such thread", 0);
return TCL_ERROR;
}
thread_wait(&threadset[i]);
return TCL_OK;
}
/*
** Stop a thread.
*/
static void stop_thread(Thread *p){
thread_wait(p);
p->xOp = 0;
p->opnum++;
thread_wait(p);
sqliteFree(p->zArg);
p->zArg = 0;
sqliteFree(p->zFilename);
p->zFilename = 0;
p->busy = 0;
}
/*
** Usage: thread_halt ID
**
** Cause a thread to shut itself down. Wait for the shutdown to be
** completed. If ID is "*" then stop all threads.
*/
static int tcl_thread_halt(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
const char **argv /* Text of each argument */
){
int i;
if( argc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID", 0);
return TCL_ERROR;
}
if( argv[1][0]=='*' && argv[1][1]==0 ){
for(i=0; i<N_THREAD; i++){
if( threadset[i].busy ) stop_thread(&threadset[i]);
}
}else{
i = parse_thread_id(interp, argv[1]);
if( i<0 ) return TCL_ERROR;
if( !threadset[i].busy ){
Tcl_AppendResult(interp, "no such thread", 0);
return TCL_ERROR;
}
stop_thread(&threadset[i]);
}
return TCL_OK;
}
/*
** Usage: thread_argc ID
**
** Wait on the most recent thread_step to complete, then return the
** number of columns in the result set.
*/
static int tcl_thread_argc(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
const char **argv /* Text of each argument */
){
int i;
char zBuf[100];
if( argc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID", 0);
return TCL_ERROR;
}
i = parse_thread_id(interp, argv[1]);
if( i<0 ) return TCL_ERROR;
if( !threadset[i].busy ){
Tcl_AppendResult(interp, "no such thread", 0);
return TCL_ERROR;
}
thread_wait(&threadset[i]);
sprintf(zBuf, "%d", threadset[i].argc);
Tcl_AppendResult(interp, zBuf, 0);
return TCL_OK;
}
/*
** Usage: thread_argv ID N
**
** Wait on the most recent thread_step to complete, then return the
** value of the N-th columns in the result set.
*/
static int tcl_thread_argv(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
const char **argv /* Text of each argument */
){
int i;
int n;
if( argc!=3 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID N", 0);
return TCL_ERROR;
}
i = parse_thread_id(interp, argv[1]);
if( i<0 ) return TCL_ERROR;
if( !threadset[i].busy ){
Tcl_AppendResult(interp, "no such thread", 0);
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
thread_wait(&threadset[i]);
if( n<0 || n>=threadset[i].argc ){
Tcl_AppendResult(interp, "column number out of range", 0);
return TCL_ERROR;
}
Tcl_AppendResult(interp, threadset[i].argv[n], 0);
return TCL_OK;
}
/*
** Usage: thread_colname ID N
**
** Wait on the most recent thread_step to complete, then return the
** name of the N-th columns in the result set.
*/
static int tcl_thread_colname(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
const char **argv /* Text of each argument */
){
int i;
int n;
if( argc!=3 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID N", 0);
return TCL_ERROR;
}
i = parse_thread_id(interp, argv[1]);
if( i<0 ) return TCL_ERROR;
if( !threadset[i].busy ){
Tcl_AppendResult(interp, "no such thread", 0);
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
thread_wait(&threadset[i]);
if( n<0 || n>=threadset[i].argc ){
Tcl_AppendResult(interp, "column number out of range", 0);
return TCL_ERROR;
}
Tcl_AppendResult(interp, threadset[i].colv[n], 0);
return TCL_OK;
}
/*
** Usage: thread_result ID
**
** Wait on the most recent operation to complete, then return the
** result code from that operation.
*/
static int tcl_thread_result(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
const char **argv /* Text of each argument */
){
int i;
const char *zName;
if( argc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID", 0);
return TCL_ERROR;
}
i = parse_thread_id(interp, argv[1]);
if( i<0 ) return TCL_ERROR;
if( !threadset[i].busy ){
Tcl_AppendResult(interp, "no such thread", 0);
return TCL_ERROR;
}
thread_wait(&threadset[i]);
switch( threadset[i].rc ){
case SQLITE_OK: zName = "SQLITE_OK"; break;
case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break;
case SQLITE_PERM: zName = "SQLITE_PERM"; break;
case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break;
case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
case SQLITE_READONLY: zName = "SQLITE_READONLY"; break;
case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break;
case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break;
case SQLITE_FULL: zName = "SQLITE_FULL"; break;
case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break;
case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break;
case SQLITE_TOOBIG: zName = "SQLITE_TOOBIG"; break;
case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break;
case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break;
case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break;
case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break;
case SQLITE_AUTH: zName = "SQLITE_AUTH"; break;
case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break;
case SQLITE_RANGE: zName = "SQLITE_RANGE"; break;
case SQLITE_ROW: zName = "SQLITE_ROW"; break;
case SQLITE_DONE: zName = "SQLITE_DONE"; break;
default: zName = "SQLITE_Unknown"; break;
}
Tcl_AppendResult(interp, zName, 0);
return TCL_OK;
}
/*
** Usage: thread_error ID
**
** Wait on the most recent operation to complete, then return the
** error string.
*/
static int tcl_thread_error(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
const char **argv /* Text of each argument */
){
int i;
if( argc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID", 0);
return TCL_ERROR;
}
i = parse_thread_id(interp, argv[1]);
if( i<0 ) return TCL_ERROR;
if( !threadset[i].busy ){
Tcl_AppendResult(interp, "no such thread", 0);
return TCL_ERROR;
}
thread_wait(&threadset[i]);
Tcl_AppendResult(interp, threadset[i].zErr, 0);
return TCL_OK;
}
/*
** This procedure runs in the thread to compile an SQL statement.
*/
static void do_compile(Thread *p){
if( p->db==0 ){
p->zErr = p->zStaticErr = "no database is open";
p->rc = SQLITE_ERROR;
return;
}
if( p->vm ){
sqlite_finalize(p->vm, 0);
p->vm = 0;
}
p->rc = sqlite_compile(p->db, p->zArg, 0, &p->vm, &p->zErr);
}
/*
** Usage: thread_compile ID SQL
**
** Compile a new virtual machine.
*/
static int tcl_thread_compile(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
const char **argv /* Text of each argument */
){
int i;
if( argc!=3 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID SQL", 0);
return TCL_ERROR;
}
i = parse_thread_id(interp, argv[1]);
if( i<0 ) return TCL_ERROR;
if( !threadset[i].busy ){
Tcl_AppendResult(interp, "no such thread", 0);
return TCL_ERROR;
}
thread_wait(&threadset[i]);
threadset[i].xOp = do_compile;
sqliteFree(threadset[i].zArg);
threadset[i].zArg = sqliteStrDup(argv[2]);
threadset[i].opnum++;
return TCL_OK;
}
/*
** This procedure runs in the thread to step the virtual machine.
*/
static void do_step(Thread *p){
if( p->vm==0 ){
p->zErr = p->zStaticErr = "no virtual machine available";
p->rc = SQLITE_ERROR;
return;
}
p->rc = sqlite_step(p->vm, &p->argc, &p->argv, &p->colv);
}
/*
** Usage: thread_step ID
**
** Advance the virtual machine by one step
*/
static int tcl_thread_step(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
const char **argv /* Text of each argument */
){
int i;
if( argc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" IDL", 0);
return TCL_ERROR;
}
i = parse_thread_id(interp, argv[1]);
if( i<0 ) return TCL_ERROR;
if( !threadset[i].busy ){
Tcl_AppendResult(interp, "no such thread", 0);
return TCL_ERROR;
}
thread_wait(&threadset[i]);
threadset[i].xOp = do_step;
threadset[i].opnum++;
return TCL_OK;
}
/*
** This procedure runs in the thread to finalize a virtual machine.
*/
static void do_finalize(Thread *p){
if( p->vm==0 ){
p->zErr = p->zStaticErr = "no virtual machine available";
p->rc = SQLITE_ERROR;
return;
}
p->rc = sqlite_finalize(p->vm, &p->zErr);
p->vm = 0;
}
/*
** Usage: thread_finalize ID
**
** Finalize the virtual machine.
*/
static int tcl_thread_finalize(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
const char **argv /* Text of each argument */
){
int i;
if( argc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" IDL", 0);
return TCL_ERROR;
}
i = parse_thread_id(interp, argv[1]);
if( i<0 ) return TCL_ERROR;
if( !threadset[i].busy ){
Tcl_AppendResult(interp, "no such thread", 0);
return TCL_ERROR;
}
thread_wait(&threadset[i]);
threadset[i].xOp = do_finalize;
sqliteFree(threadset[i].zArg);
threadset[i].zArg = 0;
threadset[i].opnum++;
return TCL_OK;
}
/*
** Register commands with the TCL interpreter.
*/
int Sqlitetest4_Init(Tcl_Interp *interp){
static struct {
char *zName;
Tcl_CmdProc *xProc;
} aCmd[] = {
{ "thread_create", (Tcl_CmdProc*)tcl_thread_create },
{ "thread_wait", (Tcl_CmdProc*)tcl_thread_wait },
{ "thread_halt", (Tcl_CmdProc*)tcl_thread_halt },
{ "thread_argc", (Tcl_CmdProc*)tcl_thread_argc },
{ "thread_argv", (Tcl_CmdProc*)tcl_thread_argv },
{ "thread_colname", (Tcl_CmdProc*)tcl_thread_colname },
{ "thread_result", (Tcl_CmdProc*)tcl_thread_result },
{ "thread_error", (Tcl_CmdProc*)tcl_thread_error },
{ "thread_compile", (Tcl_CmdProc*)tcl_thread_compile },
{ "thread_step", (Tcl_CmdProc*)tcl_thread_step },
{ "thread_finalize", (Tcl_CmdProc*)tcl_thread_finalize },
};
int i;
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
}
return TCL_OK;
}
#else
int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; }
#endif /* OS_UNIX */

88
test/thread1.test Normal file
View File

@@ -0,0 +1,88 @@
# 2003 December 18
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this script is multithreading behavior
#
# $Id: thread1.test,v 1.1 2003/12/19 02:52:09 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# Skip this whole file if the thread testing code is not enabled
#
if {[llength [info command thread_step]]==0} {
finish_test
return
}
# Create some data to work with
#
do_test thread1-1.1 {
execsql {
CREATE TABLE t1(a,b);
INSERT INTO t1 VALUES(1,'abcdefgh');
INSERT INTO t1 SELECT a+1, b||b FROM t1;
INSERT INTO t1 SELECT a+2, b||b FROM t1;
INSERT INTO t1 SELECT a+4, b||b FROM t1;
SELECT count(*), max(length(b)) FROM t1;
}
} {8 64}
# Interleave two threads on read access. Then make sure a third
# thread can write the database.
#
do_test thread1-1.2 {
thread_create A test.db
thread_create B test.db
thread_create C test.db
thread_compile A {SELECT a FROM t1}
thread_step A
thread_result A
} SQLITE_ROW
do_test thread1-1.3 {
thread_argc A
} 1
do_test thread1-1.4 {
thread_argv A 0
} 1
do_test thread1-1.5 {
thread_compile B {SELECT b FROM t1}
thread_step B
thread_result B
} SQLITE_ROW
do_test thread1-1.6 {
thread_argc B
} 1
do_test thread1-1.7 {
thread_argv B 0
} abcdefgh
do_test thread1-1.8 {
thread_finalize A
thread_result A
} SQLITE_OK
do_test thread1-1.9 {
thread_finalize B
thread_result B
} SQLITE_OK
do_test thread1-1.10 {
thread_compile C {CREATE TABLE t2(x,y)}
thread_step C
thread_result C
} SQLITE_DONE
do_test thread1-1.11 {
thread_finalize C
thread_result C
} SQLITE_OK
thread_halt *
finish_test