1*85ffcae1Sdrh /*
2*85ffcae1Sdrh ** 2021-05-12
3*85ffcae1Sdrh **
4*85ffcae1Sdrh ** The author disclaims copyright to this source code. In place of
5*85ffcae1Sdrh ** a legal notice, here is a blessing:
6*85ffcae1Sdrh **
7*85ffcae1Sdrh ** May you do good and not evil.
8*85ffcae1Sdrh ** May you find forgiveness for yourself and forgive others.
9*85ffcae1Sdrh ** May you share freely, never taking more than you give.
10*85ffcae1Sdrh **
11*85ffcae1Sdrh *************************************************************************
12*85ffcae1Sdrh **
13*85ffcae1Sdrh ** Testing threading behavior when multiple database connections in separate
14*85ffcae1Sdrh ** threads of the same process are all talking to the same database file.
15*85ffcae1Sdrh **
16*85ffcae1Sdrh ** For best results, ensure that SQLite is compiled with HAVE_USLEEP=1
17*85ffcae1Sdrh **
18*85ffcae1Sdrh ** Only works on unix platforms.
19*85ffcae1Sdrh **
20*85ffcae1Sdrh ** Usage:
21*85ffcae1Sdrh **
22*85ffcae1Sdrh ** ./threadtest5 ?DATABASE?
23*85ffcae1Sdrh **
24*85ffcae1Sdrh ** If DATABASE is omitted, it defaults to using file:/mem?vfs=memdb.
25*85ffcae1Sdrh */
26*85ffcae1Sdrh #include "sqlite3.h"
27*85ffcae1Sdrh #include <pthread.h>
28*85ffcae1Sdrh #include <stdio.h>
29*85ffcae1Sdrh #include <unistd.h>
30*85ffcae1Sdrh #include <stdlib.h>
31*85ffcae1Sdrh #include <string.h>
32*85ffcae1Sdrh #include <stdarg.h>
33*85ffcae1Sdrh
34*85ffcae1Sdrh /* Name of the in-memory database */
35*85ffcae1Sdrh static char *zDbName = 0;
36*85ffcae1Sdrh
37*85ffcae1Sdrh /* True for debugging */
38*85ffcae1Sdrh static int eVerbose = 0;
39*85ffcae1Sdrh
40*85ffcae1Sdrh /* If rc is not SQLITE_OK, then print an error message and stop
41*85ffcae1Sdrh ** the test.
42*85ffcae1Sdrh */
error_out(int rc,const char * zCtx,int lineno)43*85ffcae1Sdrh static void error_out(int rc, const char *zCtx, int lineno){
44*85ffcae1Sdrh if( rc!=SQLITE_OK ){
45*85ffcae1Sdrh fprintf(stderr, "error %d at %d in \"%s\"\n", rc, lineno, zCtx);
46*85ffcae1Sdrh exit(-1);
47*85ffcae1Sdrh }
48*85ffcae1Sdrh }
49*85ffcae1Sdrh
50*85ffcae1Sdrh #if 0
51*85ffcae1Sdrh /* Return the number of milliseconds since the Julian epoch (-4714-11-24).
52*85ffcae1Sdrh */
53*85ffcae1Sdrh static sqlite3_int64 gettime(void){
54*85ffcae1Sdrh sqlite3_int64 tm;
55*85ffcae1Sdrh sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
56*85ffcae1Sdrh pVfs->xCurrentTimeInt64(pVfs, &tm);
57*85ffcae1Sdrh return tm;
58*85ffcae1Sdrh }
59*85ffcae1Sdrh #endif
60*85ffcae1Sdrh
61*85ffcae1Sdrh /* Run the SQL in the second argument.
62*85ffcae1Sdrh */
exec(sqlite3 * db,const char * zId,int lineno,const char * zFormat,...)63*85ffcae1Sdrh static int exec(
64*85ffcae1Sdrh sqlite3 *db,
65*85ffcae1Sdrh const char *zId,
66*85ffcae1Sdrh int lineno,
67*85ffcae1Sdrh const char *zFormat,
68*85ffcae1Sdrh ...
69*85ffcae1Sdrh ){
70*85ffcae1Sdrh int rc;
71*85ffcae1Sdrh va_list ap;
72*85ffcae1Sdrh char *zSql;
73*85ffcae1Sdrh va_start(ap, zFormat);
74*85ffcae1Sdrh zSql = sqlite3_vmprintf(zFormat, ap);
75*85ffcae1Sdrh va_end(ap);
76*85ffcae1Sdrh if( eVerbose){
77*85ffcae1Sdrh printf("%s:%d: [%s]\n", zId, lineno, zSql);
78*85ffcae1Sdrh fflush(stdout);
79*85ffcae1Sdrh }
80*85ffcae1Sdrh rc = sqlite3_exec(db, zSql, 0, 0, 0);
81*85ffcae1Sdrh if( rc && eVerbose ){
82*85ffcae1Sdrh printf("%s:%d: return-code %d\n", zId, lineno, rc);
83*85ffcae1Sdrh fflush(stdout);
84*85ffcae1Sdrh }
85*85ffcae1Sdrh sqlite3_free(zSql);
86*85ffcae1Sdrh return rc;
87*85ffcae1Sdrh }
88*85ffcae1Sdrh
89*85ffcae1Sdrh /* Generate a perpared statement from the input SQL
90*85ffcae1Sdrh */
prepare(sqlite3 * db,const char * zId,int lineno,const char * zFormat,...)91*85ffcae1Sdrh static sqlite3_stmt *prepare(
92*85ffcae1Sdrh sqlite3 *db,
93*85ffcae1Sdrh const char *zId,
94*85ffcae1Sdrh int lineno,
95*85ffcae1Sdrh const char *zFormat,
96*85ffcae1Sdrh ...
97*85ffcae1Sdrh ){
98*85ffcae1Sdrh int rc;
99*85ffcae1Sdrh va_list ap;
100*85ffcae1Sdrh char *zSql;
101*85ffcae1Sdrh sqlite3_stmt *pStmt = 0;
102*85ffcae1Sdrh va_start(ap, zFormat);
103*85ffcae1Sdrh zSql = sqlite3_vmprintf(zFormat, ap);
104*85ffcae1Sdrh va_end(ap);
105*85ffcae1Sdrh if( eVerbose){
106*85ffcae1Sdrh printf("%s:%d: [%s]\n", zId, lineno, zSql);
107*85ffcae1Sdrh fflush(stdout);
108*85ffcae1Sdrh }
109*85ffcae1Sdrh
110*85ffcae1Sdrh rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
111*85ffcae1Sdrh if( rc ){
112*85ffcae1Sdrh printf("%s:%d: ERROR - %s\n", zId, lineno, sqlite3_errmsg(db));
113*85ffcae1Sdrh exit(-1);
114*85ffcae1Sdrh }
115*85ffcae1Sdrh sqlite3_free(zSql);
116*85ffcae1Sdrh return pStmt;
117*85ffcae1Sdrh }
118*85ffcae1Sdrh
119*85ffcae1Sdrh /*
120*85ffcae1Sdrh ** Wait for table zTable to exist in the schema.
121*85ffcae1Sdrh */
waitOnTable(sqlite3 * db,const char * zWorker,const char * zTable)122*85ffcae1Sdrh static void waitOnTable(sqlite3 *db, const char *zWorker, const char *zTable){
123*85ffcae1Sdrh while(1){
124*85ffcae1Sdrh int eFound = 0;
125*85ffcae1Sdrh sqlite3_stmt *q = prepare(db, zWorker, __LINE__,
126*85ffcae1Sdrh "SELECT 1 FROM sqlite_schema WHERE name=%Q", zTable);
127*85ffcae1Sdrh if( sqlite3_step(q)==SQLITE_ROW && sqlite3_column_int(q,0)!=0 ){
128*85ffcae1Sdrh eFound = 1;
129*85ffcae1Sdrh }
130*85ffcae1Sdrh sqlite3_finalize(q);
131*85ffcae1Sdrh if( eFound ) return;
132*85ffcae1Sdrh sqlite3_sleep(1);
133*85ffcae1Sdrh }
134*85ffcae1Sdrh }
135*85ffcae1Sdrh
136*85ffcae1Sdrh /*
137*85ffcae1Sdrh ** Return true if x is a prime number
138*85ffcae1Sdrh */
isPrime(int x)139*85ffcae1Sdrh static int isPrime(int x){
140*85ffcae1Sdrh int i;
141*85ffcae1Sdrh if( x<2 ) return 1;
142*85ffcae1Sdrh for(i=2; i*i<=x; i++){
143*85ffcae1Sdrh if( (x%i)==0 ) return 0;
144*85ffcae1Sdrh }
145*85ffcae1Sdrh return 1;
146*85ffcae1Sdrh }
147*85ffcae1Sdrh
148*85ffcae1Sdrh /* Each worker thread runs an instance of the following */
worker(void * pArg)149*85ffcae1Sdrh static void *worker(void *pArg){
150*85ffcae1Sdrh int rc;
151*85ffcae1Sdrh const char *zName = (const char*)pArg;
152*85ffcae1Sdrh sqlite3 *db = 0;
153*85ffcae1Sdrh
154*85ffcae1Sdrh if( eVerbose ){
155*85ffcae1Sdrh printf("%s: startup\n", zName);
156*85ffcae1Sdrh fflush(stdout);
157*85ffcae1Sdrh }
158*85ffcae1Sdrh
159*85ffcae1Sdrh rc = sqlite3_open(zDbName, &db);
160*85ffcae1Sdrh error_out(rc, "sqlite3_open", __LINE__);
161*85ffcae1Sdrh sqlite3_busy_timeout(db, 2000);
162*85ffcae1Sdrh
163*85ffcae1Sdrh while( 1 ){
164*85ffcae1Sdrh sqlite3_stmt *q1;
165*85ffcae1Sdrh int tid = -1;
166*85ffcae1Sdrh q1 = prepare(db, zName, __LINE__,
167*85ffcae1Sdrh "UPDATE task SET doneby=%Q"
168*85ffcae1Sdrh " WHERE tid=(SELECT tid FROM task WHERE doneby IS NULL LIMIT 1)"
169*85ffcae1Sdrh "RETURNING tid", zName
170*85ffcae1Sdrh );
171*85ffcae1Sdrh if( sqlite3_step(q1)==SQLITE_ROW ){
172*85ffcae1Sdrh tid = sqlite3_column_int(q1,0);
173*85ffcae1Sdrh }
174*85ffcae1Sdrh sqlite3_finalize(q1);
175*85ffcae1Sdrh if( tid<0 ) break;
176*85ffcae1Sdrh if( eVerbose ){
177*85ffcae1Sdrh printf("%s: starting task %d\n", zName, tid);
178*85ffcae1Sdrh fflush(stdout);
179*85ffcae1Sdrh }
180*85ffcae1Sdrh if( tid==1 ){
181*85ffcae1Sdrh exec(db, zName, __LINE__,
182*85ffcae1Sdrh "CREATE TABLE IF NOT EXISTS p1(x INTEGER PRIMARY KEY);"
183*85ffcae1Sdrh );
184*85ffcae1Sdrh }else if( tid>=2 && tid<=51 ){
185*85ffcae1Sdrh int a, b, i;
186*85ffcae1Sdrh waitOnTable(db, zName, "p1");
187*85ffcae1Sdrh a = (tid-2)*200 + 1;
188*85ffcae1Sdrh b = a+200;
189*85ffcae1Sdrh for(i=a; i<b; i++){
190*85ffcae1Sdrh if( isPrime(i) ){
191*85ffcae1Sdrh exec(db, zName, __LINE__,
192*85ffcae1Sdrh "INSERT INTO p1(x) VALUES(%d)", i);
193*85ffcae1Sdrh }
194*85ffcae1Sdrh }
195*85ffcae1Sdrh }else if( tid==52 ){
196*85ffcae1Sdrh exec(db, zName, __LINE__,
197*85ffcae1Sdrh "CREATE TABLE IF NOT EXISTS p2(x INTEGER PRIMARY KEY);"
198*85ffcae1Sdrh "WITH RECURSIVE"
199*85ffcae1Sdrh " c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<10000)"
200*85ffcae1Sdrh "INSERT INTO p2(x) SELECT x FROM c;"
201*85ffcae1Sdrh );
202*85ffcae1Sdrh }else if( tid>=53 && tid<=62 ){
203*85ffcae1Sdrh int a, b, i;
204*85ffcae1Sdrh waitOnTable(db, zName, "p2");
205*85ffcae1Sdrh a = (tid-53)*10 + 2;
206*85ffcae1Sdrh b = a+9;
207*85ffcae1Sdrh for(i=a; i<=b; i++){
208*85ffcae1Sdrh exec(db, zName, __LINE__,
209*85ffcae1Sdrh "DELETE FROM p2 WHERE x>%d AND (x %% %d)==0", i, i);
210*85ffcae1Sdrh }
211*85ffcae1Sdrh }
212*85ffcae1Sdrh if( eVerbose ){
213*85ffcae1Sdrh printf("%s: completed task %d\n", zName, tid);
214*85ffcae1Sdrh fflush(stdout);
215*85ffcae1Sdrh }
216*85ffcae1Sdrh sqlite3_sleep(1);
217*85ffcae1Sdrh }
218*85ffcae1Sdrh
219*85ffcae1Sdrh sqlite3_close(db);
220*85ffcae1Sdrh
221*85ffcae1Sdrh if( eVerbose ){
222*85ffcae1Sdrh printf("%s: exit\n", zName);
223*85ffcae1Sdrh fflush(stdout);
224*85ffcae1Sdrh }
225*85ffcae1Sdrh return 0;
226*85ffcae1Sdrh }
227*85ffcae1Sdrh
228*85ffcae1Sdrh /* Print a usage comment and die */
usage(const char * argv0)229*85ffcae1Sdrh static void usage(const char *argv0){
230*85ffcae1Sdrh printf("Usage: %s [options]\n", argv0);
231*85ffcae1Sdrh printf(
232*85ffcae1Sdrh " -num-workers N Run N worker threads\n"
233*85ffcae1Sdrh " -v Debugging output\n"
234*85ffcae1Sdrh );
235*85ffcae1Sdrh exit(1);
236*85ffcae1Sdrh }
237*85ffcae1Sdrh
238*85ffcae1Sdrh /* Maximum number of threads */
239*85ffcae1Sdrh #define MX_WORKER 100
240*85ffcae1Sdrh
241*85ffcae1Sdrh /*
242*85ffcae1Sdrh ** Main routine
243*85ffcae1Sdrh */
main(int argc,char ** argv)244*85ffcae1Sdrh int main(int argc, char **argv){
245*85ffcae1Sdrh int i;
246*85ffcae1Sdrh int nWorker = 4;
247*85ffcae1Sdrh int rc;
248*85ffcae1Sdrh sqlite3 *db = 0;
249*85ffcae1Sdrh sqlite3_stmt *q;
250*85ffcae1Sdrh pthread_t aWorker[MX_WORKER];
251*85ffcae1Sdrh char aWorkerName[MX_WORKER][8];
252*85ffcae1Sdrh
253*85ffcae1Sdrh for(i=1; i<argc; i++){
254*85ffcae1Sdrh const char *zArg = argv[i];
255*85ffcae1Sdrh if( zArg[0]!='-' ){
256*85ffcae1Sdrh if( zDbName==0 ){
257*85ffcae1Sdrh zDbName = argv[i];
258*85ffcae1Sdrh continue;
259*85ffcae1Sdrh }
260*85ffcae1Sdrh printf("unknown argument: %s\n", zArg);
261*85ffcae1Sdrh usage(argv[0]);
262*85ffcae1Sdrh }
263*85ffcae1Sdrh if( zArg[1]=='-' ) zArg++;
264*85ffcae1Sdrh if( strcmp(zArg, "-v")==0 ){
265*85ffcae1Sdrh eVerbose = 1;
266*85ffcae1Sdrh continue;
267*85ffcae1Sdrh }
268*85ffcae1Sdrh if( strcmp(zArg, "-num-workers")==0 && i+1<argc ){
269*85ffcae1Sdrh nWorker = atoi(argv[++i]);
270*85ffcae1Sdrh if( nWorker<1 || nWorker>MX_WORKER ){
271*85ffcae1Sdrh printf("number of threads must be between 1 and %d\n", MX_WORKER);
272*85ffcae1Sdrh exit(1);
273*85ffcae1Sdrh }
274*85ffcae1Sdrh continue;
275*85ffcae1Sdrh }
276*85ffcae1Sdrh printf("unknown option: %s\n", argv[i]);
277*85ffcae1Sdrh usage(argv[0]);
278*85ffcae1Sdrh }
279*85ffcae1Sdrh if( zDbName==0 ) zDbName = "file:/mem?vfs=memdb";
280*85ffcae1Sdrh
281*85ffcae1Sdrh sqlite3_config(SQLITE_CONFIG_URI, (int)1);
282*85ffcae1Sdrh rc = sqlite3_open(zDbName, &db);
283*85ffcae1Sdrh error_out(rc, "sqlite3_open", __LINE__);
284*85ffcae1Sdrh
285*85ffcae1Sdrh rc = exec(db, "SETUP", __LINE__,
286*85ffcae1Sdrh "DROP TABLE IF EXISTS task;\n"
287*85ffcae1Sdrh "DROP TABLE IF EXISTS p1;\n"
288*85ffcae1Sdrh "DROP TABLE IF EXISTS p2;\n"
289*85ffcae1Sdrh "DROP TABLE IF EXISTS verify;\n"
290*85ffcae1Sdrh "CREATE TABLE IF NOT EXISTS task(\n"
291*85ffcae1Sdrh " tid INTEGER PRIMARY KEY,\n"
292*85ffcae1Sdrh " doneby TEXT\n"
293*85ffcae1Sdrh ");\n"
294*85ffcae1Sdrh "WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100)"
295*85ffcae1Sdrh "INSERT INTO task(tid) SELECT x FROM c;\n"
296*85ffcae1Sdrh );
297*85ffcae1Sdrh error_out(rc, "sqlite3_exec", __LINE__);
298*85ffcae1Sdrh
299*85ffcae1Sdrh for(i=0; i<nWorker; i++){
300*85ffcae1Sdrh sqlite3_snprintf(sizeof(aWorkerName[i]), aWorkerName[i],
301*85ffcae1Sdrh "W%02d", i);
302*85ffcae1Sdrh pthread_create(&aWorker[i], 0, worker, aWorkerName[i]);
303*85ffcae1Sdrh }
304*85ffcae1Sdrh for(i=0; i<nWorker; i++){
305*85ffcae1Sdrh pthread_join(aWorker[i], 0);
306*85ffcae1Sdrh }
307*85ffcae1Sdrh
308*85ffcae1Sdrh for(i=0; i<nWorker; i++){
309*85ffcae1Sdrh q = prepare(db, "MAIN", __LINE__,
310*85ffcae1Sdrh "SELECT group_concat(tid,',') FROM task WHERE doneby=%Q",
311*85ffcae1Sdrh aWorkerName[i]);
312*85ffcae1Sdrh if( sqlite3_step(q)==SQLITE_ROW ){
313*85ffcae1Sdrh printf("%s: %s\n", aWorkerName[i], sqlite3_column_text(q,0));
314*85ffcae1Sdrh }
315*85ffcae1Sdrh sqlite3_finalize(q);
316*85ffcae1Sdrh }
317*85ffcae1Sdrh q = prepare(db, "MAIN", __LINE__, "SELECT count(*) FROM p2");
318*85ffcae1Sdrh if( sqlite3_step(q)!=SQLITE_ROW || sqlite3_column_int(q,0)<10 ){
319*85ffcae1Sdrh printf("incorrect result\n");
320*85ffcae1Sdrh exit(-1);
321*85ffcae1Sdrh }
322*85ffcae1Sdrh sqlite3_finalize(q);
323*85ffcae1Sdrh q = prepare(db, "MAIN", __LINE__, "SELECT x FROM p1 EXCEPT SELECT x FROM p2");
324*85ffcae1Sdrh if( sqlite3_step(q)==SQLITE_ROW ){
325*85ffcae1Sdrh printf("incorrect result\n");
326*85ffcae1Sdrh exit(-1);
327*85ffcae1Sdrh }
328*85ffcae1Sdrh sqlite3_finalize(q);
329*85ffcae1Sdrh q = prepare(db, "MAIN", __LINE__, "SELECT x FROM p2 EXCEPT SELECT x FROM p1");
330*85ffcae1Sdrh if( sqlite3_step(q)==SQLITE_ROW ){
331*85ffcae1Sdrh printf("incorrect result\n");
332*85ffcae1Sdrh exit(-1);
333*85ffcae1Sdrh }
334*85ffcae1Sdrh sqlite3_finalize(q);
335*85ffcae1Sdrh printf("OK\n");
336*85ffcae1Sdrh
337*85ffcae1Sdrh sqlite3_close(db);
338*85ffcae1Sdrh return 0;
339*85ffcae1Sdrh }
340