4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
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.
11 *************************************************************************
13 ** Testing threading behavior when multiple database connections in separate
14 ** threads of the same process are all talking to the same database file.
16 ** For best results, ensure that SQLite is compiled with HAVE_USLEEP=1
18 ** Only works on unix platforms.
22 ** ./threadtest5 ?DATABASE?
24 ** If DATABASE is omitted, it defaults to using file:/mem?vfs=memdb.
34 /* Name of the in-memory database */
35 static char *zDbName
= 0;
37 /* True for debugging */
38 static int eVerbose
= 0;
40 /* If rc is not SQLITE_OK, then print an error message and stop
43 static void error_out(int rc
, const char *zCtx
, int lineno
){
45 fprintf(stderr
, "error %d at %d in \"%s\"\n", rc
, lineno
, zCtx
);
51 /* Return the number of milliseconds since the Julian epoch (-4714-11-24).
53 static sqlite3_int64
gettime(void){
55 sqlite3_vfs
*pVfs
= sqlite3_vfs_find(0);
56 pVfs
->xCurrentTimeInt64(pVfs
, &tm
);
61 /* Run the SQL in the second argument.
73 va_start(ap
, zFormat
);
74 zSql
= sqlite3_vmprintf(zFormat
, ap
);
77 printf("%s:%d: [%s]\n", zId
, lineno
, zSql
);
80 rc
= sqlite3_exec(db
, zSql
, 0, 0, 0);
82 printf("%s:%d: return-code %d\n", zId
, lineno
, rc
);
89 /* Generate a perpared statement from the input SQL
91 static sqlite3_stmt
*prepare(
101 sqlite3_stmt
*pStmt
= 0;
102 va_start(ap
, zFormat
);
103 zSql
= sqlite3_vmprintf(zFormat
, ap
);
106 printf("%s:%d: [%s]\n", zId
, lineno
, zSql
);
110 rc
= sqlite3_prepare_v2(db
, zSql
, -1, &pStmt
, 0);
112 printf("%s:%d: ERROR - %s\n", zId
, lineno
, sqlite3_errmsg(db
));
120 ** Wait for table zTable to exist in the schema.
122 static void waitOnTable(sqlite3
*db
, const char *zWorker
, const char *zTable
){
125 sqlite3_stmt
*q
= prepare(db
, zWorker
, __LINE__
,
126 "SELECT 1 FROM sqlite_schema WHERE name=%Q", zTable
);
127 if( sqlite3_step(q
)==SQLITE_ROW
&& sqlite3_column_int(q
,0)!=0 ){
137 ** Return true if x is a prime number
139 static int isPrime(int x
){
142 for(i
=2; i
*i
<=x
; i
++){
143 if( (x
%i
)==0 ) return 0;
148 /* Each worker thread runs an instance of the following */
149 static void *worker(void *pArg
){
151 const char *zName
= (const char*)pArg
;
155 printf("%s: startup\n", zName
);
159 rc
= sqlite3_open(zDbName
, &db
);
160 error_out(rc
, "sqlite3_open", __LINE__
);
161 sqlite3_busy_timeout(db
, 2000);
166 q1
= prepare(db
, zName
, __LINE__
,
167 "UPDATE task SET doneby=%Q"
168 " WHERE tid=(SELECT tid FROM task WHERE doneby IS NULL LIMIT 1)"
169 "RETURNING tid", zName
171 if( sqlite3_step(q1
)==SQLITE_ROW
){
172 tid
= sqlite3_column_int(q1
,0);
174 sqlite3_finalize(q1
);
177 printf("%s: starting task %d\n", zName
, tid
);
181 exec(db
, zName
, __LINE__
,
182 "CREATE TABLE IF NOT EXISTS p1(x INTEGER PRIMARY KEY);"
184 }else if( tid
>=2 && tid
<=51 ){
186 waitOnTable(db
, zName
, "p1");
191 exec(db
, zName
, __LINE__
,
192 "INSERT INTO p1(x) VALUES(%d)", i
);
196 exec(db
, zName
, __LINE__
,
197 "CREATE TABLE IF NOT EXISTS p2(x INTEGER PRIMARY KEY);"
199 " c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<10000)"
200 "INSERT INTO p2(x) SELECT x FROM c;"
202 }else if( tid
>=53 && tid
<=62 ){
204 waitOnTable(db
, zName
, "p2");
208 exec(db
, zName
, __LINE__
,
209 "DELETE FROM p2 WHERE x>%d AND (x %% %d)==0", i
, i
);
213 printf("%s: completed task %d\n", zName
, tid
);
222 printf("%s: exit\n", zName
);
228 /* Print a usage comment and die */
229 static void usage(const char *argv0
){
230 printf("Usage: %s [options]\n", argv0
);
232 " -num-workers N Run N worker threads\n"
233 " -v Debugging output\n"
238 /* Maximum number of threads */
239 #define MX_WORKER 100
244 int main(int argc
, char **argv
){
250 pthread_t aWorker
[MX_WORKER
];
251 char aWorkerName
[MX_WORKER
][8];
253 for(i
=1; i
<argc
; i
++){
254 const char *zArg
= argv
[i
];
260 printf("unknown argument: %s\n", zArg
);
263 if( zArg
[1]=='-' ) zArg
++;
264 if( strcmp(zArg
, "-v")==0 ){
268 if( strcmp(zArg
, "-num-workers")==0 && i
+1<argc
){
269 nWorker
= atoi(argv
[++i
]);
270 if( nWorker
<1 || nWorker
>MX_WORKER
){
271 printf("number of threads must be between 1 and %d\n", MX_WORKER
);
276 printf("unknown option: %s\n", argv
[i
]);
279 if( zDbName
==0 ) zDbName
= "file:/mem?vfs=memdb";
281 sqlite3_config(SQLITE_CONFIG_URI
, (int)1);
282 rc
= sqlite3_open(zDbName
, &db
);
283 error_out(rc
, "sqlite3_open", __LINE__
);
285 rc
= exec(db
, "SETUP", __LINE__
,
286 "DROP TABLE IF EXISTS task;\n"
287 "DROP TABLE IF EXISTS p1;\n"
288 "DROP TABLE IF EXISTS p2;\n"
289 "DROP TABLE IF EXISTS verify;\n"
290 "CREATE TABLE IF NOT EXISTS task(\n"
291 " tid INTEGER PRIMARY KEY,\n"
294 "WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100)"
295 "INSERT INTO task(tid) SELECT x FROM c;\n"
297 error_out(rc
, "sqlite3_exec", __LINE__
);
299 for(i
=0; i
<nWorker
; i
++){
300 sqlite3_snprintf(sizeof(aWorkerName
[i
]), aWorkerName
[i
],
302 pthread_create(&aWorker
[i
], 0, worker
, aWorkerName
[i
]);
304 for(i
=0; i
<nWorker
; i
++){
305 pthread_join(aWorker
[i
], 0);
308 for(i
=0; i
<nWorker
; i
++){
309 q
= prepare(db
, "MAIN", __LINE__
,
310 "SELECT group_concat(tid,',') FROM task WHERE doneby=%Q",
312 if( sqlite3_step(q
)==SQLITE_ROW
){
313 printf("%s: %s\n", aWorkerName
[i
], sqlite3_column_text(q
,0));
317 q
= prepare(db
, "MAIN", __LINE__
, "SELECT count(*) FROM p2");
318 if( sqlite3_step(q
)!=SQLITE_ROW
|| sqlite3_column_int(q
,0)<10 ){
319 printf("incorrect result\n");
323 q
= prepare(db
, "MAIN", __LINE__
, "SELECT x FROM p1 EXCEPT SELECT x FROM p2");
324 if( sqlite3_step(q
)==SQLITE_ROW
){
325 printf("incorrect result\n");
329 q
= prepare(db
, "MAIN", __LINE__
, "SELECT x FROM p2 EXCEPT SELECT x FROM p1");
330 if( sqlite3_step(q
)==SQLITE_ROW
){
331 printf("incorrect result\n");