xref: /sqlite-3.40.0/src/test4.c (revision b4bc7057)
1 /*
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 *************************************************************************
12 ** Code for testing the the SQLite library in a multithreaded environment.
13 **
14 ** $Id: test4.c,v 1.14 2006/01/11 23:40:34 drh Exp $
15 */
16 #include "sqliteInt.h"
17 #include "tcl.h"
18 #include "os.h"
19 #if defined(OS_UNIX) && OS_UNIX==1 && defined(THREADSAFE) && THREADSAFE==1
20 #include <stdlib.h>
21 #include <string.h>
22 #include <pthread.h>
23 #include <sched.h>
24 #include <ctype.h>
25 
26 /*
27 ** Each thread is controlled by an instance of the following
28 ** structure.
29 */
30 typedef struct Thread Thread;
31 struct Thread {
32   /* The first group of fields are writable by the master and read-only
33   ** to the thread. */
34   char *zFilename;       /* Name of database file */
35   void (*xOp)(Thread*);  /* next operation to do */
36   char *zArg;            /* argument usable by xOp */
37   int opnum;             /* Operation number */
38   int busy;              /* True if this thread is in use */
39 
40   /* The next group of fields are writable by the thread but read-only to the
41   ** master. */
42   int completed;        /* Number of operations completed */
43   sqlite3 *db;           /* Open database */
44   sqlite3_stmt *pStmt;     /* Pending operation */
45   char *zErr;           /* operation error */
46   char *zStaticErr;     /* Static error message */
47   int rc;               /* operation return code */
48   int argc;             /* number of columns in result */
49   const char *argv[100];    /* result columns */
50   const char *colv[100];    /* result column names */
51 };
52 
53 /*
54 ** There can be as many as 26 threads running at once.  Each is named
55 ** by a capital letter: A, B, C, ..., Y, Z.
56 */
57 #define N_THREAD 26
58 static Thread threadset[N_THREAD];
59 
60 
61 /*
62 ** The main loop for a thread.  Threads use busy waiting.
63 */
64 static void *thread_main(void *pArg){
65   Thread *p = (Thread*)pArg;
66   if( p->db ){
67     sqlite3_close(p->db);
68   }
69   sqlite3_open(p->zFilename, &p->db);
70   if( SQLITE_OK!=sqlite3_errcode(p->db) ){
71     p->zErr = strdup(sqlite3_errmsg(p->db));
72     sqlite3_close(p->db);
73     p->db = 0;
74   }
75   p->pStmt = 0;
76   p->completed = 1;
77   while( p->opnum<=p->completed ) sched_yield();
78   while( p->xOp ){
79     if( p->zErr && p->zErr!=p->zStaticErr ){
80       sqlite3_free(p->zErr);
81       p->zErr = 0;
82     }
83     (*p->xOp)(p);
84     p->completed++;
85     while( p->opnum<=p->completed ) sched_yield();
86   }
87   if( p->pStmt ){
88     sqlite3_finalize(p->pStmt);
89     p->pStmt = 0;
90   }
91   if( p->db ){
92     sqlite3_close(p->db);
93     p->db = 0;
94   }
95   if( p->zErr && p->zErr!=p->zStaticErr ){
96     sqlite3_free(p->zErr);
97     p->zErr = 0;
98   }
99   p->completed++;
100   sqlite3_thread_cleanup();
101   return 0;
102 }
103 
104 /*
105 ** Get a thread ID which is an upper case letter.  Return the index.
106 ** If the argument is not a valid thread ID put an error message in
107 ** the interpreter and return -1.
108 */
109 static int parse_thread_id(Tcl_Interp *interp, const char *zArg){
110   if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper((unsigned char)zArg[0]) ){
111     Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0);
112     return -1;
113   }
114   return zArg[0] - 'A';
115 }
116 
117 /*
118 ** Usage:    thread_create NAME  FILENAME
119 **
120 ** NAME should be an upper case letter.  Start the thread running with
121 ** an open connection to the given database.
122 */
123 static int tcl_thread_create(
124   void *NotUsed,
125   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
126   int argc,              /* Number of arguments */
127   const char **argv      /* Text of each argument */
128 ){
129   int i;
130   pthread_t x;
131   int rc;
132 
133   if( argc!=3 ){
134     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
135        " ID FILENAME", 0);
136     return TCL_ERROR;
137   }
138   i = parse_thread_id(interp, argv[1]);
139   if( i<0 ) return TCL_ERROR;
140   if( threadset[i].busy ){
141     Tcl_AppendResult(interp, "thread ", argv[1], " is already running", 0);
142     return TCL_ERROR;
143   }
144   threadset[i].busy = 1;
145   sqliteFree(threadset[i].zFilename);
146   threadset[i].zFilename = sqliteStrDup(argv[2]);
147   threadset[i].opnum = 1;
148   threadset[i].completed = 0;
149   rc = pthread_create(&x, 0, thread_main, &threadset[i]);
150   if( rc ){
151     Tcl_AppendResult(interp, "failed to create the thread", 0);
152     sqliteFree(threadset[i].zFilename);
153     threadset[i].busy = 0;
154     return TCL_ERROR;
155   }
156   pthread_detach(x);
157   return TCL_OK;
158 }
159 
160 /*
161 ** Wait for a thread to reach its idle state.
162 */
163 static void thread_wait(Thread *p){
164   while( p->opnum>p->completed ) sched_yield();
165 }
166 
167 /*
168 ** Usage:  thread_wait ID
169 **
170 ** Wait on thread ID to reach its idle state.
171 */
172 static int tcl_thread_wait(
173   void *NotUsed,
174   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
175   int argc,              /* Number of arguments */
176   const char **argv      /* Text of each argument */
177 ){
178   int i;
179 
180   if( argc!=2 ){
181     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
182        " ID", 0);
183     return TCL_ERROR;
184   }
185   i = parse_thread_id(interp, argv[1]);
186   if( i<0 ) return TCL_ERROR;
187   if( !threadset[i].busy ){
188     Tcl_AppendResult(interp, "no such thread", 0);
189     return TCL_ERROR;
190   }
191   thread_wait(&threadset[i]);
192   return TCL_OK;
193 }
194 
195 /*
196 ** Stop a thread.
197 */
198 static void stop_thread(Thread *p){
199   thread_wait(p);
200   p->xOp = 0;
201   p->opnum++;
202   thread_wait(p);
203   sqliteFree(p->zArg);
204   p->zArg = 0;
205   sqliteFree(p->zFilename);
206   p->zFilename = 0;
207   p->busy = 0;
208 }
209 
210 /*
211 ** Usage:  thread_halt ID
212 **
213 ** Cause a thread to shut itself down.  Wait for the shutdown to be
214 ** completed.  If ID is "*" then stop all threads.
215 */
216 static int tcl_thread_halt(
217   void *NotUsed,
218   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
219   int argc,              /* Number of arguments */
220   const char **argv      /* Text of each argument */
221 ){
222   int i;
223 
224   if( argc!=2 ){
225     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
226        " ID", 0);
227     return TCL_ERROR;
228   }
229   if( argv[1][0]=='*' && argv[1][1]==0 ){
230     for(i=0; i<N_THREAD; i++){
231       if( threadset[i].busy ) stop_thread(&threadset[i]);
232     }
233   }else{
234     i = parse_thread_id(interp, argv[1]);
235     if( i<0 ) return TCL_ERROR;
236     if( !threadset[i].busy ){
237       Tcl_AppendResult(interp, "no such thread", 0);
238       return TCL_ERROR;
239     }
240     stop_thread(&threadset[i]);
241   }
242   return TCL_OK;
243 }
244 
245 /*
246 ** Usage: thread_argc  ID
247 **
248 ** Wait on the most recent thread_step to complete, then return the
249 ** number of columns in the result set.
250 */
251 static int tcl_thread_argc(
252   void *NotUsed,
253   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
254   int argc,              /* Number of arguments */
255   const char **argv      /* Text of each argument */
256 ){
257   int i;
258   char zBuf[100];
259 
260   if( argc!=2 ){
261     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
262        " ID", 0);
263     return TCL_ERROR;
264   }
265   i = parse_thread_id(interp, argv[1]);
266   if( i<0 ) return TCL_ERROR;
267   if( !threadset[i].busy ){
268     Tcl_AppendResult(interp, "no such thread", 0);
269     return TCL_ERROR;
270   }
271   thread_wait(&threadset[i]);
272   sprintf(zBuf, "%d", threadset[i].argc);
273   Tcl_AppendResult(interp, zBuf, 0);
274   return TCL_OK;
275 }
276 
277 /*
278 ** Usage: thread_argv  ID   N
279 **
280 ** Wait on the most recent thread_step to complete, then return the
281 ** value of the N-th columns in the result set.
282 */
283 static int tcl_thread_argv(
284   void *NotUsed,
285   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
286   int argc,              /* Number of arguments */
287   const char **argv      /* Text of each argument */
288 ){
289   int i;
290   int n;
291 
292   if( argc!=3 ){
293     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
294        " ID N", 0);
295     return TCL_ERROR;
296   }
297   i = parse_thread_id(interp, argv[1]);
298   if( i<0 ) return TCL_ERROR;
299   if( !threadset[i].busy ){
300     Tcl_AppendResult(interp, "no such thread", 0);
301     return TCL_ERROR;
302   }
303   if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
304   thread_wait(&threadset[i]);
305   if( n<0 || n>=threadset[i].argc ){
306     Tcl_AppendResult(interp, "column number out of range", 0);
307     return TCL_ERROR;
308   }
309   Tcl_AppendResult(interp, threadset[i].argv[n], 0);
310   return TCL_OK;
311 }
312 
313 /*
314 ** Usage: thread_colname  ID   N
315 **
316 ** Wait on the most recent thread_step to complete, then return the
317 ** name of the N-th columns in the result set.
318 */
319 static int tcl_thread_colname(
320   void *NotUsed,
321   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
322   int argc,              /* Number of arguments */
323   const char **argv      /* Text of each argument */
324 ){
325   int i;
326   int n;
327 
328   if( argc!=3 ){
329     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
330        " ID N", 0);
331     return TCL_ERROR;
332   }
333   i = parse_thread_id(interp, argv[1]);
334   if( i<0 ) return TCL_ERROR;
335   if( !threadset[i].busy ){
336     Tcl_AppendResult(interp, "no such thread", 0);
337     return TCL_ERROR;
338   }
339   if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
340   thread_wait(&threadset[i]);
341   if( n<0 || n>=threadset[i].argc ){
342     Tcl_AppendResult(interp, "column number out of range", 0);
343     return TCL_ERROR;
344   }
345   Tcl_AppendResult(interp, threadset[i].colv[n], 0);
346   return TCL_OK;
347 }
348 
349 /*
350 ** Usage: thread_result  ID
351 **
352 ** Wait on the most recent operation to complete, then return the
353 ** result code from that operation.
354 */
355 static int tcl_thread_result(
356   void *NotUsed,
357   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
358   int argc,              /* Number of arguments */
359   const char **argv      /* Text of each argument */
360 ){
361   int i;
362   const char *zName;
363 
364   if( argc!=2 ){
365     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
366        " ID", 0);
367     return TCL_ERROR;
368   }
369   i = parse_thread_id(interp, argv[1]);
370   if( i<0 ) return TCL_ERROR;
371   if( !threadset[i].busy ){
372     Tcl_AppendResult(interp, "no such thread", 0);
373     return TCL_ERROR;
374   }
375   thread_wait(&threadset[i]);
376   switch( threadset[i].rc ){
377     case SQLITE_OK:         zName = "SQLITE_OK";          break;
378     case SQLITE_ERROR:      zName = "SQLITE_ERROR";       break;
379     case SQLITE_PERM:       zName = "SQLITE_PERM";        break;
380     case SQLITE_ABORT:      zName = "SQLITE_ABORT";       break;
381     case SQLITE_BUSY:       zName = "SQLITE_BUSY";        break;
382     case SQLITE_LOCKED:     zName = "SQLITE_LOCKED";      break;
383     case SQLITE_NOMEM:      zName = "SQLITE_NOMEM";       break;
384     case SQLITE_READONLY:   zName = "SQLITE_READONLY";    break;
385     case SQLITE_INTERRUPT:  zName = "SQLITE_INTERRUPT";   break;
386     case SQLITE_IOERR:      zName = "SQLITE_IOERR";       break;
387     case SQLITE_CORRUPT:    zName = "SQLITE_CORRUPT";     break;
388     case SQLITE_FULL:       zName = "SQLITE_FULL";        break;
389     case SQLITE_CANTOPEN:   zName = "SQLITE_CANTOPEN";    break;
390     case SQLITE_PROTOCOL:   zName = "SQLITE_PROTOCOL";    break;
391     case SQLITE_EMPTY:      zName = "SQLITE_EMPTY";       break;
392     case SQLITE_SCHEMA:     zName = "SQLITE_SCHEMA";      break;
393     case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT";  break;
394     case SQLITE_MISMATCH:   zName = "SQLITE_MISMATCH";    break;
395     case SQLITE_MISUSE:     zName = "SQLITE_MISUSE";      break;
396     case SQLITE_NOLFS:      zName = "SQLITE_NOLFS";       break;
397     case SQLITE_AUTH:       zName = "SQLITE_AUTH";        break;
398     case SQLITE_FORMAT:     zName = "SQLITE_FORMAT";      break;
399     case SQLITE_RANGE:      zName = "SQLITE_RANGE";       break;
400     case SQLITE_ROW:        zName = "SQLITE_ROW";         break;
401     case SQLITE_DONE:       zName = "SQLITE_DONE";        break;
402     default:                zName = "SQLITE_Unknown";     break;
403   }
404   Tcl_AppendResult(interp, zName, 0);
405   return TCL_OK;
406 }
407 
408 /*
409 ** Usage: thread_error  ID
410 **
411 ** Wait on the most recent operation to complete, then return the
412 ** error string.
413 */
414 static int tcl_thread_error(
415   void *NotUsed,
416   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
417   int argc,              /* Number of arguments */
418   const char **argv      /* Text of each argument */
419 ){
420   int i;
421 
422   if( argc!=2 ){
423     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
424        " ID", 0);
425     return TCL_ERROR;
426   }
427   i = parse_thread_id(interp, argv[1]);
428   if( i<0 ) return TCL_ERROR;
429   if( !threadset[i].busy ){
430     Tcl_AppendResult(interp, "no such thread", 0);
431     return TCL_ERROR;
432   }
433   thread_wait(&threadset[i]);
434   Tcl_AppendResult(interp, threadset[i].zErr, 0);
435   return TCL_OK;
436 }
437 
438 /*
439 ** This procedure runs in the thread to compile an SQL statement.
440 */
441 static void do_compile(Thread *p){
442   if( p->db==0 ){
443     p->zErr = p->zStaticErr = "no database is open";
444     p->rc = SQLITE_ERROR;
445     return;
446   }
447   if( p->pStmt ){
448     sqlite3_finalize(p->pStmt);
449     p->pStmt = 0;
450   }
451   p->rc = sqlite3_prepare(p->db, p->zArg, -1, &p->pStmt, 0);
452 }
453 
454 /*
455 ** Usage: thread_compile ID SQL
456 **
457 ** Compile a new virtual machine.
458 */
459 static int tcl_thread_compile(
460   void *NotUsed,
461   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
462   int argc,              /* Number of arguments */
463   const char **argv      /* Text of each argument */
464 ){
465   int i;
466   if( argc!=3 ){
467     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
468        " ID SQL", 0);
469     return TCL_ERROR;
470   }
471   i = parse_thread_id(interp, argv[1]);
472   if( i<0 ) return TCL_ERROR;
473   if( !threadset[i].busy ){
474     Tcl_AppendResult(interp, "no such thread", 0);
475     return TCL_ERROR;
476   }
477   thread_wait(&threadset[i]);
478   threadset[i].xOp = do_compile;
479   sqliteFree(threadset[i].zArg);
480   threadset[i].zArg = sqliteStrDup(argv[2]);
481   threadset[i].opnum++;
482   return TCL_OK;
483 }
484 
485 /*
486 ** This procedure runs in the thread to step the virtual machine.
487 */
488 static void do_step(Thread *p){
489   int i;
490   if( p->pStmt==0 ){
491     p->zErr = p->zStaticErr = "no virtual machine available";
492     p->rc = SQLITE_ERROR;
493     return;
494   }
495   p->rc = sqlite3_step(p->pStmt);
496   if( p->rc==SQLITE_ROW ){
497     p->argc = sqlite3_column_count(p->pStmt);
498     for(i=0; i<sqlite3_data_count(p->pStmt); i++){
499       p->argv[i] = sqlite3_column_text(p->pStmt, i);
500     }
501     for(i=0; i<p->argc; i++){
502       p->colv[i] = sqlite3_column_name(p->pStmt, i);
503     }
504   }
505 }
506 
507 /*
508 ** Usage: thread_step ID
509 **
510 ** Advance the virtual machine by one step
511 */
512 static int tcl_thread_step(
513   void *NotUsed,
514   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
515   int argc,              /* Number of arguments */
516   const char **argv      /* Text of each argument */
517 ){
518   int i;
519   if( argc!=2 ){
520     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
521        " IDL", 0);
522     return TCL_ERROR;
523   }
524   i = parse_thread_id(interp, argv[1]);
525   if( i<0 ) return TCL_ERROR;
526   if( !threadset[i].busy ){
527     Tcl_AppendResult(interp, "no such thread", 0);
528     return TCL_ERROR;
529   }
530   thread_wait(&threadset[i]);
531   threadset[i].xOp = do_step;
532   threadset[i].opnum++;
533   return TCL_OK;
534 }
535 
536 /*
537 ** This procedure runs in the thread to finalize a virtual machine.
538 */
539 static void do_finalize(Thread *p){
540   if( p->pStmt==0 ){
541     p->zErr = p->zStaticErr = "no virtual machine available";
542     p->rc = SQLITE_ERROR;
543     return;
544   }
545   p->rc = sqlite3_finalize(p->pStmt);
546   p->pStmt = 0;
547 }
548 
549 /*
550 ** Usage: thread_finalize ID
551 **
552 ** Finalize the virtual machine.
553 */
554 static int tcl_thread_finalize(
555   void *NotUsed,
556   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
557   int argc,              /* Number of arguments */
558   const char **argv      /* Text of each argument */
559 ){
560   int i;
561   if( argc!=2 ){
562     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
563        " IDL", 0);
564     return TCL_ERROR;
565   }
566   i = parse_thread_id(interp, argv[1]);
567   if( i<0 ) return TCL_ERROR;
568   if( !threadset[i].busy ){
569     Tcl_AppendResult(interp, "no such thread", 0);
570     return TCL_ERROR;
571   }
572   thread_wait(&threadset[i]);
573   threadset[i].xOp = do_finalize;
574   sqliteFree(threadset[i].zArg);
575   threadset[i].zArg = 0;
576   threadset[i].opnum++;
577   return TCL_OK;
578 }
579 
580 /*
581 ** Usage: thread_swap ID ID
582 **
583 ** Interchange the sqlite* pointer between two threads.
584 */
585 static int tcl_thread_swap(
586   void *NotUsed,
587   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
588   int argc,              /* Number of arguments */
589   const char **argv      /* Text of each argument */
590 ){
591   int i, j;
592   sqlite3 *temp;
593   if( argc!=3 ){
594     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
595        " ID1 ID2", 0);
596     return TCL_ERROR;
597   }
598   i = parse_thread_id(interp, argv[1]);
599   if( i<0 ) return TCL_ERROR;
600   if( !threadset[i].busy ){
601     Tcl_AppendResult(interp, "no such thread", 0);
602     return TCL_ERROR;
603   }
604   thread_wait(&threadset[i]);
605   j = parse_thread_id(interp, argv[2]);
606   if( j<0 ) return TCL_ERROR;
607   if( !threadset[j].busy ){
608     Tcl_AppendResult(interp, "no such thread", 0);
609     return TCL_ERROR;
610   }
611   thread_wait(&threadset[j]);
612   temp = threadset[i].db;
613   threadset[i].db = threadset[j].db;
614   threadset[j].db = temp;
615   return TCL_OK;
616 }
617 
618 /*
619 ** Register commands with the TCL interpreter.
620 */
621 int Sqlitetest4_Init(Tcl_Interp *interp){
622   static struct {
623      char *zName;
624      Tcl_CmdProc *xProc;
625   } aCmd[] = {
626      { "thread_create",     (Tcl_CmdProc*)tcl_thread_create     },
627      { "thread_wait",       (Tcl_CmdProc*)tcl_thread_wait       },
628      { "thread_halt",       (Tcl_CmdProc*)tcl_thread_halt       },
629      { "thread_argc",       (Tcl_CmdProc*)tcl_thread_argc       },
630      { "thread_argv",       (Tcl_CmdProc*)tcl_thread_argv       },
631      { "thread_colname",    (Tcl_CmdProc*)tcl_thread_colname    },
632      { "thread_result",     (Tcl_CmdProc*)tcl_thread_result     },
633      { "thread_error",      (Tcl_CmdProc*)tcl_thread_error      },
634      { "thread_compile",    (Tcl_CmdProc*)tcl_thread_compile    },
635      { "thread_step",       (Tcl_CmdProc*)tcl_thread_step       },
636      { "thread_finalize",   (Tcl_CmdProc*)tcl_thread_finalize   },
637      { "thread_swap",       (Tcl_CmdProc*)tcl_thread_swap       },
638   };
639   int i;
640 
641   for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
642     Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
643   }
644   return TCL_OK;
645 }
646 #else
647 int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; }
648 #endif /* OS_UNIX */
649