4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
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.
11 *************************************************************************
12 ** Code for testing the SQLite library in a multithreaded environment.
14 #include "sqliteInt.h"
15 #include "tclsqlite.h"
16 #if SQLITE_OS_UNIX && SQLITE_THREADSAFE
23 extern const char *sqlite3ErrName(int);
26 ** Each thread is controlled by an instance of the following
29 typedef struct Thread Thread
;
31 /* The first group of fields are writable by the leader and read-only
33 char *zFilename
; /* Name of database file */
34 void (*xOp
)(Thread
*); /* next operation to do */
35 char *zArg
; /* argument usable by xOp */
36 int opnum
; /* Operation number */
37 int busy
; /* True if this thread is in use */
39 /* The next group of fields are writable by the thread but read-only to the
41 int completed
; /* Number of operations completed */
42 sqlite3
*db
; /* Open database */
43 sqlite3_stmt
*pStmt
; /* Pending operation */
44 char *zErr
; /* operation error */
45 char *zStaticErr
; /* Static error message */
46 int rc
; /* operation return code */
47 int argc
; /* number of columns in result */
48 const char *argv
[100]; /* result columns */
49 const char *colv
[100]; /* result column names */
53 ** There can be as many as 26 threads running at once. Each is named
54 ** by a capital letter: A, B, C, ..., Y, Z.
57 static Thread threadset
[N_THREAD
];
59 static void test_barrier(){
60 sqlite3_mutex
*pMutex
= sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_APP1
);
61 sqlite3_mutex_enter(pMutex
);
62 sqlite3_mutex_leave(pMutex
);
66 ** The main loop for a thread. Threads use busy waiting.
68 static void *test_thread_main(void *pArg
){
69 Thread
*p
= (Thread
*)pArg
;
73 sqlite3_open(p
->zFilename
, &p
->db
);
74 if( SQLITE_OK
!=sqlite3_errcode(p
->db
) ){
75 p
->zErr
= strdup(sqlite3_errmsg(p
->db
));
82 while( p
->opnum
<=p
->completed
) sched_yield();
85 if( p
->zErr
&& p
->zErr
!=p
->zStaticErr
){
86 sqlite3_free(p
->zErr
);
92 while( p
->opnum
<=p
->completed
) sched_yield();
96 sqlite3_finalize(p
->pStmt
);
100 sqlite3_close(p
->db
);
103 if( p
->zErr
&& p
->zErr
!=p
->zStaticErr
){
104 sqlite3_free(p
->zErr
);
109 #ifndef SQLITE_OMIT_DEPRECATED
110 sqlite3_thread_cleanup();
116 ** Get a thread ID which is an upper case letter. Return the index.
117 ** If the argument is not a valid thread ID put an error message in
118 ** the interpreter and return -1.
120 static int parse_thread_id(Tcl_Interp
*interp
, const char *zArg
){
121 if( zArg
==0 || zArg
[0]==0 || zArg
[1]!=0 || !isupper((unsigned char)zArg
[0]) ){
122 Tcl_AppendResult(interp
, "thread ID must be an upper case letter", 0);
125 return zArg
[0] - 'A';
129 ** Usage: thread_create NAME FILENAME
131 ** NAME should be an upper case letter. Start the thread running with
132 ** an open connection to the given database.
134 static int SQLITE_TCLAPI
tcl_thread_create(
136 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
137 int argc
, /* Number of arguments */
138 const char **argv
/* Text of each argument */
145 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
149 i
= parse_thread_id(interp
, argv
[1]);
150 if( i
<0 ) return TCL_ERROR
;
151 if( threadset
[i
].busy
){
152 Tcl_AppendResult(interp
, "thread ", argv
[1], " is already running", 0);
155 threadset
[i
].busy
= 1;
156 sqlite3_free(threadset
[i
].zFilename
);
157 threadset
[i
].zFilename
= sqlite3_mprintf("%s", argv
[2]);
158 threadset
[i
].opnum
= 1;
159 threadset
[i
].completed
= 0;
160 rc
= pthread_create(&x
, 0, test_thread_main
, &threadset
[i
]);
162 Tcl_AppendResult(interp
, "failed to create the thread", 0);
163 sqlite3_free(threadset
[i
].zFilename
);
164 threadset
[i
].busy
= 0;
172 ** Wait for a thread to reach its idle state.
174 static void test_thread_wait(Thread
*p
){
176 while( p
->opnum
>p
->completed
) sched_yield();
181 ** Usage: thread_wait ID
183 ** Wait on thread ID to reach its idle state.
185 static int SQLITE_TCLAPI
tcl_thread_wait(
187 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
188 int argc
, /* Number of arguments */
189 const char **argv
/* Text of each argument */
194 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
198 i
= parse_thread_id(interp
, argv
[1]);
199 if( i
<0 ) return TCL_ERROR
;
200 if( !threadset
[i
].busy
){
201 Tcl_AppendResult(interp
, "no such thread", 0);
204 test_thread_wait(&threadset
[i
]);
211 static void test_stop_thread(Thread
*p
){
216 sqlite3_free(p
->zArg
);
218 sqlite3_free(p
->zFilename
);
224 ** Usage: thread_halt ID
226 ** Cause a thread to shut itself down. Wait for the shutdown to be
227 ** completed. If ID is "*" then stop all threads.
229 static int SQLITE_TCLAPI
tcl_thread_halt(
231 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
232 int argc
, /* Number of arguments */
233 const char **argv
/* Text of each argument */
238 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
242 if( argv
[1][0]=='*' && argv
[1][1]==0 ){
243 for(i
=0; i
<N_THREAD
; i
++){
244 if( threadset
[i
].busy
) test_stop_thread(&threadset
[i
]);
247 i
= parse_thread_id(interp
, argv
[1]);
248 if( i
<0 ) return TCL_ERROR
;
249 if( !threadset
[i
].busy
){
250 Tcl_AppendResult(interp
, "no such thread", 0);
253 test_stop_thread(&threadset
[i
]);
259 ** Usage: thread_argc ID
261 ** Wait on the most recent thread_step to complete, then return the
262 ** number of columns in the result set.
264 static int SQLITE_TCLAPI
tcl_thread_argc(
266 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
267 int argc
, /* Number of arguments */
268 const char **argv
/* Text of each argument */
274 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
278 i
= parse_thread_id(interp
, argv
[1]);
279 if( i
<0 ) return TCL_ERROR
;
280 if( !threadset
[i
].busy
){
281 Tcl_AppendResult(interp
, "no such thread", 0);
284 test_thread_wait(&threadset
[i
]);
285 sqlite3_snprintf(sizeof(zBuf
), zBuf
, "%d", threadset
[i
].argc
);
286 Tcl_AppendResult(interp
, zBuf
, 0);
291 ** Usage: thread_argv ID N
293 ** Wait on the most recent thread_step to complete, then return the
294 ** value of the N-th columns in the result set.
296 static int SQLITE_TCLAPI
tcl_thread_argv(
298 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
299 int argc
, /* Number of arguments */
300 const char **argv
/* Text of each argument */
306 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
310 i
= parse_thread_id(interp
, argv
[1]);
311 if( i
<0 ) return TCL_ERROR
;
312 if( !threadset
[i
].busy
){
313 Tcl_AppendResult(interp
, "no such thread", 0);
316 if( Tcl_GetInt(interp
, argv
[2], &n
) ) return TCL_ERROR
;
317 test_thread_wait(&threadset
[i
]);
318 if( n
<0 || n
>=threadset
[i
].argc
){
319 Tcl_AppendResult(interp
, "column number out of range", 0);
322 Tcl_AppendResult(interp
, threadset
[i
].argv
[n
], 0);
327 ** Usage: thread_colname ID N
329 ** Wait on the most recent thread_step to complete, then return the
330 ** name of the N-th columns in the result set.
332 static int SQLITE_TCLAPI
tcl_thread_colname(
334 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
335 int argc
, /* Number of arguments */
336 const char **argv
/* Text of each argument */
342 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
346 i
= parse_thread_id(interp
, argv
[1]);
347 if( i
<0 ) return TCL_ERROR
;
348 if( !threadset
[i
].busy
){
349 Tcl_AppendResult(interp
, "no such thread", 0);
352 if( Tcl_GetInt(interp
, argv
[2], &n
) ) return TCL_ERROR
;
353 test_thread_wait(&threadset
[i
]);
354 if( n
<0 || n
>=threadset
[i
].argc
){
355 Tcl_AppendResult(interp
, "column number out of range", 0);
358 Tcl_AppendResult(interp
, threadset
[i
].colv
[n
], 0);
363 ** Usage: thread_result ID
365 ** Wait on the most recent operation to complete, then return the
366 ** result code from that operation.
368 static int SQLITE_TCLAPI
tcl_thread_result(
370 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
371 int argc
, /* Number of arguments */
372 const char **argv
/* Text of each argument */
378 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
382 i
= parse_thread_id(interp
, argv
[1]);
383 if( i
<0 ) return TCL_ERROR
;
384 if( !threadset
[i
].busy
){
385 Tcl_AppendResult(interp
, "no such thread", 0);
388 test_thread_wait(&threadset
[i
]);
389 zName
= sqlite3ErrName(threadset
[i
].rc
);
390 Tcl_AppendResult(interp
, zName
, 0);
395 ** Usage: thread_error ID
397 ** Wait on the most recent operation to complete, then return the
400 static int SQLITE_TCLAPI
tcl_thread_error(
402 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
403 int argc
, /* Number of arguments */
404 const char **argv
/* Text of each argument */
409 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
413 i
= parse_thread_id(interp
, argv
[1]);
414 if( i
<0 ) return TCL_ERROR
;
415 if( !threadset
[i
].busy
){
416 Tcl_AppendResult(interp
, "no such thread", 0);
419 test_thread_wait(&threadset
[i
]);
420 Tcl_AppendResult(interp
, threadset
[i
].zErr
, 0);
425 ** This procedure runs in the thread to compile an SQL statement.
427 static void do_compile(Thread
*p
){
429 p
->zErr
= p
->zStaticErr
= "no database is open";
430 p
->rc
= SQLITE_ERROR
;
434 sqlite3_finalize(p
->pStmt
);
437 p
->rc
= sqlite3_prepare(p
->db
, p
->zArg
, -1, &p
->pStmt
, 0);
441 ** Usage: thread_compile ID SQL
443 ** Compile a new virtual machine.
445 static int SQLITE_TCLAPI
tcl_thread_compile(
447 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
448 int argc
, /* Number of arguments */
449 const char **argv
/* Text of each argument */
453 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
457 i
= parse_thread_id(interp
, argv
[1]);
458 if( i
<0 ) return TCL_ERROR
;
459 if( !threadset
[i
].busy
){
460 Tcl_AppendResult(interp
, "no such thread", 0);
463 test_thread_wait(&threadset
[i
]);
464 threadset
[i
].xOp
= do_compile
;
465 sqlite3_free(threadset
[i
].zArg
);
466 threadset
[i
].zArg
= sqlite3_mprintf("%s", argv
[2]);
468 threadset
[i
].opnum
++;
473 ** This procedure runs in the thread to step the virtual machine.
475 static void do_step(Thread
*p
){
478 p
->zErr
= p
->zStaticErr
= "no virtual machine available";
479 p
->rc
= SQLITE_ERROR
;
482 p
->rc
= sqlite3_step(p
->pStmt
);
483 if( p
->rc
==SQLITE_ROW
){
484 p
->argc
= sqlite3_column_count(p
->pStmt
);
485 for(i
=0; i
<sqlite3_data_count(p
->pStmt
); i
++){
486 p
->argv
[i
] = (char*)sqlite3_column_text(p
->pStmt
, i
);
488 for(i
=0; i
<p
->argc
; i
++){
489 p
->colv
[i
] = sqlite3_column_name(p
->pStmt
, i
);
495 ** Usage: thread_step ID
497 ** Advance the virtual machine by one step
499 static int SQLITE_TCLAPI
tcl_thread_step(
501 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
502 int argc
, /* Number of arguments */
503 const char **argv
/* Text of each argument */
507 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
511 i
= parse_thread_id(interp
, argv
[1]);
512 if( i
<0 ) return TCL_ERROR
;
513 if( !threadset
[i
].busy
){
514 Tcl_AppendResult(interp
, "no such thread", 0);
517 test_thread_wait(&threadset
[i
]);
518 threadset
[i
].xOp
= do_step
;
520 threadset
[i
].opnum
++;
525 ** This procedure runs in the thread to finalize a virtual machine.
527 static void do_finalize(Thread
*p
){
529 p
->zErr
= p
->zStaticErr
= "no virtual machine available";
530 p
->rc
= SQLITE_ERROR
;
533 p
->rc
= sqlite3_finalize(p
->pStmt
);
538 ** Usage: thread_finalize ID
540 ** Finalize the virtual machine.
542 static int SQLITE_TCLAPI
tcl_thread_finalize(
544 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
545 int argc
, /* Number of arguments */
546 const char **argv
/* Text of each argument */
550 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
554 i
= parse_thread_id(interp
, argv
[1]);
555 if( i
<0 ) return TCL_ERROR
;
556 if( !threadset
[i
].busy
){
557 Tcl_AppendResult(interp
, "no such thread", 0);
560 test_thread_wait(&threadset
[i
]);
561 threadset
[i
].xOp
= do_finalize
;
562 sqlite3_free(threadset
[i
].zArg
);
563 threadset
[i
].zArg
= 0;
565 threadset
[i
].opnum
++;
570 ** Usage: thread_swap ID ID
572 ** Interchange the sqlite* pointer between two threads.
574 static int SQLITE_TCLAPI
tcl_thread_swap(
576 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
577 int argc
, /* Number of arguments */
578 const char **argv
/* Text of each argument */
583 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
587 i
= parse_thread_id(interp
, argv
[1]);
588 if( i
<0 ) return TCL_ERROR
;
589 if( !threadset
[i
].busy
){
590 Tcl_AppendResult(interp
, "no such thread", 0);
593 test_thread_wait(&threadset
[i
]);
594 j
= parse_thread_id(interp
, argv
[2]);
595 if( j
<0 ) return TCL_ERROR
;
596 if( !threadset
[j
].busy
){
597 Tcl_AppendResult(interp
, "no such thread", 0);
600 test_thread_wait(&threadset
[j
]);
601 temp
= threadset
[i
].db
;
602 threadset
[i
].db
= threadset
[j
].db
;
603 threadset
[j
].db
= temp
;
608 ** Usage: thread_db_get ID
610 ** Return the database connection pointer for the given thread. Then
611 ** remove the pointer from the thread itself. Afterwards, the thread
612 ** can be stopped and the connection can be used by the main thread.
614 static int SQLITE_TCLAPI
tcl_thread_db_get(
616 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
617 int argc
, /* Number of arguments */
618 const char **argv
/* Text of each argument */
622 extern int sqlite3TestMakePointerStr(Tcl_Interp
*, char*, void*);
624 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
628 i
= parse_thread_id(interp
, argv
[1]);
629 if( i
<0 ) return TCL_ERROR
;
630 if( !threadset
[i
].busy
){
631 Tcl_AppendResult(interp
, "no such thread", 0);
634 test_thread_wait(&threadset
[i
]);
635 sqlite3TestMakePointerStr(interp
, zBuf
, threadset
[i
].db
);
637 Tcl_AppendResult(interp
, zBuf
, (char*)0);
642 ** Usage: thread_db_put ID DB
645 static int SQLITE_TCLAPI
tcl_thread_db_put(
647 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
648 int argc
, /* Number of arguments */
649 const char **argv
/* Text of each argument */
652 extern int sqlite3TestMakePointerStr(Tcl_Interp
*, char*, void*);
653 extern void *sqlite3TestTextToPtr(const char *);
655 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
659 i
= parse_thread_id(interp
, argv
[1]);
660 if( i
<0 ) return TCL_ERROR
;
661 if( !threadset
[i
].busy
){
662 Tcl_AppendResult(interp
, "no such thread", 0);
665 test_thread_wait(&threadset
[i
]);
666 assert( !threadset
[i
].db
);
667 threadset
[i
].db
= (sqlite3
*)sqlite3TestTextToPtr(argv
[2]);
672 ** Usage: thread_stmt_get ID
674 ** Return the database stmt pointer for the given thread. Then
675 ** remove the pointer from the thread itself.
677 static int SQLITE_TCLAPI
tcl_thread_stmt_get(
679 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
680 int argc
, /* Number of arguments */
681 const char **argv
/* Text of each argument */
685 extern int sqlite3TestMakePointerStr(Tcl_Interp
*, char*, void*);
687 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
691 i
= parse_thread_id(interp
, argv
[1]);
692 if( i
<0 ) return TCL_ERROR
;
693 if( !threadset
[i
].busy
){
694 Tcl_AppendResult(interp
, "no such thread", 0);
697 test_thread_wait(&threadset
[i
]);
698 sqlite3TestMakePointerStr(interp
, zBuf
, threadset
[i
].pStmt
);
699 threadset
[i
].pStmt
= 0;
700 Tcl_AppendResult(interp
, zBuf
, (char*)0);
705 ** Register commands with the TCL interpreter.
707 int Sqlitetest4_Init(Tcl_Interp
*interp
){
712 { "thread_create", (Tcl_CmdProc
*)tcl_thread_create
},
713 { "thread_wait", (Tcl_CmdProc
*)tcl_thread_wait
},
714 { "thread_halt", (Tcl_CmdProc
*)tcl_thread_halt
},
715 { "thread_argc", (Tcl_CmdProc
*)tcl_thread_argc
},
716 { "thread_argv", (Tcl_CmdProc
*)tcl_thread_argv
},
717 { "thread_colname", (Tcl_CmdProc
*)tcl_thread_colname
},
718 { "thread_result", (Tcl_CmdProc
*)tcl_thread_result
},
719 { "thread_error", (Tcl_CmdProc
*)tcl_thread_error
},
720 { "thread_compile", (Tcl_CmdProc
*)tcl_thread_compile
},
721 { "thread_step", (Tcl_CmdProc
*)tcl_thread_step
},
722 { "thread_finalize", (Tcl_CmdProc
*)tcl_thread_finalize
},
723 { "thread_swap", (Tcl_CmdProc
*)tcl_thread_swap
},
724 { "thread_db_get", (Tcl_CmdProc
*)tcl_thread_db_get
},
725 { "thread_db_put", (Tcl_CmdProc
*)tcl_thread_db_put
},
726 { "thread_stmt_get", (Tcl_CmdProc
*)tcl_thread_stmt_get
},
730 for(i
=0; i
<sizeof(aCmd
)/sizeof(aCmd
[0]); i
++){
731 Tcl_CreateCommand(interp
, aCmd
[i
].zName
, aCmd
[i
].xProc
, 0, 0);
736 int Sqlitetest4_Init(Tcl_Interp
*interp
){ return TCL_OK
; }
737 #endif /* SQLITE_OS_UNIX */