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