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 #if defined(INCLUDE_SQLITE_TCL_H)
16 # include "sqlite_tcl.h"
20 #if SQLITE_OS_UNIX && SQLITE_THREADSAFE
27 extern const char *sqlite3ErrName(int);
30 ** Each thread is controlled by an instance of the following
33 typedef struct Thread Thread
;
35 /* The first group of fields are writable by the leader and read-only
37 char *zFilename
; /* Name of database file */
38 void (*xOp
)(Thread
*); /* next operation to do */
39 char *zArg
; /* argument usable by xOp */
40 int opnum
; /* Operation number */
41 int busy
; /* True if this thread is in use */
43 /* The next group of fields are writable by the thread but read-only to the
45 int completed
; /* Number of operations completed */
46 sqlite3
*db
; /* Open database */
47 sqlite3_stmt
*pStmt
; /* Pending operation */
48 char *zErr
; /* operation error */
49 char *zStaticErr
; /* Static error message */
50 int rc
; /* operation return code */
51 int argc
; /* number of columns in result */
52 const char *argv
[100]; /* result columns */
53 const char *colv
[100]; /* result column names */
57 ** There can be as many as 26 threads running at once. Each is named
58 ** by a capital letter: A, B, C, ..., Y, Z.
61 static Thread threadset
[N_THREAD
];
63 static void test_barrier(){
64 sqlite3_mutex
*pMutex
= sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_APP1
);
65 sqlite3_mutex_enter(pMutex
);
66 sqlite3_mutex_leave(pMutex
);
70 ** The main loop for a thread. Threads use busy waiting.
72 static void *test_thread_main(void *pArg
){
73 Thread
*p
= (Thread
*)pArg
;
77 sqlite3_open(p
->zFilename
, &p
->db
);
78 if( SQLITE_OK
!=sqlite3_errcode(p
->db
) ){
79 p
->zErr
= strdup(sqlite3_errmsg(p
->db
));
86 while( p
->opnum
<=p
->completed
) sched_yield();
89 if( p
->zErr
&& p
->zErr
!=p
->zStaticErr
){
90 sqlite3_free(p
->zErr
);
96 while( p
->opnum
<=p
->completed
) sched_yield();
100 sqlite3_finalize(p
->pStmt
);
104 sqlite3_close(p
->db
);
107 if( p
->zErr
&& p
->zErr
!=p
->zStaticErr
){
108 sqlite3_free(p
->zErr
);
113 #ifndef SQLITE_OMIT_DEPRECATED
114 sqlite3_thread_cleanup();
120 ** Get a thread ID which is an upper case letter. Return the index.
121 ** If the argument is not a valid thread ID put an error message in
122 ** the interpreter and return -1.
124 static int parse_thread_id(Tcl_Interp
*interp
, const char *zArg
){
125 if( zArg
==0 || zArg
[0]==0 || zArg
[1]!=0 || !isupper((unsigned char)zArg
[0]) ){
126 Tcl_AppendResult(interp
, "thread ID must be an upper case letter", 0);
129 return zArg
[0] - 'A';
133 ** Usage: thread_create NAME FILENAME
135 ** NAME should be an upper case letter. Start the thread running with
136 ** an open connection to the given database.
138 static int SQLITE_TCLAPI
tcl_thread_create(
140 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
141 int argc
, /* Number of arguments */
142 const char **argv
/* Text of each argument */
149 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
153 i
= parse_thread_id(interp
, argv
[1]);
154 if( i
<0 ) return TCL_ERROR
;
155 if( threadset
[i
].busy
){
156 Tcl_AppendResult(interp
, "thread ", argv
[1], " is already running", 0);
159 threadset
[i
].busy
= 1;
160 sqlite3_free(threadset
[i
].zFilename
);
161 threadset
[i
].zFilename
= sqlite3_mprintf("%s", argv
[2]);
162 threadset
[i
].opnum
= 1;
163 threadset
[i
].completed
= 0;
164 rc
= pthread_create(&x
, 0, test_thread_main
, &threadset
[i
]);
166 Tcl_AppendResult(interp
, "failed to create the thread", 0);
167 sqlite3_free(threadset
[i
].zFilename
);
168 threadset
[i
].busy
= 0;
176 ** Wait for a thread to reach its idle state.
178 static void test_thread_wait(Thread
*p
){
180 while( p
->opnum
>p
->completed
) sched_yield();
185 ** Usage: thread_wait ID
187 ** Wait on thread ID to reach its idle state.
189 static int SQLITE_TCLAPI
tcl_thread_wait(
191 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
192 int argc
, /* Number of arguments */
193 const char **argv
/* Text of each argument */
198 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
202 i
= parse_thread_id(interp
, argv
[1]);
203 if( i
<0 ) return TCL_ERROR
;
204 if( !threadset
[i
].busy
){
205 Tcl_AppendResult(interp
, "no such thread", 0);
208 test_thread_wait(&threadset
[i
]);
215 static void test_stop_thread(Thread
*p
){
220 sqlite3_free(p
->zArg
);
222 sqlite3_free(p
->zFilename
);
228 ** Usage: thread_halt ID
230 ** Cause a thread to shut itself down. Wait for the shutdown to be
231 ** completed. If ID is "*" then stop all threads.
233 static int SQLITE_TCLAPI
tcl_thread_halt(
235 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
236 int argc
, /* Number of arguments */
237 const char **argv
/* Text of each argument */
242 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
246 if( argv
[1][0]=='*' && argv
[1][1]==0 ){
247 for(i
=0; i
<N_THREAD
; i
++){
248 if( threadset
[i
].busy
) test_stop_thread(&threadset
[i
]);
251 i
= parse_thread_id(interp
, argv
[1]);
252 if( i
<0 ) return TCL_ERROR
;
253 if( !threadset
[i
].busy
){
254 Tcl_AppendResult(interp
, "no such thread", 0);
257 test_stop_thread(&threadset
[i
]);
263 ** Usage: thread_argc ID
265 ** Wait on the most recent thread_step to complete, then return the
266 ** number of columns in the result set.
268 static int SQLITE_TCLAPI
tcl_thread_argc(
270 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
271 int argc
, /* Number of arguments */
272 const char **argv
/* Text of each argument */
278 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
282 i
= parse_thread_id(interp
, argv
[1]);
283 if( i
<0 ) return TCL_ERROR
;
284 if( !threadset
[i
].busy
){
285 Tcl_AppendResult(interp
, "no such thread", 0);
288 test_thread_wait(&threadset
[i
]);
289 sqlite3_snprintf(sizeof(zBuf
), zBuf
, "%d", threadset
[i
].argc
);
290 Tcl_AppendResult(interp
, zBuf
, 0);
295 ** Usage: thread_argv ID N
297 ** Wait on the most recent thread_step to complete, then return the
298 ** value of the N-th columns in the result set.
300 static int SQLITE_TCLAPI
tcl_thread_argv(
302 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
303 int argc
, /* Number of arguments */
304 const char **argv
/* Text of each argument */
310 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
314 i
= parse_thread_id(interp
, argv
[1]);
315 if( i
<0 ) return TCL_ERROR
;
316 if( !threadset
[i
].busy
){
317 Tcl_AppendResult(interp
, "no such thread", 0);
320 if( Tcl_GetInt(interp
, argv
[2], &n
) ) return TCL_ERROR
;
321 test_thread_wait(&threadset
[i
]);
322 if( n
<0 || n
>=threadset
[i
].argc
){
323 Tcl_AppendResult(interp
, "column number out of range", 0);
326 Tcl_AppendResult(interp
, threadset
[i
].argv
[n
], 0);
331 ** Usage: thread_colname ID N
333 ** Wait on the most recent thread_step to complete, then return the
334 ** name of the N-th columns in the result set.
336 static int SQLITE_TCLAPI
tcl_thread_colname(
338 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
339 int argc
, /* Number of arguments */
340 const char **argv
/* Text of each argument */
346 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
350 i
= parse_thread_id(interp
, argv
[1]);
351 if( i
<0 ) return TCL_ERROR
;
352 if( !threadset
[i
].busy
){
353 Tcl_AppendResult(interp
, "no such thread", 0);
356 if( Tcl_GetInt(interp
, argv
[2], &n
) ) return TCL_ERROR
;
357 test_thread_wait(&threadset
[i
]);
358 if( n
<0 || n
>=threadset
[i
].argc
){
359 Tcl_AppendResult(interp
, "column number out of range", 0);
362 Tcl_AppendResult(interp
, threadset
[i
].colv
[n
], 0);
367 ** Usage: thread_result ID
369 ** Wait on the most recent operation to complete, then return the
370 ** result code from that operation.
372 static int SQLITE_TCLAPI
tcl_thread_result(
374 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
375 int argc
, /* Number of arguments */
376 const char **argv
/* Text of each argument */
382 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
386 i
= parse_thread_id(interp
, argv
[1]);
387 if( i
<0 ) return TCL_ERROR
;
388 if( !threadset
[i
].busy
){
389 Tcl_AppendResult(interp
, "no such thread", 0);
392 test_thread_wait(&threadset
[i
]);
393 zName
= sqlite3ErrName(threadset
[i
].rc
);
394 Tcl_AppendResult(interp
, zName
, 0);
399 ** Usage: thread_error ID
401 ** Wait on the most recent operation to complete, then return the
404 static int SQLITE_TCLAPI
tcl_thread_error(
406 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
407 int argc
, /* Number of arguments */
408 const char **argv
/* Text of each argument */
413 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
417 i
= parse_thread_id(interp
, argv
[1]);
418 if( i
<0 ) return TCL_ERROR
;
419 if( !threadset
[i
].busy
){
420 Tcl_AppendResult(interp
, "no such thread", 0);
423 test_thread_wait(&threadset
[i
]);
424 Tcl_AppendResult(interp
, threadset
[i
].zErr
, 0);
429 ** This procedure runs in the thread to compile an SQL statement.
431 static void do_compile(Thread
*p
){
433 p
->zErr
= p
->zStaticErr
= "no database is open";
434 p
->rc
= SQLITE_ERROR
;
438 sqlite3_finalize(p
->pStmt
);
441 p
->rc
= sqlite3_prepare(p
->db
, p
->zArg
, -1, &p
->pStmt
, 0);
445 ** Usage: thread_compile ID SQL
447 ** Compile a new virtual machine.
449 static int SQLITE_TCLAPI
tcl_thread_compile(
451 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
452 int argc
, /* Number of arguments */
453 const char **argv
/* Text of each argument */
457 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
461 i
= parse_thread_id(interp
, argv
[1]);
462 if( i
<0 ) return TCL_ERROR
;
463 if( !threadset
[i
].busy
){
464 Tcl_AppendResult(interp
, "no such thread", 0);
467 test_thread_wait(&threadset
[i
]);
468 threadset
[i
].xOp
= do_compile
;
469 sqlite3_free(threadset
[i
].zArg
);
470 threadset
[i
].zArg
= sqlite3_mprintf("%s", argv
[2]);
472 threadset
[i
].opnum
++;
477 ** This procedure runs in the thread to step the virtual machine.
479 static void do_step(Thread
*p
){
482 p
->zErr
= p
->zStaticErr
= "no virtual machine available";
483 p
->rc
= SQLITE_ERROR
;
486 p
->rc
= sqlite3_step(p
->pStmt
);
487 if( p
->rc
==SQLITE_ROW
){
488 p
->argc
= sqlite3_column_count(p
->pStmt
);
489 for(i
=0; i
<sqlite3_data_count(p
->pStmt
); i
++){
490 p
->argv
[i
] = (char*)sqlite3_column_text(p
->pStmt
, i
);
492 for(i
=0; i
<p
->argc
; i
++){
493 p
->colv
[i
] = sqlite3_column_name(p
->pStmt
, i
);
499 ** Usage: thread_step ID
501 ** Advance the virtual machine by one step
503 static int SQLITE_TCLAPI
tcl_thread_step(
505 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
506 int argc
, /* Number of arguments */
507 const char **argv
/* Text of each argument */
511 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
515 i
= parse_thread_id(interp
, argv
[1]);
516 if( i
<0 ) return TCL_ERROR
;
517 if( !threadset
[i
].busy
){
518 Tcl_AppendResult(interp
, "no such thread", 0);
521 test_thread_wait(&threadset
[i
]);
522 threadset
[i
].xOp
= do_step
;
524 threadset
[i
].opnum
++;
529 ** This procedure runs in the thread to finalize a virtual machine.
531 static void do_finalize(Thread
*p
){
533 p
->zErr
= p
->zStaticErr
= "no virtual machine available";
534 p
->rc
= SQLITE_ERROR
;
537 p
->rc
= sqlite3_finalize(p
->pStmt
);
542 ** Usage: thread_finalize ID
544 ** Finalize the virtual machine.
546 static int SQLITE_TCLAPI
tcl_thread_finalize(
548 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
549 int argc
, /* Number of arguments */
550 const char **argv
/* Text of each argument */
554 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
558 i
= parse_thread_id(interp
, argv
[1]);
559 if( i
<0 ) return TCL_ERROR
;
560 if( !threadset
[i
].busy
){
561 Tcl_AppendResult(interp
, "no such thread", 0);
564 test_thread_wait(&threadset
[i
]);
565 threadset
[i
].xOp
= do_finalize
;
566 sqlite3_free(threadset
[i
].zArg
);
567 threadset
[i
].zArg
= 0;
569 threadset
[i
].opnum
++;
574 ** Usage: thread_swap ID ID
576 ** Interchange the sqlite* pointer between two threads.
578 static int SQLITE_TCLAPI
tcl_thread_swap(
580 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
581 int argc
, /* Number of arguments */
582 const char **argv
/* Text of each argument */
587 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
591 i
= parse_thread_id(interp
, argv
[1]);
592 if( i
<0 ) return TCL_ERROR
;
593 if( !threadset
[i
].busy
){
594 Tcl_AppendResult(interp
, "no such thread", 0);
597 test_thread_wait(&threadset
[i
]);
598 j
= parse_thread_id(interp
, argv
[2]);
599 if( j
<0 ) return TCL_ERROR
;
600 if( !threadset
[j
].busy
){
601 Tcl_AppendResult(interp
, "no such thread", 0);
604 test_thread_wait(&threadset
[j
]);
605 temp
= threadset
[i
].db
;
606 threadset
[i
].db
= threadset
[j
].db
;
607 threadset
[j
].db
= temp
;
612 ** Usage: thread_db_get ID
614 ** Return the database connection pointer for the given thread. Then
615 ** remove the pointer from the thread itself. Afterwards, the thread
616 ** can be stopped and the connection can be used by the main thread.
618 static int SQLITE_TCLAPI
tcl_thread_db_get(
620 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
621 int argc
, /* Number of arguments */
622 const char **argv
/* Text of each argument */
626 extern int sqlite3TestMakePointerStr(Tcl_Interp
*, char*, void*);
628 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
632 i
= parse_thread_id(interp
, argv
[1]);
633 if( i
<0 ) return TCL_ERROR
;
634 if( !threadset
[i
].busy
){
635 Tcl_AppendResult(interp
, "no such thread", 0);
638 test_thread_wait(&threadset
[i
]);
639 sqlite3TestMakePointerStr(interp
, zBuf
, threadset
[i
].db
);
641 Tcl_AppendResult(interp
, zBuf
, (char*)0);
646 ** Usage: thread_db_put ID DB
649 static int SQLITE_TCLAPI
tcl_thread_db_put(
651 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
652 int argc
, /* Number of arguments */
653 const char **argv
/* Text of each argument */
656 extern int sqlite3TestMakePointerStr(Tcl_Interp
*, char*, void*);
657 extern void *sqlite3TestTextToPtr(const char *);
659 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
663 i
= parse_thread_id(interp
, argv
[1]);
664 if( i
<0 ) return TCL_ERROR
;
665 if( !threadset
[i
].busy
){
666 Tcl_AppendResult(interp
, "no such thread", 0);
669 test_thread_wait(&threadset
[i
]);
670 assert( !threadset
[i
].db
);
671 threadset
[i
].db
= (sqlite3
*)sqlite3TestTextToPtr(argv
[2]);
676 ** Usage: thread_stmt_get ID
678 ** Return the database stmt pointer for the given thread. Then
679 ** remove the pointer from the thread itself.
681 static int SQLITE_TCLAPI
tcl_thread_stmt_get(
683 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
684 int argc
, /* Number of arguments */
685 const char **argv
/* Text of each argument */
689 extern int sqlite3TestMakePointerStr(Tcl_Interp
*, char*, void*);
691 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
695 i
= parse_thread_id(interp
, argv
[1]);
696 if( i
<0 ) return TCL_ERROR
;
697 if( !threadset
[i
].busy
){
698 Tcl_AppendResult(interp
, "no such thread", 0);
701 test_thread_wait(&threadset
[i
]);
702 sqlite3TestMakePointerStr(interp
, zBuf
, threadset
[i
].pStmt
);
703 threadset
[i
].pStmt
= 0;
704 Tcl_AppendResult(interp
, zBuf
, (char*)0);
709 ** Register commands with the TCL interpreter.
711 int Sqlitetest4_Init(Tcl_Interp
*interp
){
716 { "thread_create", (Tcl_CmdProc
*)tcl_thread_create
},
717 { "thread_wait", (Tcl_CmdProc
*)tcl_thread_wait
},
718 { "thread_halt", (Tcl_CmdProc
*)tcl_thread_halt
},
719 { "thread_argc", (Tcl_CmdProc
*)tcl_thread_argc
},
720 { "thread_argv", (Tcl_CmdProc
*)tcl_thread_argv
},
721 { "thread_colname", (Tcl_CmdProc
*)tcl_thread_colname
},
722 { "thread_result", (Tcl_CmdProc
*)tcl_thread_result
},
723 { "thread_error", (Tcl_CmdProc
*)tcl_thread_error
},
724 { "thread_compile", (Tcl_CmdProc
*)tcl_thread_compile
},
725 { "thread_step", (Tcl_CmdProc
*)tcl_thread_step
},
726 { "thread_finalize", (Tcl_CmdProc
*)tcl_thread_finalize
},
727 { "thread_swap", (Tcl_CmdProc
*)tcl_thread_swap
},
728 { "thread_db_get", (Tcl_CmdProc
*)tcl_thread_db_get
},
729 { "thread_db_put", (Tcl_CmdProc
*)tcl_thread_db_put
},
730 { "thread_stmt_get", (Tcl_CmdProc
*)tcl_thread_stmt_get
},
734 for(i
=0; i
<sizeof(aCmd
)/sizeof(aCmd
[0]); i
++){
735 Tcl_CreateCommand(interp
, aCmd
[i
].zName
, aCmd
[i
].xProc
, 0, 0);
740 int Sqlitetest4_Init(Tcl_Interp
*interp
){ return TCL_OK
; }
741 #endif /* SQLITE_OS_UNIX */