Annotation of embedaddon/sqlite3/src/test4.c, revision 1.1.1.1

1.1       misho       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: #include "sqliteInt.h"
                     15: #include "tcl.h"
                     16: #if SQLITE_OS_UNIX && SQLITE_THREADSAFE
                     17: #include <stdlib.h>
                     18: #include <string.h>
                     19: #include <pthread.h>
                     20: #include <sched.h>
                     21: #include <ctype.h>
                     22: 
                     23: /*
                     24: ** Each thread is controlled by an instance of the following
                     25: ** structure.
                     26: */
                     27: typedef struct Thread Thread;
                     28: struct Thread {
                     29:   /* The first group of fields are writable by the master and read-only
                     30:   ** to the thread. */
                     31:   char *zFilename;       /* Name of database file */
                     32:   void (*xOp)(Thread*);  /* next operation to do */
                     33:   char *zArg;            /* argument usable by xOp */
                     34:   int opnum;             /* Operation number */
                     35:   int busy;              /* True if this thread is in use */
                     36: 
                     37:   /* The next group of fields are writable by the thread but read-only to the
                     38:   ** master. */
                     39:   int completed;        /* Number of operations completed */
                     40:   sqlite3 *db;           /* Open database */
                     41:   sqlite3_stmt *pStmt;     /* Pending operation */
                     42:   char *zErr;           /* operation error */
                     43:   char *zStaticErr;     /* Static error message */
                     44:   int rc;               /* operation return code */
                     45:   int argc;             /* number of columns in result */
                     46:   const char *argv[100];    /* result columns */
                     47:   const char *colv[100];    /* result column names */
                     48: };
                     49: 
                     50: /*
                     51: ** There can be as many as 26 threads running at once.  Each is named
                     52: ** by a capital letter: A, B, C, ..., Y, Z.
                     53: */
                     54: #define N_THREAD 26
                     55: static Thread threadset[N_THREAD];
                     56: 
                     57: 
                     58: /*
                     59: ** The main loop for a thread.  Threads use busy waiting. 
                     60: */
                     61: static void *thread_main(void *pArg){
                     62:   Thread *p = (Thread*)pArg;
                     63:   if( p->db ){
                     64:     sqlite3_close(p->db);
                     65:   }
                     66:   sqlite3_open(p->zFilename, &p->db);
                     67:   if( SQLITE_OK!=sqlite3_errcode(p->db) ){
                     68:     p->zErr = strdup(sqlite3_errmsg(p->db));
                     69:     sqlite3_close(p->db);
                     70:     p->db = 0;
                     71:   }
                     72:   p->pStmt = 0;
                     73:   p->completed = 1;
                     74:   while( p->opnum<=p->completed ) sched_yield();
                     75:   while( p->xOp ){
                     76:     if( p->zErr && p->zErr!=p->zStaticErr ){
                     77:       sqlite3_free(p->zErr);
                     78:       p->zErr = 0;
                     79:     }
                     80:     (*p->xOp)(p);
                     81:     p->completed++;
                     82:     while( p->opnum<=p->completed ) sched_yield();
                     83:   }
                     84:   if( p->pStmt ){
                     85:     sqlite3_finalize(p->pStmt);
                     86:     p->pStmt = 0;
                     87:   }
                     88:   if( p->db ){
                     89:     sqlite3_close(p->db);
                     90:     p->db = 0;
                     91:   }
                     92:   if( p->zErr && p->zErr!=p->zStaticErr ){
                     93:     sqlite3_free(p->zErr);
                     94:     p->zErr = 0;
                     95:   }
                     96:   p->completed++;
                     97: #ifndef SQLITE_OMIT_DEPRECATED
                     98:   sqlite3_thread_cleanup();
                     99: #endif
                    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((unsigned char)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:   sqlite3_free(threadset[i].zFilename);
                    145:   threadset[i].zFilename = sqlite3_mprintf("%s", 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:     sqlite3_free(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:   sqlite3_free(p->zArg);
                    203:   p->zArg = 0;
                    204:   sqlite3_free(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_PERM:       zName = "SQLITE_PERM";        break;
                    379:     case SQLITE_ABORT:      zName = "SQLITE_ABORT";       break;
                    380:     case SQLITE_BUSY:       zName = "SQLITE_BUSY";        break;
                    381:     case SQLITE_LOCKED:     zName = "SQLITE_LOCKED";      break;
                    382:     case SQLITE_NOMEM:      zName = "SQLITE_NOMEM";       break;
                    383:     case SQLITE_READONLY:   zName = "SQLITE_READONLY";    break;
                    384:     case SQLITE_INTERRUPT:  zName = "SQLITE_INTERRUPT";   break;
                    385:     case SQLITE_IOERR:      zName = "SQLITE_IOERR";       break;
                    386:     case SQLITE_CORRUPT:    zName = "SQLITE_CORRUPT";     break;
                    387:     case SQLITE_FULL:       zName = "SQLITE_FULL";        break;
                    388:     case SQLITE_CANTOPEN:   zName = "SQLITE_CANTOPEN";    break;
                    389:     case SQLITE_PROTOCOL:   zName = "SQLITE_PROTOCOL";    break;
                    390:     case SQLITE_EMPTY:      zName = "SQLITE_EMPTY";       break;
                    391:     case SQLITE_SCHEMA:     zName = "SQLITE_SCHEMA";      break;
                    392:     case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT";  break;
                    393:     case SQLITE_MISMATCH:   zName = "SQLITE_MISMATCH";    break;
                    394:     case SQLITE_MISUSE:     zName = "SQLITE_MISUSE";      break;
                    395:     case SQLITE_NOLFS:      zName = "SQLITE_NOLFS";       break;
                    396:     case SQLITE_AUTH:       zName = "SQLITE_AUTH";        break;
                    397:     case SQLITE_FORMAT:     zName = "SQLITE_FORMAT";      break;
                    398:     case SQLITE_RANGE:      zName = "SQLITE_RANGE";       break;
                    399:     case SQLITE_ROW:        zName = "SQLITE_ROW";         break;
                    400:     case SQLITE_DONE:       zName = "SQLITE_DONE";        break;
                    401:     default:                zName = "SQLITE_Unknown";     break;
                    402:   }
                    403:   Tcl_AppendResult(interp, zName, 0);
                    404:   return TCL_OK;
                    405: }
                    406: 
                    407: /*
                    408: ** Usage: thread_error  ID
                    409: **
                    410: ** Wait on the most recent operation to complete, then return the
                    411: ** error string.
                    412: */
                    413: static int tcl_thread_error(
                    414:   void *NotUsed,
                    415:   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
                    416:   int argc,              /* Number of arguments */
                    417:   const char **argv      /* Text of each argument */
                    418: ){
                    419:   int i;
                    420: 
                    421:   if( argc!=2 ){
                    422:     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
                    423:        " ID", 0);
                    424:     return TCL_ERROR;
                    425:   }
                    426:   i = parse_thread_id(interp, argv[1]);
                    427:   if( i<0 ) return TCL_ERROR;
                    428:   if( !threadset[i].busy ){
                    429:     Tcl_AppendResult(interp, "no such thread", 0);
                    430:     return TCL_ERROR;
                    431:   }
                    432:   thread_wait(&threadset[i]);
                    433:   Tcl_AppendResult(interp, threadset[i].zErr, 0);
                    434:   return TCL_OK;
                    435: }
                    436: 
                    437: /*
                    438: ** This procedure runs in the thread to compile an SQL statement.
                    439: */
                    440: static void do_compile(Thread *p){
                    441:   if( p->db==0 ){
                    442:     p->zErr = p->zStaticErr = "no database is open";
                    443:     p->rc = SQLITE_ERROR;
                    444:     return;
                    445:   }
                    446:   if( p->pStmt ){
                    447:     sqlite3_finalize(p->pStmt);
                    448:     p->pStmt = 0;
                    449:   }
                    450:   p->rc = sqlite3_prepare(p->db, p->zArg, -1, &p->pStmt, 0);
                    451: }
                    452: 
                    453: /*
                    454: ** Usage: thread_compile ID SQL
                    455: **
                    456: ** Compile a new virtual machine.
                    457: */
                    458: static int tcl_thread_compile(
                    459:   void *NotUsed,
                    460:   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
                    461:   int argc,              /* Number of arguments */
                    462:   const char **argv      /* Text of each argument */
                    463: ){
                    464:   int i;
                    465:   if( argc!=3 ){
                    466:     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
                    467:        " ID SQL", 0);
                    468:     return TCL_ERROR;
                    469:   }
                    470:   i = parse_thread_id(interp, argv[1]);
                    471:   if( i<0 ) return TCL_ERROR;
                    472:   if( !threadset[i].busy ){
                    473:     Tcl_AppendResult(interp, "no such thread", 0);
                    474:     return TCL_ERROR;
                    475:   }
                    476:   thread_wait(&threadset[i]);
                    477:   threadset[i].xOp = do_compile;
                    478:   sqlite3_free(threadset[i].zArg);
                    479:   threadset[i].zArg = sqlite3_mprintf("%s", argv[2]);
                    480:   threadset[i].opnum++;
                    481:   return TCL_OK;
                    482: }
                    483: 
                    484: /*
                    485: ** This procedure runs in the thread to step the virtual machine.
                    486: */
                    487: static void do_step(Thread *p){
                    488:   int i;
                    489:   if( p->pStmt==0 ){
                    490:     p->zErr = p->zStaticErr = "no virtual machine available";
                    491:     p->rc = SQLITE_ERROR;
                    492:     return;
                    493:   }
                    494:   p->rc = sqlite3_step(p->pStmt);
                    495:   if( p->rc==SQLITE_ROW ){
                    496:     p->argc = sqlite3_column_count(p->pStmt);
                    497:     for(i=0; i<sqlite3_data_count(p->pStmt); i++){
                    498:       p->argv[i] = (char*)sqlite3_column_text(p->pStmt, i);
                    499:     }
                    500:     for(i=0; i<p->argc; i++){
                    501:       p->colv[i] = sqlite3_column_name(p->pStmt, i);
                    502:     }
                    503:   }
                    504: }
                    505: 
                    506: /*
                    507: ** Usage: thread_step ID
                    508: **
                    509: ** Advance the virtual machine by one step
                    510: */
                    511: static int tcl_thread_step(
                    512:   void *NotUsed,
                    513:   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
                    514:   int argc,              /* Number of arguments */
                    515:   const char **argv      /* Text of each argument */
                    516: ){
                    517:   int i;
                    518:   if( argc!=2 ){
                    519:     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
                    520:        " IDL", 0);
                    521:     return TCL_ERROR;
                    522:   }
                    523:   i = parse_thread_id(interp, argv[1]);
                    524:   if( i<0 ) return TCL_ERROR;
                    525:   if( !threadset[i].busy ){
                    526:     Tcl_AppendResult(interp, "no such thread", 0);
                    527:     return TCL_ERROR;
                    528:   }
                    529:   thread_wait(&threadset[i]);
                    530:   threadset[i].xOp = do_step;
                    531:   threadset[i].opnum++;
                    532:   return TCL_OK;
                    533: }
                    534: 
                    535: /*
                    536: ** This procedure runs in the thread to finalize a virtual machine.
                    537: */
                    538: static void do_finalize(Thread *p){
                    539:   if( p->pStmt==0 ){
                    540:     p->zErr = p->zStaticErr = "no virtual machine available";
                    541:     p->rc = SQLITE_ERROR;
                    542:     return;
                    543:   }
                    544:   p->rc = sqlite3_finalize(p->pStmt);
                    545:   p->pStmt = 0;
                    546: }
                    547: 
                    548: /*
                    549: ** Usage: thread_finalize ID
                    550: **
                    551: ** Finalize the virtual machine.
                    552: */
                    553: static int tcl_thread_finalize(
                    554:   void *NotUsed,
                    555:   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
                    556:   int argc,              /* Number of arguments */
                    557:   const char **argv      /* Text of each argument */
                    558: ){
                    559:   int i;
                    560:   if( argc!=2 ){
                    561:     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
                    562:        " IDL", 0);
                    563:     return TCL_ERROR;
                    564:   }
                    565:   i = parse_thread_id(interp, argv[1]);
                    566:   if( i<0 ) return TCL_ERROR;
                    567:   if( !threadset[i].busy ){
                    568:     Tcl_AppendResult(interp, "no such thread", 0);
                    569:     return TCL_ERROR;
                    570:   }
                    571:   thread_wait(&threadset[i]);
                    572:   threadset[i].xOp = do_finalize;
                    573:   sqlite3_free(threadset[i].zArg);
                    574:   threadset[i].zArg = 0;
                    575:   threadset[i].opnum++;
                    576:   return TCL_OK;
                    577: }
                    578: 
                    579: /*
                    580: ** Usage: thread_swap ID ID
                    581: **
                    582: ** Interchange the sqlite* pointer between two threads.
                    583: */
                    584: static int tcl_thread_swap(
                    585:   void *NotUsed,
                    586:   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
                    587:   int argc,              /* Number of arguments */
                    588:   const char **argv      /* Text of each argument */
                    589: ){
                    590:   int i, j;
                    591:   sqlite3 *temp;
                    592:   if( argc!=3 ){
                    593:     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
                    594:        " ID1 ID2", 0);
                    595:     return TCL_ERROR;
                    596:   }
                    597:   i = parse_thread_id(interp, argv[1]);
                    598:   if( i<0 ) return TCL_ERROR;
                    599:   if( !threadset[i].busy ){
                    600:     Tcl_AppendResult(interp, "no such thread", 0);
                    601:     return TCL_ERROR;
                    602:   }
                    603:   thread_wait(&threadset[i]);
                    604:   j = parse_thread_id(interp, argv[2]);
                    605:   if( j<0 ) return TCL_ERROR;
                    606:   if( !threadset[j].busy ){
                    607:     Tcl_AppendResult(interp, "no such thread", 0);
                    608:     return TCL_ERROR;
                    609:   }
                    610:   thread_wait(&threadset[j]);
                    611:   temp = threadset[i].db;
                    612:   threadset[i].db = threadset[j].db;
                    613:   threadset[j].db = temp;
                    614:   return TCL_OK;
                    615: }
                    616: 
                    617: /*
                    618: ** Usage: thread_db_get ID
                    619: **
                    620: ** Return the database connection pointer for the given thread.  Then
                    621: ** remove the pointer from the thread itself.  Afterwards, the thread
                    622: ** can be stopped and the connection can be used by the main thread.
                    623: */
                    624: static int tcl_thread_db_get(
                    625:   void *NotUsed,
                    626:   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
                    627:   int argc,              /* Number of arguments */
                    628:   const char **argv      /* Text of each argument */
                    629: ){
                    630:   int i;
                    631:   char zBuf[100];
                    632:   extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
                    633:   if( argc!=2 ){
                    634:     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
                    635:        " ID", 0);
                    636:     return TCL_ERROR;
                    637:   }
                    638:   i = parse_thread_id(interp, argv[1]);
                    639:   if( i<0 ) return TCL_ERROR;
                    640:   if( !threadset[i].busy ){
                    641:     Tcl_AppendResult(interp, "no such thread", 0);
                    642:     return TCL_ERROR;
                    643:   }
                    644:   thread_wait(&threadset[i]);
                    645:   sqlite3TestMakePointerStr(interp, zBuf, threadset[i].db);
                    646:   threadset[i].db = 0;
                    647:   Tcl_AppendResult(interp, zBuf, (char*)0);
                    648:   return TCL_OK;
                    649: }
                    650: 
                    651: /*
                    652: ** Usage: thread_db_put ID DB
                    653: **
                    654: */
                    655: static int tcl_thread_db_put(
                    656:   void *NotUsed,
                    657:   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
                    658:   int argc,              /* Number of arguments */
                    659:   const char **argv      /* Text of each argument */
                    660: ){
                    661:   int i;
                    662:   extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
                    663:   extern void *sqlite3TestTextToPtr(const char *);
                    664:   if( argc!=3 ){
                    665:     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
                    666:        " ID DB", 0);
                    667:     return TCL_ERROR;
                    668:   }
                    669:   i = parse_thread_id(interp, argv[1]);
                    670:   if( i<0 ) return TCL_ERROR;
                    671:   if( !threadset[i].busy ){
                    672:     Tcl_AppendResult(interp, "no such thread", 0);
                    673:     return TCL_ERROR;
                    674:   }
                    675:   thread_wait(&threadset[i]);
                    676:   assert( !threadset[i].db );
                    677:   threadset[i].db = (sqlite3*)sqlite3TestTextToPtr(argv[2]);
                    678:   return TCL_OK;
                    679: }
                    680: 
                    681: /*
                    682: ** Usage: thread_stmt_get ID
                    683: **
                    684: ** Return the database stmt pointer for the given thread.  Then
                    685: ** remove the pointer from the thread itself. 
                    686: */
                    687: static int tcl_thread_stmt_get(
                    688:   void *NotUsed,
                    689:   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
                    690:   int argc,              /* Number of arguments */
                    691:   const char **argv      /* Text of each argument */
                    692: ){
                    693:   int i;
                    694:   char zBuf[100];
                    695:   extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
                    696:   if( argc!=2 ){
                    697:     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
                    698:        " ID", 0);
                    699:     return TCL_ERROR;
                    700:   }
                    701:   i = parse_thread_id(interp, argv[1]);
                    702:   if( i<0 ) return TCL_ERROR;
                    703:   if( !threadset[i].busy ){
                    704:     Tcl_AppendResult(interp, "no such thread", 0);
                    705:     return TCL_ERROR;
                    706:   }
                    707:   thread_wait(&threadset[i]);
                    708:   sqlite3TestMakePointerStr(interp, zBuf, threadset[i].pStmt);
                    709:   threadset[i].pStmt = 0;
                    710:   Tcl_AppendResult(interp, zBuf, (char*)0);
                    711:   return TCL_OK;
                    712: }
                    713: 
                    714: /*
                    715: ** Register commands with the TCL interpreter.
                    716: */
                    717: int Sqlitetest4_Init(Tcl_Interp *interp){
                    718:   static struct {
                    719:      char *zName;
                    720:      Tcl_CmdProc *xProc;
                    721:   } aCmd[] = {
                    722:      { "thread_create",     (Tcl_CmdProc*)tcl_thread_create     },
                    723:      { "thread_wait",       (Tcl_CmdProc*)tcl_thread_wait       },
                    724:      { "thread_halt",       (Tcl_CmdProc*)tcl_thread_halt       },
                    725:      { "thread_argc",       (Tcl_CmdProc*)tcl_thread_argc       },
                    726:      { "thread_argv",       (Tcl_CmdProc*)tcl_thread_argv       },
                    727:      { "thread_colname",    (Tcl_CmdProc*)tcl_thread_colname    },
                    728:      { "thread_result",     (Tcl_CmdProc*)tcl_thread_result     },
                    729:      { "thread_error",      (Tcl_CmdProc*)tcl_thread_error      },
                    730:      { "thread_compile",    (Tcl_CmdProc*)tcl_thread_compile    },
                    731:      { "thread_step",       (Tcl_CmdProc*)tcl_thread_step       },
                    732:      { "thread_finalize",   (Tcl_CmdProc*)tcl_thread_finalize   },
                    733:      { "thread_swap",       (Tcl_CmdProc*)tcl_thread_swap       },
                    734:      { "thread_db_get",     (Tcl_CmdProc*)tcl_thread_db_get     },
                    735:      { "thread_db_put",     (Tcl_CmdProc*)tcl_thread_db_put     },
                    736:      { "thread_stmt_get",   (Tcl_CmdProc*)tcl_thread_stmt_get   },
                    737:   };
                    738:   int i;
                    739: 
                    740:   for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
                    741:     Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
                    742:   }
                    743:   return TCL_OK;
                    744: }
                    745: #else
                    746: int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; }
                    747: #endif /* SQLITE_OS_UNIX */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>