00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "sqliteInt.h"
00017 #include "tcl.h"
00018 #if defined(OS_UNIX) && OS_UNIX==1 && defined(THREADSAFE) && THREADSAFE==1
00019 #include <stdlib.h>
00020 #include <string.h>
00021 #include <pthread.h>
00022 #include <sched.h>
00023 #include <ctype.h>
00024
00025
00026
00027
00028
00029 typedef struct Thread Thread;
00030 struct Thread {
00031
00032
00033 char *zFilename;
00034 void (*xOp)(Thread*);
00035 char *zArg;
00036 int opnum;
00037 int busy;
00038
00039
00040
00041 int completed;
00042 sqlite *db;
00043 sqlite_vm *vm;
00044 char *zErr;
00045 char *zStaticErr;
00046 int rc;
00047 int argc;
00048 const char **argv;
00049 const char **colv;
00050 };
00051
00052
00053
00054
00055
00056 #define N_THREAD 26
00057 static Thread threadset[N_THREAD];
00058
00059
00060
00061
00062
00063 static void *thread_main(void *pArg){
00064 Thread *p = (Thread*)pArg;
00065 if( p->db ){
00066 sqlite_close(p->db);
00067 }
00068 p->db = sqlite_open(p->zFilename, 0, &p->zErr);
00069 p->vm = 0;
00070 p->completed = 1;
00071 while( p->opnum<=p->completed ) sched_yield();
00072 while( p->xOp ){
00073 if( p->zErr && p->zErr!=p->zStaticErr ){
00074 sqlite_freemem(p->zErr);
00075 p->zErr = 0;
00076 }
00077 (*p->xOp)(p);
00078 p->completed++;
00079 while( p->opnum<=p->completed ) sched_yield();
00080 }
00081 if( p->vm ){
00082 sqlite_finalize(p->vm, 0);
00083 p->vm = 0;
00084 }
00085 if( p->db ){
00086 sqlite_close(p->db);
00087 p->db = 0;
00088 }
00089 if( p->zErr && p->zErr!=p->zStaticErr ){
00090 sqlite_freemem(p->zErr);
00091 p->zErr = 0;
00092 }
00093 p->completed++;
00094 return 0;
00095 }
00096
00097
00098
00099
00100
00101
00102 static int parse_thread_id(Tcl_Interp *interp, const char *zArg){
00103 if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper(zArg[0]) ){
00104 Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0);
00105 return -1;
00106 }
00107 return zArg[0] - 'A';
00108 }
00109
00110
00111
00112
00113
00114
00115
00116 static int tcl_thread_create(
00117 void *NotUsed,
00118 Tcl_Interp *interp,
00119 int argc,
00120 const char **argv
00121 ){
00122 int i;
00123 pthread_t x;
00124 int rc;
00125
00126 if( argc!=3 ){
00127 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00128 " ID FILENAME", 0);
00129 return TCL_ERROR;
00130 }
00131 i = parse_thread_id(interp, argv[1]);
00132 if( i<0 ) return TCL_ERROR;
00133 if( threadset[i].busy ){
00134 Tcl_AppendResult(interp, "thread ", argv[1], " is already running", 0);
00135 return TCL_ERROR;
00136 }
00137 threadset[i].busy = 1;
00138 sqliteFree(threadset[i].zFilename);
00139 threadset[i].zFilename = sqliteStrDup(argv[2]);
00140 threadset[i].opnum = 1;
00141 threadset[i].completed = 0;
00142 rc = pthread_create(&x, 0, thread_main, &threadset[i]);
00143 if( rc ){
00144 Tcl_AppendResult(interp, "failed to create the thread", 0);
00145 sqliteFree(threadset[i].zFilename);
00146 threadset[i].busy = 0;
00147 return TCL_ERROR;
00148 }
00149 pthread_detach(x);
00150 return TCL_OK;
00151 }
00152
00153
00154
00155
00156 static void thread_wait(Thread *p){
00157 while( p->opnum>p->completed ) sched_yield();
00158 }
00159
00160
00161
00162
00163
00164
00165 static int tcl_thread_wait(
00166 void *NotUsed,
00167 Tcl_Interp *interp,
00168 int argc,
00169 const char **argv
00170 ){
00171 int i;
00172
00173 if( argc!=2 ){
00174 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00175 " ID", 0);
00176 return TCL_ERROR;
00177 }
00178 i = parse_thread_id(interp, argv[1]);
00179 if( i<0 ) return TCL_ERROR;
00180 if( !threadset[i].busy ){
00181 Tcl_AppendResult(interp, "no such thread", 0);
00182 return TCL_ERROR;
00183 }
00184 thread_wait(&threadset[i]);
00185 return TCL_OK;
00186 }
00187
00188
00189
00190
00191 static void stop_thread(Thread *p){
00192 thread_wait(p);
00193 p->xOp = 0;
00194 p->opnum++;
00195 thread_wait(p);
00196 sqliteFree(p->zArg);
00197 p->zArg = 0;
00198 sqliteFree(p->zFilename);
00199 p->zFilename = 0;
00200 p->busy = 0;
00201 }
00202
00203
00204
00205
00206
00207
00208
00209 static int tcl_thread_halt(
00210 void *NotUsed,
00211 Tcl_Interp *interp,
00212 int argc,
00213 const char **argv
00214 ){
00215 int i;
00216
00217 if( argc!=2 ){
00218 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00219 " ID", 0);
00220 return TCL_ERROR;
00221 }
00222 if( argv[1][0]=='*' && argv[1][1]==0 ){
00223 for(i=0; i<N_THREAD; i++){
00224 if( threadset[i].busy ) stop_thread(&threadset[i]);
00225 }
00226 }else{
00227 i = parse_thread_id(interp, argv[1]);
00228 if( i<0 ) return TCL_ERROR;
00229 if( !threadset[i].busy ){
00230 Tcl_AppendResult(interp, "no such thread", 0);
00231 return TCL_ERROR;
00232 }
00233 stop_thread(&threadset[i]);
00234 }
00235 return TCL_OK;
00236 }
00237
00238
00239
00240
00241
00242
00243
00244 static int tcl_thread_argc(
00245 void *NotUsed,
00246 Tcl_Interp *interp,
00247 int argc,
00248 const char **argv
00249 ){
00250 int i;
00251 char zBuf[100];
00252
00253 if( argc!=2 ){
00254 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00255 " ID", 0);
00256 return TCL_ERROR;
00257 }
00258 i = parse_thread_id(interp, argv[1]);
00259 if( i<0 ) return TCL_ERROR;
00260 if( !threadset[i].busy ){
00261 Tcl_AppendResult(interp, "no such thread", 0);
00262 return TCL_ERROR;
00263 }
00264 thread_wait(&threadset[i]);
00265 sprintf(zBuf, "%d", threadset[i].argc);
00266 Tcl_AppendResult(interp, zBuf, 0);
00267 return TCL_OK;
00268 }
00269
00270
00271
00272
00273
00274
00275
00276 static int tcl_thread_argv(
00277 void *NotUsed,
00278 Tcl_Interp *interp,
00279 int argc,
00280 const char **argv
00281 ){
00282 int i;
00283 int n;
00284
00285 if( argc!=3 ){
00286 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00287 " ID N", 0);
00288 return TCL_ERROR;
00289 }
00290 i = parse_thread_id(interp, argv[1]);
00291 if( i<0 ) return TCL_ERROR;
00292 if( !threadset[i].busy ){
00293 Tcl_AppendResult(interp, "no such thread", 0);
00294 return TCL_ERROR;
00295 }
00296 if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
00297 thread_wait(&threadset[i]);
00298 if( n<0 || n>=threadset[i].argc ){
00299 Tcl_AppendResult(interp, "column number out of range", 0);
00300 return TCL_ERROR;
00301 }
00302 Tcl_AppendResult(interp, threadset[i].argv[n], 0);
00303 return TCL_OK;
00304 }
00305
00306
00307
00308
00309
00310
00311
00312 static int tcl_thread_colname(
00313 void *NotUsed,
00314 Tcl_Interp *interp,
00315 int argc,
00316 const char **argv
00317 ){
00318 int i;
00319 int n;
00320
00321 if( argc!=3 ){
00322 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00323 " ID N", 0);
00324 return TCL_ERROR;
00325 }
00326 i = parse_thread_id(interp, argv[1]);
00327 if( i<0 ) return TCL_ERROR;
00328 if( !threadset[i].busy ){
00329 Tcl_AppendResult(interp, "no such thread", 0);
00330 return TCL_ERROR;
00331 }
00332 if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
00333 thread_wait(&threadset[i]);
00334 if( n<0 || n>=threadset[i].argc ){
00335 Tcl_AppendResult(interp, "column number out of range", 0);
00336 return TCL_ERROR;
00337 }
00338 Tcl_AppendResult(interp, threadset[i].colv[n], 0);
00339 return TCL_OK;
00340 }
00341
00342
00343
00344
00345
00346
00347
00348 static int tcl_thread_result(
00349 void *NotUsed,
00350 Tcl_Interp *interp,
00351 int argc,
00352 const char **argv
00353 ){
00354 int i;
00355 const char *zName;
00356
00357 if( argc!=2 ){
00358 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00359 " ID", 0);
00360 return TCL_ERROR;
00361 }
00362 i = parse_thread_id(interp, argv[1]);
00363 if( i<0 ) return TCL_ERROR;
00364 if( !threadset[i].busy ){
00365 Tcl_AppendResult(interp, "no such thread", 0);
00366 return TCL_ERROR;
00367 }
00368 thread_wait(&threadset[i]);
00369 switch( threadset[i].rc ){
00370 case SQLITE_OK: zName = "SQLITE_OK"; break;
00371 case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
00372 case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break;
00373 case SQLITE_PERM: zName = "SQLITE_PERM"; break;
00374 case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
00375 case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
00376 case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break;
00377 case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
00378 case SQLITE_READONLY: zName = "SQLITE_READONLY"; break;
00379 case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
00380 case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
00381 case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break;
00382 case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break;
00383 case SQLITE_FULL: zName = "SQLITE_FULL"; break;
00384 case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break;
00385 case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
00386 case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
00387 case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break;
00388 case SQLITE_TOOBIG: zName = "SQLITE_TOOBIG"; break;
00389 case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break;
00390 case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break;
00391 case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break;
00392 case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break;
00393 case SQLITE_AUTH: zName = "SQLITE_AUTH"; break;
00394 case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break;
00395 case SQLITE_RANGE: zName = "SQLITE_RANGE"; break;
00396 case SQLITE_ROW: zName = "SQLITE_ROW"; break;
00397 case SQLITE_DONE: zName = "SQLITE_DONE"; break;
00398 default: zName = "SQLITE_Unknown"; break;
00399 }
00400 Tcl_AppendResult(interp, zName, 0);
00401 return TCL_OK;
00402 }
00403
00404
00405
00406
00407
00408
00409
00410 static int tcl_thread_error(
00411 void *NotUsed,
00412 Tcl_Interp *interp,
00413 int argc,
00414 const char **argv
00415 ){
00416 int i;
00417
00418 if( argc!=2 ){
00419 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00420 " ID", 0);
00421 return TCL_ERROR;
00422 }
00423 i = parse_thread_id(interp, argv[1]);
00424 if( i<0 ) return TCL_ERROR;
00425 if( !threadset[i].busy ){
00426 Tcl_AppendResult(interp, "no such thread", 0);
00427 return TCL_ERROR;
00428 }
00429 thread_wait(&threadset[i]);
00430 Tcl_AppendResult(interp, threadset[i].zErr, 0);
00431 return TCL_OK;
00432 }
00433
00434
00435
00436
00437 static void do_compile(Thread *p){
00438 if( p->db==0 ){
00439 p->zErr = p->zStaticErr = "no database is open";
00440 p->rc = SQLITE_ERROR;
00441 return;
00442 }
00443 if( p->vm ){
00444 sqlite_finalize(p->vm, 0);
00445 p->vm = 0;
00446 }
00447 p->rc = sqlite_compile(p->db, p->zArg, 0, &p->vm, &p->zErr);
00448 }
00449
00450
00451
00452
00453
00454
00455 static int tcl_thread_compile(
00456 void *NotUsed,
00457 Tcl_Interp *interp,
00458 int argc,
00459 const char **argv
00460 ){
00461 int i;
00462 if( argc!=3 ){
00463 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00464 " ID SQL", 0);
00465 return TCL_ERROR;
00466 }
00467 i = parse_thread_id(interp, argv[1]);
00468 if( i<0 ) return TCL_ERROR;
00469 if( !threadset[i].busy ){
00470 Tcl_AppendResult(interp, "no such thread", 0);
00471 return TCL_ERROR;
00472 }
00473 thread_wait(&threadset[i]);
00474 threadset[i].xOp = do_compile;
00475 sqliteFree(threadset[i].zArg);
00476 threadset[i].zArg = sqliteStrDup(argv[2]);
00477 threadset[i].opnum++;
00478 return TCL_OK;
00479 }
00480
00481
00482
00483
00484 static void do_step(Thread *p){
00485 if( p->vm==0 ){
00486 p->zErr = p->zStaticErr = "no virtual machine available";
00487 p->rc = SQLITE_ERROR;
00488 return;
00489 }
00490 p->rc = sqlite_step(p->vm, &p->argc, &p->argv, &p->colv);
00491 }
00492
00493
00494
00495
00496
00497
00498 static int tcl_thread_step(
00499 void *NotUsed,
00500 Tcl_Interp *interp,
00501 int argc,
00502 const char **argv
00503 ){
00504 int i;
00505 if( argc!=2 ){
00506 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00507 " IDL", 0);
00508 return TCL_ERROR;
00509 }
00510 i = parse_thread_id(interp, argv[1]);
00511 if( i<0 ) return TCL_ERROR;
00512 if( !threadset[i].busy ){
00513 Tcl_AppendResult(interp, "no such thread", 0);
00514 return TCL_ERROR;
00515 }
00516 thread_wait(&threadset[i]);
00517 threadset[i].xOp = do_step;
00518 threadset[i].opnum++;
00519 return TCL_OK;
00520 }
00521
00522
00523
00524
00525 static void do_finalize(Thread *p){
00526 if( p->vm==0 ){
00527 p->zErr = p->zStaticErr = "no virtual machine available";
00528 p->rc = SQLITE_ERROR;
00529 return;
00530 }
00531 p->rc = sqlite_finalize(p->vm, &p->zErr);
00532 p->vm = 0;
00533 }
00534
00535
00536
00537
00538
00539
00540 static int tcl_thread_finalize(
00541 void *NotUsed,
00542 Tcl_Interp *interp,
00543 int argc,
00544 const char **argv
00545 ){
00546 int i;
00547 if( argc!=2 ){
00548 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00549 " IDL", 0);
00550 return TCL_ERROR;
00551 }
00552 i = parse_thread_id(interp, argv[1]);
00553 if( i<0 ) return TCL_ERROR;
00554 if( !threadset[i].busy ){
00555 Tcl_AppendResult(interp, "no such thread", 0);
00556 return TCL_ERROR;
00557 }
00558 thread_wait(&threadset[i]);
00559 threadset[i].xOp = do_finalize;
00560 sqliteFree(threadset[i].zArg);
00561 threadset[i].zArg = 0;
00562 threadset[i].opnum++;
00563 return TCL_OK;
00564 }
00565
00566
00567
00568
00569
00570
00571 static int tcl_thread_swap(
00572 void *NotUsed,
00573 Tcl_Interp *interp,
00574 int argc,
00575 const char **argv
00576 ){
00577 int i, j;
00578 sqlite *temp;
00579 if( argc!=3 ){
00580 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00581 " ID1 ID2", 0);
00582 return TCL_ERROR;
00583 }
00584 i = parse_thread_id(interp, argv[1]);
00585 if( i<0 ) return TCL_ERROR;
00586 if( !threadset[i].busy ){
00587 Tcl_AppendResult(interp, "no such thread", 0);
00588 return TCL_ERROR;
00589 }
00590 thread_wait(&threadset[i]);
00591 j = parse_thread_id(interp, argv[2]);
00592 if( j<0 ) return TCL_ERROR;
00593 if( !threadset[j].busy ){
00594 Tcl_AppendResult(interp, "no such thread", 0);
00595 return TCL_ERROR;
00596 }
00597 thread_wait(&threadset[j]);
00598 temp = threadset[i].db;
00599 threadset[i].db = threadset[j].db;
00600 threadset[j].db = temp;
00601 return TCL_OK;
00602 }
00603
00604
00605
00606
00607 int Sqlitetest4_Init(Tcl_Interp *interp){
00608 static struct {
00609 char *zName;
00610 Tcl_CmdProc *xProc;
00611 } aCmd[] = {
00612 { "thread_create", (Tcl_CmdProc*)tcl_thread_create },
00613 { "thread_wait", (Tcl_CmdProc*)tcl_thread_wait },
00614 { "thread_halt", (Tcl_CmdProc*)tcl_thread_halt },
00615 { "thread_argc", (Tcl_CmdProc*)tcl_thread_argc },
00616 { "thread_argv", (Tcl_CmdProc*)tcl_thread_argv },
00617 { "thread_colname", (Tcl_CmdProc*)tcl_thread_colname },
00618 { "thread_result", (Tcl_CmdProc*)tcl_thread_result },
00619 { "thread_error", (Tcl_CmdProc*)tcl_thread_error },
00620 { "thread_compile", (Tcl_CmdProc*)tcl_thread_compile },
00621 { "thread_step", (Tcl_CmdProc*)tcl_thread_step },
00622 { "thread_finalize", (Tcl_CmdProc*)tcl_thread_finalize },
00623 { "thread_swap", (Tcl_CmdProc*)tcl_thread_swap },
00624 };
00625 int i;
00626
00627 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
00628 Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
00629 }
00630 return TCL_OK;
00631 }
00632 #else
00633 int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; }
00634 #endif