Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
test4.c738 linesDownload Raw Back to src
1/*2** 2003 December 183**4** The author disclaims copyright to this source code.  In place of5** 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 SQLite library in a multithreaded environment.13*/14#include "sqliteInt.h"15#include "tclsqlite.h"16#if SQLITE_OS_UNIX && SQLITE_THREADSAFE17#include <stdlib.h>18#include <string.h>19#include <pthread.h>20#include <sched.h>21#include <ctype.h>22 23extern const char *sqlite3ErrName(int);24 25/*26** Each thread is controlled by an instance of the following27** structure.28*/29typedef struct Thread Thread;30struct Thread {31  /* The first group of fields are writable by the leader and read-only32  ** to the thread. */33  char *zFilename;       /* Name of database file */34  void (*xOp)(Thread*);  /* next operation to do */35  char *zArg;            /* argument usable by xOp */36  int opnum;             /* Operation number */37  int busy;              /* True if this thread is in use */38 39  /* The next group of fields are writable by the thread but read-only to the40  ** leader. */41  int completed;        /* Number of operations completed */42  sqlite3 *db;           /* Open database */43  sqlite3_stmt *pStmt;     /* Pending operation */44  char *zErr;           /* operation error */45  char *zStaticErr;     /* Static error message */46  int rc;               /* operation return code */47  int argc;             /* number of columns in result */48  const char *argv[100];    /* result columns */49  const char *colv[100];    /* result column names */50};51 52/*53** There can be as many as 26 threads running at once.  Each is named54** by a capital letter: A, B, C, ..., Y, Z.55*/56#define N_THREAD 2657static Thread threadset[N_THREAD];58 59static void test_barrier(){60  sqlite3_mutex *pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_APP1);61  sqlite3_mutex_enter(pMutex);62  sqlite3_mutex_leave(pMutex);63}64 65/*66** The main loop for a thread.  Threads use busy waiting. 67*/68static void *test_thread_main(void *pArg){69  Thread *p = (Thread*)pArg;70  if( p->db ){71    sqlite3_close(p->db);72  }73  sqlite3_open(p->zFilename, &p->db);74  if( SQLITE_OK!=sqlite3_errcode(p->db) ){75    p->zErr = strdup(sqlite3_errmsg(p->db));76    sqlite3_close(p->db);77    p->db = 0;78  }79  p->pStmt = 0;80  test_barrier();81  p->completed = 1;82  while( p->opnum<=p->completed ) sched_yield();83  test_barrier();84  while( p->xOp ){85    if( p->zErr && p->zErr!=p->zStaticErr ){86      sqlite3_free(p->zErr);87      p->zErr = 0;88    }89    (*p->xOp)(p);90    test_barrier();91    p->completed++;92    while( p->opnum<=p->completed ) sched_yield();93    test_barrier();94  }95  if( p->pStmt ){96    sqlite3_finalize(p->pStmt);97    p->pStmt = 0;98  }99  if( p->db ){100    sqlite3_close(p->db);101    p->db = 0;102  }103  if( p->zErr && p->zErr!=p->zStaticErr ){104    sqlite3_free(p->zErr);105    p->zErr = 0;106  }107  test_barrier();108  p->completed++;109#ifndef SQLITE_OMIT_DEPRECATED110  sqlite3_thread_cleanup();111#endif112  return 0;113}114 115/*116** Get a thread ID which is an upper case letter.  Return the index.117** If the argument is not a valid thread ID put an error message in118** the interpreter and return -1.119*/120static int parse_thread_id(Tcl_Interp *interp, const char *zArg){121  if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper((unsigned char)zArg[0]) ){122    Tcl_AppendResult(interp, "thread ID must be an upper case letter", NULL);123    return -1;124  }125  return zArg[0] - 'A';126}127 128/*129** Usage:    thread_create NAME  FILENAME130**131** NAME should be an upper case letter.  Start the thread running with132** an open connection to the given database.133*/134static int SQLITE_TCLAPI tcl_thread_create(135  void *NotUsed,136  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */137  int argc,              /* Number of arguments */138  const char **argv      /* Text of each argument */139){140  int i;141  pthread_t x;142  int rc;143 144  if( argc!=3 ){145    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],146       " ID FILENAME", NULL);147    return TCL_ERROR;148  }149  i = parse_thread_id(interp, argv[1]);150  if( i<0 ) return TCL_ERROR;151  if( threadset[i].busy ){152    Tcl_AppendResult(interp, "thread ", argv[1], " is already running", NULL);153    return TCL_ERROR;154  }155  threadset[i].busy = 1;156  sqlite3_free(threadset[i].zFilename);157  threadset[i].zFilename = sqlite3_mprintf("%s", argv[2]);158  threadset[i].opnum = 1;159  threadset[i].completed = 0;160  rc = pthread_create(&x, 0, test_thread_main, &threadset[i]);161  if( rc ){162    Tcl_AppendResult(interp, "failed to create the thread", NULL);163    sqlite3_free(threadset[i].zFilename);164    threadset[i].busy = 0;165    return TCL_ERROR;166  }167  pthread_detach(x);168  return TCL_OK;169}170 171/*172** Wait for a thread to reach its idle state.173*/174static void test_thread_wait(Thread *p){175  test_barrier();176  while( p->opnum>p->completed ) sched_yield();177  test_barrier();178}179 180/*181** Usage:  thread_wait ID182**183** Wait on thread ID to reach its idle state.184*/185static int SQLITE_TCLAPI tcl_thread_wait(186  void *NotUsed,187  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */188  int argc,              /* Number of arguments */189  const char **argv      /* Text of each argument */190){191  int i;192 193  if( argc!=2 ){194    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],195       " ID", NULL);196    return TCL_ERROR;197  }198  i = parse_thread_id(interp, argv[1]);199  if( i<0 ) return TCL_ERROR;200  if( !threadset[i].busy ){201    Tcl_AppendResult(interp, "no such thread", NULL);202    return TCL_ERROR;203  }204  test_thread_wait(&threadset[i]);205  return TCL_OK;206}207 208/*209** Stop a thread.210*/211static void test_stop_thread(Thread *p){212  test_thread_wait(p);213  p->xOp = 0;214  p->opnum++;215  test_thread_wait(p);216  sqlite3_free(p->zArg);217  p->zArg = 0;218  sqlite3_free(p->zFilename);219  p->zFilename = 0;220  p->busy = 0;221}222 223/*224** Usage:  thread_halt ID225**226** Cause a thread to shut itself down.  Wait for the shutdown to be227** completed.  If ID is "*" then stop all threads.228*/229static int SQLITE_TCLAPI tcl_thread_halt(230  void *NotUsed,231  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */232  int argc,              /* Number of arguments */233  const char **argv      /* Text of each argument */234){235  int i;236 237  if( argc!=2 ){238    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],239       " ID", NULL);240    return TCL_ERROR;241  }242  if( argv[1][0]=='*' && argv[1][1]==0 ){243    for(i=0; i<N_THREAD; i++){244      if( threadset[i].busy ) test_stop_thread(&threadset[i]);245    }246  }else{247    i = parse_thread_id(interp, argv[1]);248    if( i<0 ) return TCL_ERROR;249    if( !threadset[i].busy ){250      Tcl_AppendResult(interp, "no such thread", NULL);251      return TCL_ERROR;252    }253    test_stop_thread(&threadset[i]);254  }255  return TCL_OK;256}257 258/*259** Usage: thread_argc  ID260**261** Wait on the most recent thread_step to complete, then return the262** number of columns in the result set.263*/264static int SQLITE_TCLAPI tcl_thread_argc(265  void *NotUsed,266  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */267  int argc,              /* Number of arguments */268  const char **argv      /* Text of each argument */269){270  int i;271  char zBuf[100];272 273  if( argc!=2 ){274    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],275       " ID", NULL);276    return TCL_ERROR;277  }278  i = parse_thread_id(interp, argv[1]);279  if( i<0 ) return TCL_ERROR;280  if( !threadset[i].busy ){281    Tcl_AppendResult(interp, "no such thread", NULL);282    return TCL_ERROR;283  }284  test_thread_wait(&threadset[i]);285  sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", threadset[i].argc);286  Tcl_AppendResult(interp, zBuf, NULL);287  return TCL_OK;288}289 290/*291** Usage: thread_argv  ID   N292**293** Wait on the most recent thread_step to complete, then return the294** value of the N-th columns in the result set.295*/296static int SQLITE_TCLAPI tcl_thread_argv(297  void *NotUsed,298  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */299  int argc,              /* Number of arguments */300  const char **argv      /* Text of each argument */301){302  int i;303  int n;304 305  if( argc!=3 ){306    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],307       " ID N", NULL);308    return TCL_ERROR;309  }310  i = parse_thread_id(interp, argv[1]);311  if( i<0 ) return TCL_ERROR;312  if( !threadset[i].busy ){313    Tcl_AppendResult(interp, "no such thread", NULL);314    return TCL_ERROR;315  }316  if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;317  test_thread_wait(&threadset[i]);318  if( n<0 || n>=threadset[i].argc ){319    Tcl_AppendResult(interp, "column number out of range", NULL);320    return TCL_ERROR;321  }322  Tcl_AppendResult(interp, threadset[i].argv[n], NULL);323  return TCL_OK;324}325 326/*327** Usage: thread_colname  ID   N328**329** Wait on the most recent thread_step to complete, then return the330** name of the N-th columns in the result set.331*/332static int SQLITE_TCLAPI tcl_thread_colname(333  void *NotUsed,334  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */335  int argc,              /* Number of arguments */336  const char **argv      /* Text of each argument */337){338  int i;339  int n;340 341  if( argc!=3 ){342    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],343       " ID N", NULL);344    return TCL_ERROR;345  }346  i = parse_thread_id(interp, argv[1]);347  if( i<0 ) return TCL_ERROR;348  if( !threadset[i].busy ){349    Tcl_AppendResult(interp, "no such thread", NULL);350    return TCL_ERROR;351  }352  if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;353  test_thread_wait(&threadset[i]);354  if( n<0 || n>=threadset[i].argc ){355    Tcl_AppendResult(interp, "column number out of range", NULL);356    return TCL_ERROR;357  }358  Tcl_AppendResult(interp, threadset[i].colv[n], NULL);359  return TCL_OK;360}361 362/*363** Usage: thread_result  ID364**365** Wait on the most recent operation to complete, then return the366** result code from that operation.367*/368static int SQLITE_TCLAPI tcl_thread_result(369  void *NotUsed,370  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */371  int argc,              /* Number of arguments */372  const char **argv      /* Text of each argument */373){374  int i;375  const char *zName;376 377  if( argc!=2 ){378    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],379       " ID", NULL);380    return TCL_ERROR;381  }382  i = parse_thread_id(interp, argv[1]);383  if( i<0 ) return TCL_ERROR;384  if( !threadset[i].busy ){385    Tcl_AppendResult(interp, "no such thread", NULL);386    return TCL_ERROR;387  }388  test_thread_wait(&threadset[i]);389  zName = sqlite3ErrName(threadset[i].rc);390  Tcl_AppendResult(interp, zName, NULL);391  return TCL_OK;392}393 394/*395** Usage: thread_error  ID396**397** Wait on the most recent operation to complete, then return the398** error string.399*/400static int SQLITE_TCLAPI tcl_thread_error(401  void *NotUsed,402  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */403  int argc,              /* Number of arguments */404  const char **argv      /* Text of each argument */405){406  int i;407 408  if( argc!=2 ){409    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],410       " ID", NULL);411    return TCL_ERROR;412  }413  i = parse_thread_id(interp, argv[1]);414  if( i<0 ) return TCL_ERROR;415  if( !threadset[i].busy ){416    Tcl_AppendResult(interp, "no such thread", NULL);417    return TCL_ERROR;418  }419  test_thread_wait(&threadset[i]);420  Tcl_AppendResult(interp, threadset[i].zErr, NULL);421  return TCL_OK;422}423 424/*425** This procedure runs in the thread to compile an SQL statement.426*/427static void do_compile(Thread *p){428  if( p->db==0 ){429    p->zErr = p->zStaticErr = "no database is open";430    p->rc = SQLITE_ERROR;431    return;432  }433  if( p->pStmt ){434    sqlite3_finalize(p->pStmt);435    p->pStmt = 0;436  }437  p->rc = sqlite3_prepare(p->db, p->zArg, -1, &p->pStmt, 0);438}439 440/*441** Usage: thread_compile ID SQL442**443** Compile a new virtual machine.444*/445static int SQLITE_TCLAPI tcl_thread_compile(446  void *NotUsed,447  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */448  int argc,              /* Number of arguments */449  const char **argv      /* Text of each argument */450){451  int i;452  if( argc!=3 ){453    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],454       " ID SQL", NULL);455    return TCL_ERROR;456  }457  i = parse_thread_id(interp, argv[1]);458  if( i<0 ) return TCL_ERROR;459  if( !threadset[i].busy ){460    Tcl_AppendResult(interp, "no such thread", NULL);461    return TCL_ERROR;462  }463  test_thread_wait(&threadset[i]);464  threadset[i].xOp = do_compile;465  sqlite3_free(threadset[i].zArg);466  threadset[i].zArg = sqlite3_mprintf("%s", argv[2]);467  test_barrier();468  threadset[i].opnum++;469  return TCL_OK;470}471 472/*473** This procedure runs in the thread to step the virtual machine.474*/475static void do_step(Thread *p){476  int i;477  if( p->pStmt==0 ){478    p->zErr = p->zStaticErr = "no virtual machine available";479    p->rc = SQLITE_ERROR;480    return;481  }482  p->rc = sqlite3_step(p->pStmt);483  if( p->rc==SQLITE_ROW ){484    p->argc = sqlite3_column_count(p->pStmt);485    for(i=0; i<sqlite3_data_count(p->pStmt); i++){486      p->argv[i] = (char*)sqlite3_column_text(p->pStmt, i);487    }488    for(i=0; i<p->argc; i++){489      p->colv[i] = sqlite3_column_name(p->pStmt, i);490    }491  }492}493 494/*495** Usage: thread_step ID496**497** Advance the virtual machine by one step498*/499static int SQLITE_TCLAPI tcl_thread_step(500  void *NotUsed,501  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */502  int argc,              /* Number of arguments */503  const char **argv      /* Text of each argument */504){505  int i;506  if( argc!=2 ){507    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],508       " IDL", NULL);509    return TCL_ERROR;510  }511  i = parse_thread_id(interp, argv[1]);512  if( i<0 ) return TCL_ERROR;513  if( !threadset[i].busy ){514    Tcl_AppendResult(interp, "no such thread", NULL);515    return TCL_ERROR;516  }517  test_thread_wait(&threadset[i]);518  threadset[i].xOp = do_step;519  test_barrier();520  threadset[i].opnum++;521  return TCL_OK;522}523 524/*525** This procedure runs in the thread to finalize a virtual machine.526*/527static void do_finalize(Thread *p){528  if( p->pStmt==0 ){529    p->zErr = p->zStaticErr = "no virtual machine available";530    p->rc = SQLITE_ERROR;531    return;532  }533  p->rc = sqlite3_finalize(p->pStmt);534  p->pStmt = 0;535}536 537/*538** Usage: thread_finalize ID539**540** Finalize the virtual machine.541*/542static int SQLITE_TCLAPI tcl_thread_finalize(543  void *NotUsed,544  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */545  int argc,              /* Number of arguments */546  const char **argv      /* Text of each argument */547){548  int i;549  if( argc!=2 ){550    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],551       " IDL", NULL);552    return TCL_ERROR;553  }554  i = parse_thread_id(interp, argv[1]);555  if( i<0 ) return TCL_ERROR;556  if( !threadset[i].busy ){557    Tcl_AppendResult(interp, "no such thread", NULL);558    return TCL_ERROR;559  }560  test_thread_wait(&threadset[i]);561  threadset[i].xOp = do_finalize;562  sqlite3_free(threadset[i].zArg);563  threadset[i].zArg = 0;564  test_barrier();565  threadset[i].opnum++;566  return TCL_OK;567}568 569/*570** Usage: thread_swap ID ID571**572** Interchange the sqlite* pointer between two threads.573*/574static int SQLITE_TCLAPI tcl_thread_swap(575  void *NotUsed,576  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */577  int argc,              /* Number of arguments */578  const char **argv      /* Text of each argument */579){580  int i, j;581  sqlite3 *temp;582  if( argc!=3 ){583    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],584       " ID1 ID2", NULL);585    return TCL_ERROR;586  }587  i = parse_thread_id(interp, argv[1]);588  if( i<0 ) return TCL_ERROR;589  if( !threadset[i].busy ){590    Tcl_AppendResult(interp, "no such thread", NULL);591    return TCL_ERROR;592  }593  test_thread_wait(&threadset[i]);594  j = parse_thread_id(interp, argv[2]);595  if( j<0 ) return TCL_ERROR;596  if( !threadset[j].busy ){597    Tcl_AppendResult(interp, "no such thread", NULL);598    return TCL_ERROR;599  }600  test_thread_wait(&threadset[j]);601  temp = threadset[i].db;602  threadset[i].db = threadset[j].db;603  threadset[j].db = temp;604  return TCL_OK;605}606 607/*608** Usage: thread_db_get ID609**610** Return the database connection pointer for the given thread.  Then611** remove the pointer from the thread itself.  Afterwards, the thread612** can be stopped and the connection can be used by the main thread.613*/614static int SQLITE_TCLAPI tcl_thread_db_get(615  void *NotUsed,616  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */617  int argc,              /* Number of arguments */618  const char **argv      /* Text of each argument */619){620  int i;621  char zBuf[100];622  extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);623  if( argc!=2 ){624    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],625       " ID", NULL);626    return TCL_ERROR;627  }628  i = parse_thread_id(interp, argv[1]);629  if( i<0 ) return TCL_ERROR;630  if( !threadset[i].busy ){631    Tcl_AppendResult(interp, "no such thread", NULL);632    return TCL_ERROR;633  }634  test_thread_wait(&threadset[i]);635  sqlite3TestMakePointerStr(interp, zBuf, threadset[i].db);636  threadset[i].db = 0;637  Tcl_AppendResult(interp, zBuf, (char*)0);638  return TCL_OK;639}640 641/*642** Usage: thread_db_put ID DB643**644*/645static int SQLITE_TCLAPI tcl_thread_db_put(646  void *NotUsed,647  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */648  int argc,              /* Number of arguments */649  const char **argv      /* Text of each argument */650){651  int i;652  extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);653  extern void *sqlite3TestTextToPtr(const char *);654  if( argc!=3 ){655    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],656       " ID DB", NULL);657    return TCL_ERROR;658  }659  i = parse_thread_id(interp, argv[1]);660  if( i<0 ) return TCL_ERROR;661  if( !threadset[i].busy ){662    Tcl_AppendResult(interp, "no such thread", NULL);663    return TCL_ERROR;664  }665  test_thread_wait(&threadset[i]);666  assert( !threadset[i].db );667  threadset[i].db = (sqlite3*)sqlite3TestTextToPtr(argv[2]);668  return TCL_OK;669}670 671/*672** Usage: thread_stmt_get ID673**674** Return the database stmt pointer for the given thread.  Then675** remove the pointer from the thread itself. 676*/677static int SQLITE_TCLAPI tcl_thread_stmt_get(678  void *NotUsed,679  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */680  int argc,              /* Number of arguments */681  const char **argv      /* Text of each argument */682){683  int i;684  char zBuf[100];685  extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);686  if( argc!=2 ){687    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],688       " ID", NULL);689    return TCL_ERROR;690  }691  i = parse_thread_id(interp, argv[1]);692  if( i<0 ) return TCL_ERROR;693  if( !threadset[i].busy ){694    Tcl_AppendResult(interp, "no such thread", NULL);695    return TCL_ERROR;696  }697  test_thread_wait(&threadset[i]);698  sqlite3TestMakePointerStr(interp, zBuf, threadset[i].pStmt);699  threadset[i].pStmt = 0;700  Tcl_AppendResult(interp, zBuf, (char*)0);701  return TCL_OK;702}703 704/*705** Register commands with the TCL interpreter.706*/707int Sqlitetest4_Init(Tcl_Interp *interp){708  static struct {709     char *zName;710     Tcl_CmdProc *xProc;711  } aCmd[] = {712     { "thread_create",     (Tcl_CmdProc*)tcl_thread_create     },713     { "thread_wait",       (Tcl_CmdProc*)tcl_thread_wait       },714     { "thread_halt",       (Tcl_CmdProc*)tcl_thread_halt       },715     { "thread_argc",       (Tcl_CmdProc*)tcl_thread_argc       },716     { "thread_argv",       (Tcl_CmdProc*)tcl_thread_argv       },717     { "thread_colname",    (Tcl_CmdProc*)tcl_thread_colname    },718     { "thread_result",     (Tcl_CmdProc*)tcl_thread_result     },719     { "thread_error",      (Tcl_CmdProc*)tcl_thread_error      },720     { "thread_compile",    (Tcl_CmdProc*)tcl_thread_compile    },721     { "thread_step",       (Tcl_CmdProc*)tcl_thread_step       },722     { "thread_finalize",   (Tcl_CmdProc*)tcl_thread_finalize   },723     { "thread_swap",       (Tcl_CmdProc*)tcl_thread_swap       },724     { "thread_db_get",     (Tcl_CmdProc*)tcl_thread_db_get     },725     { "thread_db_put",     (Tcl_CmdProc*)tcl_thread_db_put     },726     { "thread_stmt_get",   (Tcl_CmdProc*)tcl_thread_stmt_get   },727  };728  int i;729 730  for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){731    Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);732  }733  return TCL_OK;734}735#else736int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; }737#endif /* SQLITE_OS_UNIX */738