Annotation of embedaddon/sqlite3/test/threadtest1.c, revision 1.1
1.1 ! misho 1: /*
! 2: ** 2002 January 15
! 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: ** This file implements a simple standalone program used to test whether
! 13: ** or not the SQLite library is threadsafe.
! 14: **
! 15: ** Testing the thread safety of SQLite is difficult because there are very
! 16: ** few places in the code that are even potentially unsafe, and those
! 17: ** places execute for very short periods of time. So even if the library
! 18: ** is compiled with its mutexes disabled, it is likely to work correctly
! 19: ** in a multi-threaded program most of the time.
! 20: **
! 21: ** This file is NOT part of the standard SQLite library. It is used for
! 22: ** testing only.
! 23: */
! 24: #include "sqlite.h"
! 25: #include <pthread.h>
! 26: #include <sched.h>
! 27: #include <stdio.h>
! 28: #include <stdlib.h>
! 29: #include <string.h>
! 30: #include <unistd.h>
! 31:
! 32: /*
! 33: ** Enable for tracing
! 34: */
! 35: static int verbose = 0;
! 36:
! 37: /*
! 38: ** Come here to die.
! 39: */
! 40: static void Exit(int rc){
! 41: exit(rc);
! 42: }
! 43:
! 44: extern char *sqlite3_mprintf(const char *zFormat, ...);
! 45: extern char *sqlite3_vmprintf(const char *zFormat, va_list);
! 46:
! 47: /*
! 48: ** When a lock occurs, yield.
! 49: */
! 50: static int db_is_locked(void *NotUsed, int iCount){
! 51: /* sched_yield(); */
! 52: if( verbose ) printf("BUSY %s #%d\n", (char*)NotUsed, iCount);
! 53: usleep(100);
! 54: return iCount<25;
! 55: }
! 56:
! 57: /*
! 58: ** Used to accumulate query results by db_query()
! 59: */
! 60: struct QueryResult {
! 61: const char *zFile; /* Filename - used for error reporting */
! 62: int nElem; /* Number of used entries in azElem[] */
! 63: int nAlloc; /* Number of slots allocated for azElem[] */
! 64: char **azElem; /* The result of the query */
! 65: };
! 66:
! 67: /*
! 68: ** The callback function for db_query
! 69: */
! 70: static int db_query_callback(
! 71: void *pUser, /* Pointer to the QueryResult structure */
! 72: int nArg, /* Number of columns in this result row */
! 73: char **azArg, /* Text of data in all columns */
! 74: char **NotUsed /* Names of the columns */
! 75: ){
! 76: struct QueryResult *pResult = (struct QueryResult*)pUser;
! 77: int i;
! 78: if( pResult->nElem + nArg >= pResult->nAlloc ){
! 79: if( pResult->nAlloc==0 ){
! 80: pResult->nAlloc = nArg+1;
! 81: }else{
! 82: pResult->nAlloc = pResult->nAlloc*2 + nArg + 1;
! 83: }
! 84: pResult->azElem = realloc( pResult->azElem, pResult->nAlloc*sizeof(char*));
! 85: if( pResult->azElem==0 ){
! 86: fprintf(stdout,"%s: malloc failed\n", pResult->zFile);
! 87: return 1;
! 88: }
! 89: }
! 90: if( azArg==0 ) return 0;
! 91: for(i=0; i<nArg; i++){
! 92: pResult->azElem[pResult->nElem++] =
! 93: sqlite3_mprintf("%s",azArg[i] ? azArg[i] : "");
! 94: }
! 95: return 0;
! 96: }
! 97:
! 98: /*
! 99: ** Execute a query against the database. NULL values are returned
! 100: ** as an empty string. The list is terminated by a single NULL pointer.
! 101: */
! 102: char **db_query(sqlite *db, const char *zFile, const char *zFormat, ...){
! 103: char *zSql;
! 104: int rc;
! 105: char *zErrMsg = 0;
! 106: va_list ap;
! 107: struct QueryResult sResult;
! 108: va_start(ap, zFormat);
! 109: zSql = sqlite3_vmprintf(zFormat, ap);
! 110: va_end(ap);
! 111: memset(&sResult, 0, sizeof(sResult));
! 112: sResult.zFile = zFile;
! 113: if( verbose ) printf("QUERY %s: %s\n", zFile, zSql);
! 114: rc = sqlite3_exec(db, zSql, db_query_callback, &sResult, &zErrMsg);
! 115: if( rc==SQLITE_SCHEMA ){
! 116: if( zErrMsg ) free(zErrMsg);
! 117: rc = sqlite3_exec(db, zSql, db_query_callback, &sResult, &zErrMsg);
! 118: }
! 119: if( verbose ) printf("DONE %s %s\n", zFile, zSql);
! 120: if( zErrMsg ){
! 121: fprintf(stdout,"%s: query failed: %s - %s\n", zFile, zSql, zErrMsg);
! 122: free(zErrMsg);
! 123: free(zSql);
! 124: Exit(1);
! 125: }
! 126: sqlite3_free(zSql);
! 127: if( sResult.azElem==0 ){
! 128: db_query_callback(&sResult, 0, 0, 0);
! 129: }
! 130: sResult.azElem[sResult.nElem] = 0;
! 131: return sResult.azElem;
! 132: }
! 133:
! 134: /*
! 135: ** Execute an SQL statement.
! 136: */
! 137: void db_execute(sqlite *db, const char *zFile, const char *zFormat, ...){
! 138: char *zSql;
! 139: int rc;
! 140: char *zErrMsg = 0;
! 141: va_list ap;
! 142: va_start(ap, zFormat);
! 143: zSql = sqlite3_vmprintf(zFormat, ap);
! 144: va_end(ap);
! 145: if( verbose ) printf("EXEC %s: %s\n", zFile, zSql);
! 146: do{
! 147: rc = sqlite3_exec(db, zSql, 0, 0, &zErrMsg);
! 148: }while( rc==SQLITE_BUSY );
! 149: if( verbose ) printf("DONE %s: %s\n", zFile, zSql);
! 150: if( zErrMsg ){
! 151: fprintf(stdout,"%s: command failed: %s - %s\n", zFile, zSql, zErrMsg);
! 152: free(zErrMsg);
! 153: sqlite3_free(zSql);
! 154: Exit(1);
! 155: }
! 156: sqlite3_free(zSql);
! 157: }
! 158:
! 159: /*
! 160: ** Free the results of a db_query() call.
! 161: */
! 162: void db_query_free(char **az){
! 163: int i;
! 164: for(i=0; az[i]; i++){
! 165: sqlite3_free(az[i]);
! 166: }
! 167: free(az);
! 168: }
! 169:
! 170: /*
! 171: ** Check results
! 172: */
! 173: void db_check(const char *zFile, const char *zMsg, char **az, ...){
! 174: va_list ap;
! 175: int i;
! 176: char *z;
! 177: va_start(ap, az);
! 178: for(i=0; (z = va_arg(ap, char*))!=0; i++){
! 179: if( az[i]==0 || strcmp(az[i],z)!=0 ){
! 180: fprintf(stdout,"%s: %s: bad result in column %d: %s\n",
! 181: zFile, zMsg, i+1, az[i]);
! 182: db_query_free(az);
! 183: Exit(1);
! 184: }
! 185: }
! 186: va_end(ap);
! 187: db_query_free(az);
! 188: }
! 189:
! 190: pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
! 191: pthread_cond_t sig = PTHREAD_COND_INITIALIZER;
! 192: int thread_cnt = 0;
! 193:
! 194: static void *worker_bee(void *pArg){
! 195: const char *zFilename = (char*)pArg;
! 196: char *azErr;
! 197: int i, cnt;
! 198: int t = atoi(zFilename);
! 199: char **az;
! 200: sqlite *db;
! 201:
! 202: pthread_mutex_lock(&lock);
! 203: thread_cnt++;
! 204: pthread_mutex_unlock(&lock);
! 205: printf("%s: START\n", zFilename);
! 206: fflush(stdout);
! 207: for(cnt=0; cnt<10; cnt++){
! 208: sqlite3_open(&zFilename[2], &db);
! 209: if( db==0 ){
! 210: fprintf(stdout,"%s: can't open\n", zFilename);
! 211: Exit(1);
! 212: }
! 213: sqlite3_busy_handler(db, db_is_locked, zFilename);
! 214: db_execute(db, zFilename, "CREATE TABLE t%d(a,b,c);", t);
! 215: for(i=1; i<=100; i++){
! 216: db_execute(db, zFilename, "INSERT INTO t%d VALUES(%d,%d,%d);",
! 217: t, i, i*2, i*i);
! 218: }
! 219: az = db_query(db, zFilename, "SELECT count(*) FROM t%d", t);
! 220: db_check(zFilename, "tX size", az, "100", 0);
! 221: az = db_query(db, zFilename, "SELECT avg(b) FROM t%d", t);
! 222: db_check(zFilename, "tX avg", az, "101", 0);
! 223: db_execute(db, zFilename, "DELETE FROM t%d WHERE a>50", t);
! 224: az = db_query(db, zFilename, "SELECT avg(b) FROM t%d", t);
! 225: db_check(zFilename, "tX avg2", az, "51", 0);
! 226: for(i=1; i<=50; i++){
! 227: char z1[30], z2[30];
! 228: az = db_query(db, zFilename, "SELECT b, c FROM t%d WHERE a=%d", t, i);
! 229: sprintf(z1, "%d", i*2);
! 230: sprintf(z2, "%d", i*i);
! 231: db_check(zFilename, "readback", az, z1, z2, 0);
! 232: }
! 233: db_execute(db, zFilename, "DROP TABLE t%d;", t);
! 234: sqlite3_close(db);
! 235: }
! 236: printf("%s: END\n", zFilename);
! 237: /* unlink(zFilename); */
! 238: fflush(stdout);
! 239: pthread_mutex_lock(&lock);
! 240: thread_cnt--;
! 241: if( thread_cnt<=0 ){
! 242: pthread_cond_signal(&sig);
! 243: }
! 244: pthread_mutex_unlock(&lock);
! 245: return 0;
! 246: }
! 247:
! 248: int main(int argc, char **argv){
! 249: char *zFile;
! 250: int i, n;
! 251: pthread_t id;
! 252: if( argc>2 && strcmp(argv[1], "-v")==0 ){
! 253: verbose = 1;
! 254: argc--;
! 255: argv++;
! 256: }
! 257: if( argc<2 || (n=atoi(argv[1]))<1 ) n = 10;
! 258: for(i=0; i<n; i++){
! 259: char zBuf[200];
! 260: sprintf(zBuf, "testdb-%d", (i+1)/2);
! 261: unlink(zBuf);
! 262: }
! 263: for(i=0; i<n; i++){
! 264: zFile = sqlite3_mprintf("%d.testdb-%d", i%2+1, (i+2)/2);
! 265: if( (i%2)==0 ){
! 266: /* Remove both the database file and any old journal for the file
! 267: ** being used by this thread and the next one. */
! 268: char *zDb = &zFile[2];
! 269: char *zJournal = sqlite3_mprintf("%s-journal", zDb);
! 270: unlink(zDb);
! 271: unlink(zJournal);
! 272: free(zJournal);
! 273: }
! 274:
! 275: pthread_create(&id, 0, worker_bee, (void*)zFile);
! 276: pthread_detach(id);
! 277: }
! 278: pthread_mutex_lock(&lock);
! 279: while( thread_cnt>0 ){
! 280: pthread_cond_wait(&sig, &lock);
! 281: }
! 282: pthread_mutex_unlock(&lock);
! 283: for(i=0; i<n; i++){
! 284: char zBuf[200];
! 285: sprintf(zBuf, "testdb-%d", (i+1)/2);
! 286: unlink(zBuf);
! 287: }
! 288: return 0;
! 289: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>