From 63847496f14c813a5d80efd5b7de0f1294ffe1e3 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 16:07:11 +0200 Subject: Adding upstream version 3.45.1. Signed-off-by: Daniel Baumann --- test/threadtest5.c | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 test/threadtest5.c (limited to 'test/threadtest5.c') diff --git a/test/threadtest5.c b/test/threadtest5.c new file mode 100644 index 0000000..6e6610f --- /dev/null +++ b/test/threadtest5.c @@ -0,0 +1,339 @@ +/* +** 2021-05-12 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** Testing threading behavior when multiple database connections in separate +** threads of the same process are all talking to the same database file. +** +** For best results, ensure that SQLite is compiled with HAVE_USLEEP=1 +** +** Only works on unix platforms. +** +** Usage: +** +** ./threadtest5 ?DATABASE? +** +** If DATABASE is omitted, it defaults to using file:/mem?vfs=memdb. +*/ +#include "sqlite3.h" +#include +#include +#include +#include +#include +#include + +/* Name of the in-memory database */ +static char *zDbName = 0; + +/* True for debugging */ +static int eVerbose = 0; + +/* If rc is not SQLITE_OK, then print an error message and stop +** the test. +*/ +static void error_out(int rc, const char *zCtx, int lineno){ + if( rc!=SQLITE_OK ){ + fprintf(stderr, "error %d at %d in \"%s\"\n", rc, lineno, zCtx); + exit(-1); + } +} + +#if 0 +/* Return the number of milliseconds since the Julian epoch (-4714-11-24). +*/ +static sqlite3_int64 gettime(void){ + sqlite3_int64 tm; + sqlite3_vfs *pVfs = sqlite3_vfs_find(0); + pVfs->xCurrentTimeInt64(pVfs, &tm); + return tm; +} +#endif + +/* Run the SQL in the second argument. +*/ +static int exec( + sqlite3 *db, + const char *zId, + int lineno, + const char *zFormat, + ... +){ + int rc; + va_list ap; + char *zSql; + va_start(ap, zFormat); + zSql = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + if( eVerbose){ + printf("%s:%d: [%s]\n", zId, lineno, zSql); + fflush(stdout); + } + rc = sqlite3_exec(db, zSql, 0, 0, 0); + if( rc && eVerbose ){ + printf("%s:%d: return-code %d\n", zId, lineno, rc); + fflush(stdout); + } + sqlite3_free(zSql); + return rc; +} + +/* Generate a perpared statement from the input SQL +*/ +static sqlite3_stmt *prepare( + sqlite3 *db, + const char *zId, + int lineno, + const char *zFormat, + ... +){ + int rc; + va_list ap; + char *zSql; + sqlite3_stmt *pStmt = 0; + va_start(ap, zFormat); + zSql = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + if( eVerbose){ + printf("%s:%d: [%s]\n", zId, lineno, zSql); + fflush(stdout); + } + + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + if( rc ){ + printf("%s:%d: ERROR - %s\n", zId, lineno, sqlite3_errmsg(db)); + exit(-1); + } + sqlite3_free(zSql); + return pStmt; +} + +/* +** Wait for table zTable to exist in the schema. +*/ +static void waitOnTable(sqlite3 *db, const char *zWorker, const char *zTable){ + while(1){ + int eFound = 0; + sqlite3_stmt *q = prepare(db, zWorker, __LINE__, + "SELECT 1 FROM sqlite_schema WHERE name=%Q", zTable); + if( sqlite3_step(q)==SQLITE_ROW && sqlite3_column_int(q,0)!=0 ){ + eFound = 1; + } + sqlite3_finalize(q); + if( eFound ) return; + sqlite3_sleep(1); + } +} + +/* +** Return true if x is a prime number +*/ +static int isPrime(int x){ + int i; + if( x<2 ) return 1; + for(i=2; i*i<=x; i++){ + if( (x%i)==0 ) return 0; + } + return 1; +} + +/* Each worker thread runs an instance of the following */ +static void *worker(void *pArg){ + int rc; + const char *zName = (const char*)pArg; + sqlite3 *db = 0; + + if( eVerbose ){ + printf("%s: startup\n", zName); + fflush(stdout); + } + + rc = sqlite3_open(zDbName, &db); + error_out(rc, "sqlite3_open", __LINE__); + sqlite3_busy_timeout(db, 2000); + + while( 1 ){ + sqlite3_stmt *q1; + int tid = -1; + q1 = prepare(db, zName, __LINE__, + "UPDATE task SET doneby=%Q" + " WHERE tid=(SELECT tid FROM task WHERE doneby IS NULL LIMIT 1)" + "RETURNING tid", zName + ); + if( sqlite3_step(q1)==SQLITE_ROW ){ + tid = sqlite3_column_int(q1,0); + } + sqlite3_finalize(q1); + if( tid<0 ) break; + if( eVerbose ){ + printf("%s: starting task %d\n", zName, tid); + fflush(stdout); + } + if( tid==1 ){ + exec(db, zName, __LINE__, + "CREATE TABLE IF NOT EXISTS p1(x INTEGER PRIMARY KEY);" + ); + }else if( tid>=2 && tid<=51 ){ + int a, b, i; + waitOnTable(db, zName, "p1"); + a = (tid-2)*200 + 1; + b = a+200; + for(i=a; i=53 && tid<=62 ){ + int a, b, i; + waitOnTable(db, zName, "p2"); + a = (tid-53)*10 + 2; + b = a+9; + for(i=a; i<=b; i++){ + exec(db, zName, __LINE__, + "DELETE FROM p2 WHERE x>%d AND (x %% %d)==0", i, i); + } + } + if( eVerbose ){ + printf("%s: completed task %d\n", zName, tid); + fflush(stdout); + } + sqlite3_sleep(1); + } + + sqlite3_close(db); + + if( eVerbose ){ + printf("%s: exit\n", zName); + fflush(stdout); + } + return 0; +} + +/* Print a usage comment and die */ +static void usage(const char *argv0){ + printf("Usage: %s [options]\n", argv0); + printf( + " -num-workers N Run N worker threads\n" + " -v Debugging output\n" + ); + exit(1); +} + +/* Maximum number of threads */ +#define MX_WORKER 100 + +/* +** Main routine +*/ +int main(int argc, char **argv){ + int i; + int nWorker = 4; + int rc; + sqlite3 *db = 0; + sqlite3_stmt *q; + pthread_t aWorker[MX_WORKER]; + char aWorkerName[MX_WORKER][8]; + + for(i=1; iMX_WORKER ){ + printf("number of threads must be between 1 and %d\n", MX_WORKER); + exit(1); + } + continue; + } + printf("unknown option: %s\n", argv[i]); + usage(argv[0]); + } + if( zDbName==0 ) zDbName = "file:/mem?vfs=memdb"; + + sqlite3_config(SQLITE_CONFIG_URI, (int)1); + rc = sqlite3_open(zDbName, &db); + error_out(rc, "sqlite3_open", __LINE__); + + rc = exec(db, "SETUP", __LINE__, + "DROP TABLE IF EXISTS task;\n" + "DROP TABLE IF EXISTS p1;\n" + "DROP TABLE IF EXISTS p2;\n" + "DROP TABLE IF EXISTS verify;\n" + "CREATE TABLE IF NOT EXISTS task(\n" + " tid INTEGER PRIMARY KEY,\n" + " doneby TEXT\n" + ");\n" + "WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100)" + "INSERT INTO task(tid) SELECT x FROM c;\n" + ); + error_out(rc, "sqlite3_exec", __LINE__); + + for(i=0; i