SQLite

Check-in [d8d3e6d04c]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add new tests to the threadtest4.c program. Fix a long-standing data race in WAL mode for shared-cache.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: d8d3e6d04cbb9e3033ad8613e3dbd4ad0b01765a
User & Date: drh 2014-12-12 01:27:17.213
Context
2014-12-12
16:39
Add extra tests to threadtest3. (check-in: f6bf86f907 user: dan tags: trunk)
01:27
Add new tests to the threadtest4.c program. Fix a long-standing data race in WAL mode for shared-cache. (check-in: d8d3e6d04c user: drh tags: trunk)
00:52
Remove the KeyInfo cache (for now - perhaps we will add it back in later - or maybe not since it provides negligible benefit but adds a lot of complexity and thread-safety risk). Add a mutex to ATTACH to deal with a data race. (check-in: 03c443eaf2 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/vdbeapi.c.
396
397
398
399
400
401
402


403

404
405
406
407
408
409
410
static int doWalCallbacks(sqlite3 *db){
  int rc = SQLITE_OK;
#ifndef SQLITE_OMIT_WAL
  int i;
  for(i=0; i<db->nDb; i++){
    Btree *pBt = db->aDb[i].pBt;
    if( pBt ){


      int nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));

      if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){
        rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry);
      }
    }
  }
#endif
  return rc;







>
>
|
>







396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
static int doWalCallbacks(sqlite3 *db){
  int rc = SQLITE_OK;
#ifndef SQLITE_OMIT_WAL
  int i;
  for(i=0; i<db->nDb; i++){
    Btree *pBt = db->aDb[i].pBt;
    if( pBt ){
      int nEntry;
      sqlite3BtreeEnter(pBt);
      nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));
      sqlite3BtreeLeave(pBt);
      if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){
        rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry);
      }
    }
  }
#endif
  return rc;
Changes to test/threadtest4.c.
12
13
14
15
16
17
18
19
20
21
22
23
24
25




26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

43
44
45
46
47
48
49
** This file implements a simple standalone program used to stress the
** SQLite library when accessing the same set of databases simultaneously
** from multiple threads in shared-cache mode.
**
** This test program runs on unix-like systems only.  It uses pthreads.
** To compile:
**
**     gcc -o tt4 -I. threadtest4.c sqlite3.c -ldl -lpthread
**
** To run:
**
**     ./tt4 10
**
** The argument is the number of threads.




*/
#include "sqlite3.h"
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>

/*
** An instance of the following structure is passed into each worker
** thread.
*/
typedef struct WorkerInfo WorkerInfo;
struct WorkerInfo {
  int tid;                    /* Thread ID */

  unsigned wkrFlags;          /* Flags */
  sqlite3 *mainDb;            /* Database connection of the main thread */
  sqlite3 *db;                /* Database connection of this thread */
  int nErr;                   /* Number of errors seen by this thread */
  int nTest;                  /* Number of tests run by this thread */
  char *zMsg;                 /* Message returned by this thread */
  pthread_t id;               /* Thread id */







|



|

|
>
>
>
>

















>







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
** This file implements a simple standalone program used to stress the
** SQLite library when accessing the same set of databases simultaneously
** from multiple threads in shared-cache mode.
**
** This test program runs on unix-like systems only.  It uses pthreads.
** To compile:
**
**     gcc -g -Wall -I. threadtest4.c sqlite3.c -ldl -lpthread
**
** To run:
**
**     ./a.out 10
**
** The argument is the number of threads.  There are also options, such
** as -wal and -multithread and -serialized.
**
** Consider also compiling with clang instead of gcc and adding the
** -fsanitize=thread option.
*/
#include "sqlite3.h"
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>

/*
** An instance of the following structure is passed into each worker
** thread.
*/
typedef struct WorkerInfo WorkerInfo;
struct WorkerInfo {
  int tid;                    /* Thread ID */
  int nWorker;                /* Total number of workers */
  unsigned wkrFlags;          /* Flags */
  sqlite3 *mainDb;            /* Database connection of the main thread */
  sqlite3 *db;                /* Database connection of this thread */
  int nErr;                   /* Number of errors seen by this thread */
  int nTest;                  /* Number of tests run by this thread */
  char *zMsg;                 /* Message returned by this thread */
  pthread_t id;               /* Thread id */
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304











305
306
307
308
309
310
311
  WorkerInfo *p = (WorkerInfo*)pArg;
  int iOuter;
  int i;
  int rc;
  sqlite3_stmt *pStmt;

  printf("worker %d startup\n", p->tid);  fflush(stdout);
  for(iOuter=1; iOuter<=4; iOuter++){
    worker_open_connection(p, iOuter);
    for(i=0; i<4; i++){
      worker_add_content(p, i*100+1, (i+1)*100, (p->tid+iOuter)%3 + 1);
      worker_add_content(p, i*100+1, (i+1)*100, (p->tid+iOuter+1)%3 + 1);
      worker_add_content(p, i*100+1, (i+1)*100, (p->tid+iOuter+2)%3 + 1);
    }

    pStmt = prep_sql(p->db, "SELECT count(a) FROM t1 WHERE tid=%d", p->tid);
    worker_trace(p, "query [%s]", sqlite3_sql(pStmt));
    rc = sqlite3_step(pStmt);
    if( rc!=SQLITE_ROW ){
      worker_error(p, "Failed to step: %s", sqlite3_sql(pStmt));
    }else if( sqlite3_column_int(pStmt, 0)!=400 ){
      worker_error(p, "Wrong result: %d", sqlite3_column_int(pStmt,0));
    }
    if( p->nErr ) break;
    sqlite3_finalize(pStmt);












    worker_delete_all_content(p, (p->tid+iOuter)%2);
    worker_close_connection(p);
    p->db = 0;
  }
  worker_close_connection(p);
  printf("worker %d finished\n", p->tid); fflush(stdout);







|

















>
>
>
>
>
>
>
>
>
>
>







285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
  WorkerInfo *p = (WorkerInfo*)pArg;
  int iOuter;
  int i;
  int rc;
  sqlite3_stmt *pStmt;

