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"
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 master 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
];
61 ** The main loop for a thread. Threads use busy waiting.
63 static void *thread_main(void *pArg
){
64 Thread
*p
= (Thread
*)pArg
;
68 sqlite3_open(p
->zFilename
, &p
->db
);
69 if( SQLITE_OK
!=sqlite3_errcode(p
->db
) ){
70 p
->zErr
= strdup(sqlite3_errmsg(p
->db
));
76 while( p
->opnum
<=p
->completed
) sched_yield();
78 if( p
->zErr
&& p
->zErr
!=p
->zStaticErr
){
79 sqlite3_free(p
->zErr
);
84 while( p
->opnum
<=p
->completed
) sched_yield();
87 sqlite3_finalize(p
->pStmt
);
94 if( p
->zErr
&& p
->zErr
!=p
->zStaticErr
){
95 sqlite3_free(p
->zErr
);
99 #ifndef SQLITE_OMIT_DEPRECATED
100 sqlite3_thread_cleanup();
106 ** Get a thread ID which is an upper case letter. Return the index.
107 ** If the argument is not a valid thread ID put an error message in
108 ** the interpreter and return -1.
110 static int parse_thread_id(Tcl_Interp
*interp
, const char *zArg
){
111 if( zArg
==0 || zArg
[0]==0 || zArg
[1]!=0 || !isupper((unsigned char)zArg
[0]) ){
112 Tcl_AppendResult(interp
, "thread ID must be an upper case letter", 0);
115 return zArg
[0] - 'A';
119 ** Usage: thread_create NAME FILENAME
121 ** NAME should be an upper case letter. Start the thread running with
122 ** an open connection to the given database.
124 static int tcl_thread_create(
126 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
127 int argc
, /* Number of arguments */
128 const char **argv
/* Text of each argument */
135 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
139 i
= parse_thread_id(interp
, argv
[1]);
140 if( i
<0 ) return TCL_ERROR
;
141 if( threadset
[i
].busy
){
142 Tcl_AppendResult(interp
, "thread ", argv
[1], " is already running", 0);
145 threadset
[i
].busy
= 1;
146 sqlite3_free(threadset
[i
].zFilename
);
147 threadset
[i
].zFilename
= sqlite3_mprintf("%s", argv
[2]);
148 threadset
[i
].opnum
= 1;
149 threadset
[i
].completed
= 0;
150 rc
= pthread_create(&x
, 0, thread_main
, &threadset
[i
]);
152 Tcl_AppendResult(interp
, "failed to create the thread", 0);
153 sqlite3_free(threadset
[i
].zFilename
);
154 threadset
[i
].busy
= 0;
162 ** Wait for a thread to reach its idle state.
164 static void thread_wait(Thread
*p
){
165 while( p
->opnum
>p
->completed
) sched_yield();
169 ** Usage: thread_wait ID
171 ** Wait on thread ID to reach its idle state.
173 static int tcl_thread_wait(
175 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
176 int argc
, /* Number of arguments */
177 const char **argv
/* Text of each argument */
182 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
186 i
= parse_thread_id(interp
, argv
[1]);
187 if( i
<0 ) return TCL_ERROR
;
188 if( !threadset
[i
].busy
){
189 Tcl_AppendResult(interp
, "no such thread", 0);
192 thread_wait(&threadset
[i
]);
199 static void stop_thread(Thread
*p
){
204 sqlite3_free(p
->zArg
);
206 sqlite3_free(p
->zFilename
);
212 ** Usage: thread_halt ID
214 ** Cause a thread to shut itself down. Wait for the shutdown to be
215 ** completed. If ID is "*" then stop all threads.
217 static int tcl_thread_halt(
219 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
220 int argc
, /* Number of arguments */
221 const char **argv
/* Text of each argument */
226 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
230 if( argv
[1][0]=='*' && argv
[1][1]==0 ){
231 for(i
=0; i
<N_THREAD
; i
++){
232 if( threadset
[i
].busy
) stop_thread(&threadset
[i
]);
235 i
= parse_thread_id(interp
, argv
[1]);
236 if( i
<0 ) return TCL_ERROR
;
237 if( !threadset
[i
].busy
){
238 Tcl_AppendResult(interp
, "no such thread", 0);
241 stop_thread(&threadset
[i
]);
247 ** Usage: thread_argc ID
249 ** Wait on the most recent thread_step to complete, then return the
250 ** number of columns in the result set.
252 static int tcl_thread_argc(
254 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
255 int argc
, /* Number of arguments */
256 const char **argv
/* Text of each argument */
262 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
266 i
= parse_thread_id(interp
, argv
[1]);
267 if( i
<0 ) return TCL_ERROR
;
268 if( !threadset
[i
].busy
){
269 Tcl_AppendResult(interp
, "no such thread", 0);
272 thread_wait(&threadset
[i
]);
273 sprintf(zBuf
, "%d", threadset
[i
].argc
);
274 Tcl_AppendResult(interp
, zBuf
, 0);
279 ** Usage: thread_argv ID N
281 ** Wait on the most recent thread_step to complete, then return the
282 ** value of the N-th columns in the result set.
284 static int tcl_thread_argv(
286 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
287 int argc
, /* Number of arguments */
288 const char **argv
/* Text of each argument */
294 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
298 i
= parse_thread_id(interp
, argv
[1]);
299 if( i
<0 ) return TCL_ERROR
;
300 if( !threadset
[i
].busy
){
301 Tcl_AppendResult(interp
, "no such thread", 0);
304 if( Tcl_GetInt(interp
, argv
[2], &n
) ) return TCL_ERROR
;
305 thread_wait(&threadset
[i
]);
306 if( n
<0 || n
>=threadset
[i
].argc
){
307 Tcl_AppendResult(interp
, "column number out of range", 0);
310 Tcl_AppendResult(interp
, threadset
[i
].argv
[n
], 0);
315 ** Usage: thread_colname ID N
317 ** Wait on the most recent thread_step to complete, then return the
318 ** name of the N-th columns in the result set.
320 static int tcl_thread_colname(
322 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
323 int argc
, /* Number of arguments */
324 const char **argv
/* Text of each argument */
330 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
334 i
= parse_thread_id(interp
, argv
[1]);
335 if( i
<0 ) return TCL_ERROR
;
336 if( !threadset
[i
].busy
){
337 Tcl_AppendResult(interp
, "no such thread", 0);
340 if( Tcl_GetInt(interp
, argv
[2], &n
) ) return TCL_ERROR
;
341 thread_wait(&threadset
[i
]);
342 if( n
<0 || n
>=threadset
[i
].argc
){
343 Tcl_AppendResult(interp
, "column number out of range", 0);
346 Tcl_AppendResult(interp
, threadset
[i
].colv
[n
], 0);
351 ** Usage: thread_result ID
353 ** Wait on the most recent operation to complete, then return the
354 ** result code from that operation.
356 static int tcl_thread_result(
358 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
359 int argc
, /* Number of arguments */
360 const char **argv
/* Text of each argument */
366 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
370 i
= parse_thread_id(interp
, argv
[1]);
371 if( i
<0 ) return TCL_ERROR
;
372 if( !threadset
[i
].busy
){
373 Tcl_AppendResult(interp
, "no such thread", 0);
376 thread_wait(&threadset
[i
]);
377 zName
= sqlite3ErrName(threadset
[i
].rc
);
378 Tcl_AppendResult(interp
, zName
, 0);
383 ** Usage: thread_error ID
385 ** Wait on the most recent operation to complete, then return the
388 static int tcl_thread_error(
390 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
391 int argc
, /* Number of arguments */
392 const char **argv
/* Text of each argument */
397 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
401 i
= parse_thread_id(interp
, argv
[1]);
402 if( i
<0 ) return TCL_ERROR
;
403 if( !threadset
[i
].busy
){
404 Tcl_AppendResult(interp
, "no such thread", 0);
407 thread_wait(&threadset
[i
]);
408 Tcl_AppendResult(interp
, threadset
[i
].zErr
, 0);
413 ** This procedure runs in the thread to compile an SQL statement.
415 static void do_compile(Thread
*p
){
417 p
->zErr
= p
->zStaticErr
= "no database is open";
418 p
->rc
= SQLITE_ERROR
;
422 sqlite3_finalize(p
->pStmt
);
425 p
->rc
= sqlite3_prepare(p
->db
, p
->zArg
, -1, &p
->pStmt
, 0);
429 ** Usage: thread_compile ID SQL
431 ** Compile a new virtual machine.
433 static int tcl_thread_compile(
435 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
436 int argc
, /* Number of arguments */
437 const char **argv
/* Text of each argument */
441 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
445 i
= parse_thread_id(interp
, argv
[1]);
446 if( i
<0 ) return TCL_ERROR
;
447 if( !threadset
[i
].busy
){
448 Tcl_AppendResult(interp
, "no such thread", 0);
451 thread_wait(&threadset
[i
]);
452 threadset
[i
].xOp
= do_compile
;
453 sqlite3_free(threadset
[i
].zArg
);
454 threadset
[i
].zArg
= sqlite3_mprintf("%s", argv
[2]);
455 threadset
[i
].opnum
++;
460 ** This procedure runs in the thread to step the virtual machine.
462 static void do_step(Thread
*p
){
465 p
->zErr
= p
->zStaticErr
= "no virtual machine available";
466 p
->rc
= SQLITE_ERROR
;
469 p
->rc
= sqlite3_step(p
->pStmt
);
470 if( p
->rc
==SQLITE_ROW
){
471 p
->argc
= sqlite3_column_count(p
->pStmt
);
472 for(i
=0; i
<sqlite3_data_count(p
->pStmt
); i
++){
473 p
->argv
[i
] = (char*)sqlite3_column_text(p
->pStmt
, i
);
475 for(i
=0; i
<p
->argc
; i
++){
476 p
->colv
[i
] = sqlite3_column_name(p
->pStmt
, i
);
482 ** Usage: thread_step ID
484 ** Advance the virtual machine by one step
486 static int tcl_thread_step(
488 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
489 int argc
, /* Number of arguments */
490 const char **argv
/* Text of each argument */
494 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
498 i
= parse_thread_id(interp
, argv
[1]);
499 if( i
<0 ) return TCL_ERROR
;
500 if( !threadset
[i
].busy
){
501 Tcl_AppendResult(interp
, "no such thread", 0);
504 thread_wait(&threadset
[i
]);
505 threadset
[i
].xOp
= do_step
;
506 threadset
[i
].opnum
++;
511 ** This procedure runs in the thread to finalize a virtual machine.
513 static void do_finalize(Thread
*p
){
515 p
->zErr
= p
->zStaticErr
= "no virtual machine available";
516 p
->rc
= SQLITE_ERROR
;
519 p
->rc
= sqlite3_finalize(p
->pStmt
);
524 ** Usage: thread_finalize ID
526 ** Finalize the virtual machine.
528 static int tcl_thread_finalize(
530 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
531 int argc
, /* Number of arguments */
532 const char **argv
/* Text of each argument */
536 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
540 i
= parse_thread_id(interp
, argv
[1]);
541 if( i
<0 ) return TCL_ERROR
;
542 if( !threadset
[i
].busy
){
543 Tcl_AppendResult(interp
, "no such thread", 0);
546 thread_wait(&threadset
[i
]);
547 threadset
[i
].xOp
= do_finalize
;
548 sqlite3_free(threadset
[i
].zArg
);
549 threadset
[i
].zArg
= 0;
550 threadset
[i
].opnum
++;
555 ** Usage: thread_swap ID ID
557 ** Interchange the sqlite* pointer between two threads.
559 static int tcl_thread_swap(
561 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
562 int argc
, /* Number of arguments */
563 const char **argv
/* Text of each argument */
568 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
572 i
= parse_thread_id(interp
, argv
[1]);
573 if( i
<0 ) return TCL_ERROR
;
574 if( !threadset
[i
].busy
){
575 Tcl_AppendResult(interp
, "no such thread", 0);
578 thread_wait(&threadset
[i
]);
579 j
= parse_thread_id(interp
, argv
[2]);
580 if( j
<0 ) return TCL_ERROR
;
581 if( !threadset
[j
].busy
){
582 Tcl_AppendResult(interp
, "no such thread", 0);
585 thread_wait(&threadset
[j
]);
586 temp
= threadset
[i
].db
;
587 threadset
[i
].db
= threadset
[j
].db
;
588 threadset
[j
].db
= temp
;
593 ** Usage: thread_db_get ID
595 ** Return the database connection pointer for the given thread. Then
596 ** remove the pointer from the thread itself. Afterwards, the thread
597 ** can be stopped and the connection can be used by the main thread.
599 static int tcl_thread_db_get(
601 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
602 int argc
, /* Number of arguments */
603 const char **argv
/* Text of each argument */
607 extern int sqlite3TestMakePointerStr(Tcl_Interp
*, char*, void*);
609 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
613 i
= parse_thread_id(interp
, argv
[1]);
614 if( i
<0 ) return TCL_ERROR
;
615 if( !threadset
[i
].busy
){
616 Tcl_AppendResult(interp
, "no such thread", 0);
619 thread_wait(&threadset
[i
]);
620 sqlite3TestMakePointerStr(interp
, zBuf
, threadset
[i
].db
);
622 Tcl_AppendResult(interp
, zBuf
, (char*)0);
627 ** Usage: thread_db_put ID DB
630 static int tcl_thread_db_put(
632 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
633 int argc
, /* Number of arguments */
634 const char **argv
/* Text of each argument */
637 extern int sqlite3TestMakePointerStr(Tcl_Interp
*, char*, void*);
638 extern void *sqlite3TestTextToPtr(const char *);
640 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
644 i
= parse_thread_id(interp
, argv
[1]);
645 if( i
<0 ) return TCL_ERROR
;
646 if( !threadset
[i
].busy
){
647 Tcl_AppendResult(interp
, "no such thread", 0);
650 thread_wait(&threadset
[i
]);
651 assert( !threadset
[i
].db
);
652 threadset
[i
].db
= (sqlite3
*)sqlite3TestTextToPtr(argv
[2]);
657 ** Usage: thread_stmt_get ID
659 ** Return the database stmt pointer for the given thread. Then
660 ** remove the pointer from the thread itself.
662 static int tcl_thread_stmt_get(
664 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
665 int argc
, /* Number of arguments */
666 const char **argv
/* Text of each argument */
670 extern int sqlite3TestMakePointerStr(Tcl_Interp
*, char*, void*);
672 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
676 i
= parse_thread_id(interp
, argv
[1]);
677 if( i
<0 ) return TCL_ERROR
;
678 if( !threadset
[i
].busy
){
679 Tcl_AppendResult(interp
, "no such thread", 0);
682 thread_wait(&threadset
[i
]);
683 sqlite3TestMakePointerStr(interp
, zBuf
, threadset
[i
].pStmt
);
684 threadset
[i
].pStmt
= 0;
685 Tcl_AppendResult(interp
, zBuf
, (char*)0);
690 ** Register commands with the TCL interpreter.
692 int Sqlitetest4_Init(Tcl_Interp
*interp
){
697 { "thread_create", (Tcl_CmdProc
*)tcl_thread_create
},
698 { "thread_wait", (Tcl_CmdProc
*)tcl_thread_wait
},
699 { "thread_halt", (Tcl_CmdProc
*)tcl_thread_halt
},
700 { "thread_argc", (Tcl_CmdProc
*)tcl_thread_argc
},
701 { "thread_argv", (Tcl_CmdProc
*)tcl_thread_argv
},
702 { "thread_colname", (Tcl_CmdProc
*)tcl_thread_colname
},
703 { "thread_result", (Tcl_CmdProc
*)tcl_thread_result
},
704 { "thread_error", (Tcl_CmdProc
*)tcl_thread_error
},
705 { "thread_compile", (Tcl_CmdProc
*)tcl_thread_compile
},
706 { "thread_step", (Tcl_CmdProc
*)tcl_thread_step
},
707 { "thread_finalize", (Tcl_CmdProc
*)tcl_thread_finalize
},
708 { "thread_swap", (Tcl_CmdProc
*)tcl_thread_swap
},
709 { "thread_db_get", (Tcl_CmdProc
*)tcl_thread_db_get
},
710 { "thread_db_put", (Tcl_CmdProc
*)tcl_thread_db_put
},
711 { "thread_stmt_get", (Tcl_CmdProc
*)tcl_thread_stmt_get
},
715 for(i
=0; i
<sizeof(aCmd
)/sizeof(aCmd
[0]); i
++){
716 Tcl_CreateCommand(interp
, aCmd
[i
].zName
, aCmd
[i
].xProc
, 0, 0);
721 int Sqlitetest4_Init(Tcl_Interp
*interp
){ return TCL_OK
; }
722 #endif /* SQLITE_OS_UNIX */