blob: 2043a338309ef98586035f60ac6107a6efcb38ec [file] [log] [blame]
drha6064dc2003-12-19 02:52:05 +00001/*
2** 2003 December 18
3**
4** The author disclaims copyright to this source code. In place of
5** a legal notice, here is a blessing:
6**
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.
10**
11*************************************************************************
mistachkind5578432012-08-25 10:01:29 +000012** Code for testing the SQLite library in a multithreaded environment.
drha6064dc2003-12-19 02:52:05 +000013*/
14#include "sqliteInt.h"
mistachkin52b1dbb2016-07-28 14:37:04 +000015#if defined(INCLUDE_SQLITE_TCL_H)
16# include "sqlite_tcl.h"
17#else
18# include "tcl.h"
19#endif
shaneh3a2d29f2011-04-04 21:48:01 +000020#if SQLITE_OS_UNIX && SQLITE_THREADSAFE
drha6064dc2003-12-19 02:52:05 +000021#include <stdlib.h>
22#include <string.h>
23#include <pthread.h>
24#include <sched.h>
25#include <ctype.h>
26
mistachkine84d8d32013-04-29 03:09:10 +000027extern const char *sqlite3ErrName(int);
drhd040e762013-04-10 23:48:37 +000028
drha6064dc2003-12-19 02:52:05 +000029/*
30** Each thread is controlled by an instance of the following
31** structure.
32*/
33typedef struct Thread Thread;
34struct Thread {
drh067b92b2020-06-19 15:24:12 +000035 /* The first group of fields are writable by the leader and read-only
drha6064dc2003-12-19 02:52:05 +000036 ** to the thread. */
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 */
42
43 /* The next group of fields are writable by the thread but read-only to the
drh067b92b2020-06-19 15:24:12 +000044 ** leader. */
drha6064dc2003-12-19 02:52:05 +000045 int completed; /* Number of operations completed */
drh9bb575f2004-09-06 17:24:11 +000046 sqlite3 *db; /* Open database */
danielk1977fc57d7b2004-05-26 02:04:57 +000047 sqlite3_stmt *pStmt; /* Pending operation */
drha6064dc2003-12-19 02:52:05 +000048 char *zErr; /* operation error */
49 char *zStaticErr; /* Static error message */
50 int rc; /* operation return code */
51 int argc; /* number of columns in result */
danielk1977fc57d7b2004-05-26 02:04:57 +000052 const char *argv[100]; /* result columns */
53 const char *colv[100]; /* result column names */
drha6064dc2003-12-19 02:52:05 +000054};
55
56/*
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.
59*/
60#define N_THREAD 26
61static Thread threadset[N_THREAD];
62
danc7d7ebd2022-05-16 16:55:22 +000063static void test_barrier(){
64 sqlite3_mutex *pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_APP1);
65 sqlite3_mutex_enter(pMutex);
66 sqlite3_mutex_leave(pMutex);
67}
drha6064dc2003-12-19 02:52:05 +000068
69/*
70** The main loop for a thread. Threads use busy waiting.
71*/
drh0e1a5742019-04-10 12:02:55 +000072static void *test_thread_main(void *pArg){
drha6064dc2003-12-19 02:52:05 +000073 Thread *p = (Thread*)pArg;
74 if( p->db ){
danielk19776f8a5032004-05-10 10:34:51 +000075 sqlite3_close(p->db);
drha6064dc2003-12-19 02:52:05 +000076 }
danielk19774f057f92004-06-08 00:02:33 +000077 sqlite3_open(p->zFilename, &p->db);
danielk197780290862004-05-22 09:21:21 +000078 if( SQLITE_OK!=sqlite3_errcode(p->db) ){
79 p->zErr = strdup(sqlite3_errmsg(p->db));
80 sqlite3_close(p->db);
81 p->db = 0;
82 }
danielk1977fc57d7b2004-05-26 02:04:57 +000083 p->pStmt = 0;
danc7d7ebd2022-05-16 16:55:22 +000084 test_barrier();
drha6064dc2003-12-19 02:52:05 +000085 p->completed = 1;
86 while( p->opnum<=p->completed ) sched_yield();
danc7d7ebd2022-05-16 16:55:22 +000087 test_barrier();
drha6064dc2003-12-19 02:52:05 +000088 while( p->xOp ){
89 if( p->zErr && p->zErr!=p->zStaticErr ){
drh3f4fedb2004-05-31 19:34:33 +000090 sqlite3_free(p->zErr);
drha6064dc2003-12-19 02:52:05 +000091 p->zErr = 0;
92 }
93 (*p->xOp)(p);
danc7d7ebd2022-05-16 16:55:22 +000094 test_barrier();
drha6064dc2003-12-19 02:52:05 +000095 p->completed++;
96 while( p->opnum<=p->completed ) sched_yield();
danc7d7ebd2022-05-16 16:55:22 +000097 test_barrier();
drha6064dc2003-12-19 02:52:05 +000098 }
danielk1977fc57d7b2004-05-26 02:04:57 +000099 if( p->pStmt ){
100 sqlite3_finalize(p->pStmt);
101 p->pStmt = 0;
drha6064dc2003-12-19 02:52:05 +0000102 }
103 if( p->db ){
danielk19776f8a5032004-05-10 10:34:51 +0000104 sqlite3_close(p->db);
drha6064dc2003-12-19 02:52:05 +0000105 p->db = 0;
106 }
107 if( p->zErr && p->zErr!=p->zStaticErr ){
drh3f4fedb2004-05-31 19:34:33 +0000108 sqlite3_free(p->zErr);
drha6064dc2003-12-19 02:52:05 +0000109 p->zErr = 0;
110 }
danc7d7ebd2022-05-16 16:55:22 +0000111 test_barrier();
drha6064dc2003-12-19 02:52:05 +0000112 p->completed++;
shaneeec556d2008-10-12 00:27:53 +0000113#ifndef SQLITE_OMIT_DEPRECATED
drhb4bc7052006-01-11 23:40:33 +0000114 sqlite3_thread_cleanup();
shaneeec556d2008-10-12 00:27:53 +0000115#endif
drha6064dc2003-12-19 02:52:05 +0000116 return 0;
117}
118
119/*
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.
123*/
124static int parse_thread_id(Tcl_Interp *interp, const char *zArg){
drh4c755c02004-08-08 20:22:17 +0000125 if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper((unsigned char)zArg[0]) ){
drha6064dc2003-12-19 02:52:05 +0000126 Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0);
127 return -1;
128 }
129 return zArg[0] - 'A';
130}
131
132/*
133** Usage: thread_create NAME FILENAME
134**
135** NAME should be an upper case letter. Start the thread running with
136** an open connection to the given database.
137*/
mistachkin7617e4a2016-07-28 17:11:20 +0000138static int SQLITE_TCLAPI tcl_thread_create(
drha6064dc2003-12-19 02:52:05 +0000139 void *NotUsed,
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 */
143){
144 int i;
145 pthread_t x;
146 int rc;
147
148 if( argc!=3 ){
149 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
150 " ID FILENAME", 0);
151 return TCL_ERROR;
152 }
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);
157 return TCL_ERROR;
158 }
159 threadset[i].busy = 1;
drhcab5ed72007-08-22 11:41:18 +0000160 sqlite3_free(threadset[i].zFilename);
drhb9755982010-07-24 16:34:37 +0000161 threadset[i].zFilename = sqlite3_mprintf("%s", argv[2]);
drha6064dc2003-12-19 02:52:05 +0000162 threadset[i].opnum = 1;
drh2fd2fa02003-12-20 04:00:52 +0000163 threadset[i].completed = 0;
drh0e1a5742019-04-10 12:02:55 +0000164 rc = pthread_create(&x, 0, test_thread_main, &threadset[i]);
drha6064dc2003-12-19 02:52:05 +0000165 if( rc ){
166 Tcl_AppendResult(interp, "failed to create the thread", 0);
drhcab5ed72007-08-22 11:41:18 +0000167 sqlite3_free(threadset[i].zFilename);
drha6064dc2003-12-19 02:52:05 +0000168 threadset[i].busy = 0;
169 return TCL_ERROR;
170 }
171 pthread_detach(x);
172 return TCL_OK;
173}
174
175/*
176** Wait for a thread to reach its idle state.
177*/
drh0e1a5742019-04-10 12:02:55 +0000178static void test_thread_wait(Thread *p){
danc7d7ebd2022-05-16 16:55:22 +0000179 test_barrier();
drha6064dc2003-12-19 02:52:05 +0000180 while( p->opnum>p->completed ) sched_yield();
danc7d7ebd2022-05-16 16:55:22 +0000181 test_barrier();
drha6064dc2003-12-19 02:52:05 +0000182}
183
184/*
185** Usage: thread_wait ID
186**
187** Wait on thread ID to reach its idle state.
188*/
mistachkin7617e4a2016-07-28 17:11:20 +0000189static int SQLITE_TCLAPI tcl_thread_wait(
drha6064dc2003-12-19 02:52:05 +0000190 void *NotUsed,
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 */
194){
195 int i;
196
197 if( argc!=2 ){
198 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
199 " ID", 0);
200 return TCL_ERROR;
201 }
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);
206 return TCL_ERROR;
207 }
drh0e1a5742019-04-10 12:02:55 +0000208 test_thread_wait(&threadset[i]);
drha6064dc2003-12-19 02:52:05 +0000209 return TCL_OK;
210}
211
212/*
213** Stop a thread.
214*/
drh0e1a5742019-04-10 12:02:55 +0000215static void test_stop_thread(Thread *p){
216 test_thread_wait(p);
drha6064dc2003-12-19 02:52:05 +0000217 p->xOp = 0;
218 p->opnum++;
drh0e1a5742019-04-10 12:02:55 +0000219 test_thread_wait(p);
drhcab5ed72007-08-22 11:41:18 +0000220 sqlite3_free(p->zArg);
drha6064dc2003-12-19 02:52:05 +0000221 p->zArg = 0;
drhcab5ed72007-08-22 11:41:18 +0000222 sqlite3_free(p->zFilename);
drha6064dc2003-12-19 02:52:05 +0000223 p->zFilename = 0;
224 p->busy = 0;
225}
226
227/*
228** Usage: thread_halt ID
229**
230** Cause a thread to shut itself down. Wait for the shutdown to be
231** completed. If ID is "*" then stop all threads.
232*/
mistachkin7617e4a2016-07-28 17:11:20 +0000233static int SQLITE_TCLAPI tcl_thread_halt(
drha6064dc2003-12-19 02:52:05 +0000234 void *NotUsed,
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 */
238){
239 int i;
240
241 if( argc!=2 ){
242 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
243 " ID", 0);
244 return TCL_ERROR;
245 }
246 if( argv[1][0]=='*' && argv[1][1]==0 ){
247 for(i=0; i<N_THREAD; i++){
drh0e1a5742019-04-10 12:02:55 +0000248 if( threadset[i].busy ) test_stop_thread(&threadset[i]);
drha6064dc2003-12-19 02:52:05 +0000249 }
250 }else{
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);
255 return TCL_ERROR;
256 }
drh0e1a5742019-04-10 12:02:55 +0000257 test_stop_thread(&threadset[i]);
drha6064dc2003-12-19 02:52:05 +0000258 }
259 return TCL_OK;
260}
261
262/*
263** Usage: thread_argc ID
264**
265** Wait on the most recent thread_step to complete, then return the
266** number of columns in the result set.
267*/
mistachkin7617e4a2016-07-28 17:11:20 +0000268static int SQLITE_TCLAPI tcl_thread_argc(
drha6064dc2003-12-19 02:52:05 +0000269 void *NotUsed,
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 */
273){
274 int i;
275 char zBuf[100];
276
277 if( argc!=2 ){
278 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
279 " ID", 0);
280 return TCL_ERROR;
281 }
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);
286 return TCL_ERROR;
287 }
drh0e1a5742019-04-10 12:02:55 +0000288 test_thread_wait(&threadset[i]);
drh65545b52015-01-19 00:35:53 +0000289 sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", threadset[i].argc);
drha6064dc2003-12-19 02:52:05 +0000290 Tcl_AppendResult(interp, zBuf, 0);
291 return TCL_OK;
292}
293
294/*
295** Usage: thread_argv ID N
296**
297** Wait on the most recent thread_step to complete, then return the
298** value of the N-th columns in the result set.
299*/
mistachkin7617e4a2016-07-28 17:11:20 +0000300static int SQLITE_TCLAPI tcl_thread_argv(
drha6064dc2003-12-19 02:52:05 +0000301 void *NotUsed,
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 */
305){
306 int i;
307 int n;
308
309 if( argc!=3 ){
310 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
311 " ID N", 0);
312 return TCL_ERROR;
313 }
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);
318 return TCL_ERROR;
319 }
320 if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
drh0e1a5742019-04-10 12:02:55 +0000321 test_thread_wait(&threadset[i]);
drha6064dc2003-12-19 02:52:05 +0000322 if( n<0 || n>=threadset[i].argc ){
323 Tcl_AppendResult(interp, "column number out of range", 0);
324 return TCL_ERROR;
325 }
326 Tcl_AppendResult(interp, threadset[i].argv[n], 0);
327 return TCL_OK;
328}
329
330/*
331** Usage: thread_colname ID N
332**
333** Wait on the most recent thread_step to complete, then return the
334** name of the N-th columns in the result set.
335*/
mistachkin7617e4a2016-07-28 17:11:20 +0000336static int SQLITE_TCLAPI tcl_thread_colname(
drha6064dc2003-12-19 02:52:05 +0000337 void *NotUsed,
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 */
341){
342 int i;
343 int n;
344
345 if( argc!=3 ){
346 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
347 " ID N", 0);
348 return TCL_ERROR;
349 }
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);
354 return TCL_ERROR;
355 }
356 if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
drh0e1a5742019-04-10 12:02:55 +0000357 test_thread_wait(&threadset[i]);
drha6064dc2003-12-19 02:52:05 +0000358 if( n<0 || n>=threadset[i].argc ){
359 Tcl_AppendResult(interp, "column number out of range", 0);
360 return TCL_ERROR;
361 }
362 Tcl_AppendResult(interp, threadset[i].colv[n], 0);
363 return TCL_OK;
364}
365
366/*
367** Usage: thread_result ID
368**
369** Wait on the most recent operation to complete, then return the
370** result code from that operation.
371*/
mistachkin7617e4a2016-07-28 17:11:20 +0000372static int SQLITE_TCLAPI tcl_thread_result(
drha6064dc2003-12-19 02:52:05 +0000373 void *NotUsed,
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 */
377){
378 int i;
379 const char *zName;
380
381 if( argc!=2 ){
382 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
383 " ID", 0);
384 return TCL_ERROR;
385 }
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);
390 return TCL_ERROR;
391 }
drh0e1a5742019-04-10 12:02:55 +0000392 test_thread_wait(&threadset[i]);
mistachkine84d8d32013-04-29 03:09:10 +0000393 zName = sqlite3ErrName(threadset[i].rc);
drha6064dc2003-12-19 02:52:05 +0000394 Tcl_AppendResult(interp, zName, 0);
395 return TCL_OK;
396}
397
398/*
399** Usage: thread_error ID
400**
401** Wait on the most recent operation to complete, then return the
402** error string.
403*/
mistachkin7617e4a2016-07-28 17:11:20 +0000404static int SQLITE_TCLAPI tcl_thread_error(
drha6064dc2003-12-19 02:52:05 +0000405 void *NotUsed,
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 */
409){
410 int i;
411
412 if( argc!=2 ){
413 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
414 " ID", 0);
415 return TCL_ERROR;
416 }
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);
421 return TCL_ERROR;
422 }
drh0e1a5742019-04-10 12:02:55 +0000423 test_thread_wait(&threadset[i]);
drha6064dc2003-12-19 02:52:05 +0000424 Tcl_AppendResult(interp, threadset[i].zErr, 0);
425 return TCL_OK;
426}
427
428/*
429** This procedure runs in the thread to compile an SQL statement.
430*/
431static void do_compile(Thread *p){
432 if( p->db==0 ){
433 p->zErr = p->zStaticErr = "no database is open";
434 p->rc = SQLITE_ERROR;
435 return;
436 }
danielk1977fc57d7b2004-05-26 02:04:57 +0000437 if( p->pStmt ){
438 sqlite3_finalize(p->pStmt);
439 p->pStmt = 0;
drha6064dc2003-12-19 02:52:05 +0000440 }
danielk1977fc57d7b2004-05-26 02:04:57 +0000441 p->rc = sqlite3_prepare(p->db, p->zArg, -1, &p->pStmt, 0);
drha6064dc2003-12-19 02:52:05 +0000442}
443
444/*
445** Usage: thread_compile ID SQL
446**
447** Compile a new virtual machine.
448*/
mistachkin7617e4a2016-07-28 17:11:20 +0000449static int SQLITE_TCLAPI tcl_thread_compile(
drha6064dc2003-12-19 02:52:05 +0000450 void *NotUsed,
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 */
454){
455 int i;
456 if( argc!=3 ){
457 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
458 " ID SQL", 0);
459 return TCL_ERROR;
460 }
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);
465 return TCL_ERROR;
466 }
drh0e1a5742019-04-10 12:02:55 +0000467 test_thread_wait(&threadset[i]);
drha6064dc2003-12-19 02:52:05 +0000468 threadset[i].xOp = do_compile;
drhcab5ed72007-08-22 11:41:18 +0000469 sqlite3_free(threadset[i].zArg);
drhb9755982010-07-24 16:34:37 +0000470 threadset[i].zArg = sqlite3_mprintf("%s", argv[2]);
danc7d7ebd2022-05-16 16:55:22 +0000471 test_barrier();
drha6064dc2003-12-19 02:52:05 +0000472 threadset[i].opnum++;
473 return TCL_OK;
474}
475
476/*
477** This procedure runs in the thread to step the virtual machine.
478*/
479static void do_step(Thread *p){
danielk1977fc57d7b2004-05-26 02:04:57 +0000480 int i;
481 if( p->pStmt==0 ){
drha6064dc2003-12-19 02:52:05 +0000482 p->zErr = p->zStaticErr = "no virtual machine available";
483 p->rc = SQLITE_ERROR;
484 return;
485 }
danielk1977fc57d7b2004-05-26 02:04:57 +0000486 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++){
drh24bd82c2006-01-20 17:56:32 +0000490 p->argv[i] = (char*)sqlite3_column_text(p->pStmt, i);
danielk1977fc57d7b2004-05-26 02:04:57 +0000491 }
492 for(i=0; i<p->argc; i++){
493 p->colv[i] = sqlite3_column_name(p->pStmt, i);
494 }
495 }
drha6064dc2003-12-19 02:52:05 +0000496}
497
498/*
499** Usage: thread_step ID
500**
501** Advance the virtual machine by one step
502*/
mistachkin7617e4a2016-07-28 17:11:20 +0000503static int SQLITE_TCLAPI tcl_thread_step(
drha6064dc2003-12-19 02:52:05 +0000504 void *NotUsed,
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 */
508){
509 int i;
510 if( argc!=2 ){
511 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
512 " IDL", 0);
513 return TCL_ERROR;
514 }
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);
519 return TCL_ERROR;
520 }
drh0e1a5742019-04-10 12:02:55 +0000521 test_thread_wait(&threadset[i]);
drha6064dc2003-12-19 02:52:05 +0000522 threadset[i].xOp = do_step;
danc7d7ebd2022-05-16 16:55:22 +0000523 test_barrier();
drha6064dc2003-12-19 02:52:05 +0000524 threadset[i].opnum++;
525 return TCL_OK;
526}
527
528/*
529** This procedure runs in the thread to finalize a virtual machine.
530*/
531static void do_finalize(Thread *p){
danielk1977fc57d7b2004-05-26 02:04:57 +0000532 if( p->pStmt==0 ){
drha6064dc2003-12-19 02:52:05 +0000533 p->zErr = p->zStaticErr = "no virtual machine available";
534 p->rc = SQLITE_ERROR;
535 return;
536 }
danielk1977fc57d7b2004-05-26 02:04:57 +0000537 p->rc = sqlite3_finalize(p->pStmt);
538 p->pStmt = 0;
drha6064dc2003-12-19 02:52:05 +0000539}
540
541/*
542** Usage: thread_finalize ID
543**
544** Finalize the virtual machine.
545*/
mistachkin7617e4a2016-07-28 17:11:20 +0000546static int SQLITE_TCLAPI tcl_thread_finalize(
drha6064dc2003-12-19 02:52:05 +0000547 void *NotUsed,
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 */
551){
552 int i;
553 if( argc!=2 ){
554 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
555 " IDL", 0);
556 return TCL_ERROR;
557 }
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);
562 return TCL_ERROR;
563 }
drh0e1a5742019-04-10 12:02:55 +0000564 test_thread_wait(&threadset[i]);
drha6064dc2003-12-19 02:52:05 +0000565 threadset[i].xOp = do_finalize;
drhcab5ed72007-08-22 11:41:18 +0000566 sqlite3_free(threadset[i].zArg);
drha6064dc2003-12-19 02:52:05 +0000567 threadset[i].zArg = 0;
danc7d7ebd2022-05-16 16:55:22 +0000568 test_barrier();
drha6064dc2003-12-19 02:52:05 +0000569 threadset[i].opnum++;
570 return TCL_OK;
571}
572
573/*
drh1bdd9b52004-04-23 17:04:44 +0000574** Usage: thread_swap ID ID
575**
576** Interchange the sqlite* pointer between two threads.
577*/
mistachkin7617e4a2016-07-28 17:11:20 +0000578static int SQLITE_TCLAPI tcl_thread_swap(
drh1bdd9b52004-04-23 17:04:44 +0000579 void *NotUsed,
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 */
583){
584 int i, j;
drh9bb575f2004-09-06 17:24:11 +0000585 sqlite3 *temp;
drh1bdd9b52004-04-23 17:04:44 +0000586 if( argc!=3 ){
587 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
588 " ID1 ID2", 0);
589 return TCL_ERROR;
590 }
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);
595 return TCL_ERROR;
596 }
drh0e1a5742019-04-10 12:02:55 +0000597 test_thread_wait(&threadset[i]);
drh1bdd9b52004-04-23 17:04:44 +0000598 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);
602 return TCL_ERROR;
603 }
drh0e1a5742019-04-10 12:02:55 +0000604 test_thread_wait(&threadset[j]);
drh1bdd9b52004-04-23 17:04:44 +0000605 temp = threadset[i].db;
606 threadset[i].db = threadset[j].db;
607 threadset[j].db = temp;
608 return TCL_OK;
609}
610
611/*
drh64b1bea2006-01-15 02:30:57 +0000612** Usage: thread_db_get ID
613**
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.
617*/
mistachkin7617e4a2016-07-28 17:11:20 +0000618static int SQLITE_TCLAPI tcl_thread_db_get(
drh64b1bea2006-01-15 02:30:57 +0000619 void *NotUsed,
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 */
623){
624 int i;
625 char zBuf[100];
626 extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
627 if( argc!=2 ){
628 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
629 " ID", 0);
630 return TCL_ERROR;
631 }
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);
636 return TCL_ERROR;
637 }
drh0e1a5742019-04-10 12:02:55 +0000638 test_thread_wait(&threadset[i]);
drh64b1bea2006-01-15 02:30:57 +0000639 sqlite3TestMakePointerStr(interp, zBuf, threadset[i].db);
640 threadset[i].db = 0;
drh52fc8492006-02-23 21:43:55 +0000641 Tcl_AppendResult(interp, zBuf, (char*)0);
drh64b1bea2006-01-15 02:30:57 +0000642 return TCL_OK;
643}
644
645/*
dan11b38792009-09-09 18:46:52 +0000646** Usage: thread_db_put ID DB
647**
648*/
mistachkin7617e4a2016-07-28 17:11:20 +0000649static int SQLITE_TCLAPI tcl_thread_db_put(
dan11b38792009-09-09 18:46:52 +0000650 void *NotUsed,
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 */
654){
655 int i;
656 extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
657 extern void *sqlite3TestTextToPtr(const char *);
658 if( argc!=3 ){
659 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
660 " ID DB", 0);
661 return TCL_ERROR;
662 }
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);
667 return TCL_ERROR;
668 }
drh0e1a5742019-04-10 12:02:55 +0000669 test_thread_wait(&threadset[i]);
dan11b38792009-09-09 18:46:52 +0000670 assert( !threadset[i].db );
671 threadset[i].db = (sqlite3*)sqlite3TestTextToPtr(argv[2]);
672 return TCL_OK;
673}
674
675/*
drh64b1bea2006-01-15 02:30:57 +0000676** Usage: thread_stmt_get ID
677**
678** Return the database stmt pointer for the given thread. Then
679** remove the pointer from the thread itself.
680*/
mistachkin7617e4a2016-07-28 17:11:20 +0000681static int SQLITE_TCLAPI tcl_thread_stmt_get(
drh64b1bea2006-01-15 02:30:57 +0000682 void *NotUsed,
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 */
686){
687 int i;
688 char zBuf[100];
689 extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
690 if( argc!=2 ){
691 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
692 " ID", 0);
693 return TCL_ERROR;
694 }
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);
699 return TCL_ERROR;
700 }
drh0e1a5742019-04-10 12:02:55 +0000701 test_thread_wait(&threadset[i]);
drh64b1bea2006-01-15 02:30:57 +0000702 sqlite3TestMakePointerStr(interp, zBuf, threadset[i].pStmt);
703 threadset[i].pStmt = 0;
drh52fc8492006-02-23 21:43:55 +0000704 Tcl_AppendResult(interp, zBuf, (char*)0);
drh64b1bea2006-01-15 02:30:57 +0000705 return TCL_OK;
706}
707
708/*
drha6064dc2003-12-19 02:52:05 +0000709** Register commands with the TCL interpreter.
710*/
711int Sqlitetest4_Init(Tcl_Interp *interp){
712 static struct {
713 char *zName;
714 Tcl_CmdProc *xProc;
715 } aCmd[] = {
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 },
drh1bdd9b52004-04-23 17:04:44 +0000727 { "thread_swap", (Tcl_CmdProc*)tcl_thread_swap },
drh64b1bea2006-01-15 02:30:57 +0000728 { "thread_db_get", (Tcl_CmdProc*)tcl_thread_db_get },
dan11b38792009-09-09 18:46:52 +0000729 { "thread_db_put", (Tcl_CmdProc*)tcl_thread_db_put },
drh64b1bea2006-01-15 02:30:57 +0000730 { "thread_stmt_get", (Tcl_CmdProc*)tcl_thread_stmt_get },
drha6064dc2003-12-19 02:52:05 +0000731 };
732 int i;
733
734 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
735 Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
736 }
737 return TCL_OK;
738}
739#else
740int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; }
danielk197729bafea2008-06-26 10:41:19 +0000741#endif /* SQLITE_OS_UNIX */