  printf("worker %d startup\n", p->tid);  fflush(stdout);
  for(iOuter=1; iOuter<=p->nWorker; iOuter++){
    worker_open_connection(p, iOuter);
    for(i=0; i<4; i++){
      worker_add_content(p, i*100+1, (i+1)*100, (p->tid+iOuter)%3 + 1);
      worker_add_content(p, i*100+1, (i+1)*100, (p->tid+iOuter+1)%3 + 1);
      worker_add_content(p, i*100+1, (i+1)*100, (p->tid+iOuter+2)%3 + 1);
    }

    pStmt = prep_sql(p->db, "SELECT count(a) FROM t1 WHERE tid=%d", p->tid);
    worker_trace(p, "query [%s]", sqlite3_sql(pStmt));
    rc = sqlite3_step(pStmt);
    if( rc!=SQLITE_ROW ){
      worker_error(p, "Failed to step: %s", sqlite3_sql(pStmt));
    }else if( sqlite3_column_int(pStmt, 0)!=400 ){
      worker_error(p, "Wrong result: %d", sqlite3_column_int(pStmt,0));
    }
    if( p->nErr ) break;
    sqlite3_finalize(pStmt);

    if( ((iOuter+p->tid)%3)==0 ){
      sqlite3_db_release_memory(p->db);
      p->nTest++;
    }

    if( iOuter==p->tid ){
      pthread_mutex_lock(p->pWrMutex);
      run_sql(p, "VACUUM");
      pthread_mutex_unlock(p->pWrMutex);
    }

    worker_delete_all_content(p, (p->tid+iOuter)%2);
    worker_close_connection(p);
    p->db = 0;
  }
  worker_close_connection(p);
  printf("worker %d finished\n", p->tid); fflush(stdout);
358
359
360
361
362
363
364


365
366
367
368
369
370
371
  if( nWorker==0 ){ 
    fprintf(stderr,
       "usage:  %s ?OPTIONS? N\n"
       "N is the number of threads and must be at least 2.\n"
       "Options:\n"
       "  --serialized\n"
       "  --multithread\n"


       ,argv[0]
    );
    exit(1);
  }
  if( !sqlite3_threadsafe() ){
    fprintf(stderr, "requires a threadsafe build of SQLite\n");
    exit(1);







>
>







374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
  if( nWorker==0 ){ 
    fprintf(stderr,
       "usage:  %s ?OPTIONS? N\n"
       "N is the number of threads and must be at least 2.\n"
       "Options:\n"
       "  --serialized\n"
       "  --multithread\n"
       "  --wal\n"
       "  --trace\n"
       ,argv[0]
    );
    exit(1);
  }
  if( !sqlite3_threadsafe() ){
    fprintf(stderr, "requires a threadsafe build of SQLite\n");
    exit(1);
402
403
404
405
406
407
408

409
410
411
412
413
414
415
  run_sql(p, "CREATE TABLE IF NOT EXISTS test3.t3(tid INTEGER, sp, x, y, z)");
  run_sql(p, "CREATE INDEX test3.t3tid ON t3(tid)");
  run_sql(p, "CREATE INDEX test3.t3xy ON t3(x,y)");
  aInfo = safe_malloc( sizeof(*aInfo)*nWorker );
  memset(aInfo, 0, sizeof(*aInfo)*nWorker);
  for(i=0; i<nWorker; i++){
    aInfo[i].tid = i+1;

    aInfo[i].wkrFlags = wkrFlags;
    aInfo[i].mainDb = db;
    aInfo[i].pWrMutex = &wrMutex;
    rc = pthread_create(&aInfo[i].id, 0, worker_thread, &aInfo[i]);
    if( rc!=0 ){
      fprintf(stderr, "thread creation failed for thread %d\n", i+1);
      exit(1);







>







420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
  run_sql(p, "CREATE TABLE IF NOT EXISTS test3.t3(tid INTEGER, sp, x, y, z)");
  run_sql(p, "CREATE INDEX test3.t3tid ON t3(tid)");
  run_sql(p, "CREATE INDEX test3.t3xy ON t3(x,y)");
  aInfo = safe_malloc( sizeof(*aInfo)*nWorker );
  memset(aInfo, 0, sizeof(*aInfo)*nWorker);
  for(i=0; i<nWorker; i++){
    aInfo[i].tid = i+1;
    aInfo[i].nWorker = nWorker;
    aInfo[i].wkrFlags = wkrFlags;
    aInfo[i].mainDb = db;
    aInfo[i].pWrMutex = &wrMutex;
    rc = pthread_create(&aInfo[i].id, 0, worker_thread, &aInfo[i]);
    if( rc!=0 ){
      fprintf(stderr, "thread creation failed for thread %d\n", i+1);
      exit(1);