/ Check-in [63ea318e]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Add test case demonstrating deadlock during recovery of very large log files. No fix yet.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | wal
Files: files | file ages | folders
SHA1: 63ea318eb19d264667909c70185b8a5cdc4454c0
User & Date: dan 2010-05-01 08:30:35
Context
2010-05-01
11:19
Fix the sqlite3_mutex_alloc() interface to return NULL (not segfault) when operating in threadsafe mode. (This is a general-purpose bug fix which really ought to be ported to trunk.) check-in: 64840a3c user: drh tags: wal
08:30
Add test case demonstrating deadlock during recovery of very large log files. No fix yet. check-in: 63ea318e user: dan tags: wal
00:59
Do not allow journal_mode=WAL if the underlying VFS does not support xShmOpen. check-in: d1fcccec user: drh tags: wal
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to test/walthread.test.

24
25
26
27
28
29
30

31
32
33
34
35
36
37
...
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
...
450
451
452
453
454
455
456












































































457
458
# How long, in seconds, to run each test for. If a test is set to run for
# 0 seconds, it is omitted entirely.
#
set seconds(walthread-1) 20
set seconds(walthread-2) 20
set seconds(walthread-3) 20
set seconds(walthread-4) 20


# The parameter is the name of a variable in the callers context. The
# variable may or may not exist when this command is invoked.
#
# If the variable does exist, its value is returned. Otherwise, this
# command uses [vwait] to wait until it is set, then returns the value.
# In other words, this is a version of the [set VARNAME] command that
................................................................................
  puts "Running $P(testname) for $P(seconds) seconds..."

  catch { db close }
  file delete -force test.db test.db-journal test.db-wal

  sqlite3 db test.db
  eval $P(init)
  db close

  foreach T $P(threads) {
    set name  [lindex $T 0]
    set count [lindex $T 1]
    set prg   [lindex $T 2]

    for {set i 1} {$i <= $count} {incr i} {
................................................................................
    incr row
    if {$row == 10} { set row 1 }
  }

  set {} ok
}













































































finish_test








>







 







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
...
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
...
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
# How long, in seconds, to run each test for. If a test is set to run for
# 0 seconds, it is omitted entirely.
#
set seconds(walthread-1) 20
set seconds(walthread-2) 20
set seconds(walthread-3) 20
set seconds(walthread-4) 20
set seconds(walthread-5) 1

# The parameter is the name of a variable in the callers context. The
# variable may or may not exist when this command is invoked.
#
# If the variable does exist, its value is returned. Otherwise, this
# command uses [vwait] to wait until it is set, then returns the value.
# In other words, this is a version of the [set VARNAME] command that
................................................................................
  puts "Running $P(testname) for $P(seconds) seconds..."

  catch { db close }
  file delete -force test.db test.db-journal test.db-wal

  sqlite3 db test.db
  eval $P(init)
  catch { db close }

  foreach T $P(threads) {
    set name  [lindex $T 0]
    set count [lindex $T 1]
    set prg   [lindex $T 2]

    for {set i 1} {$i <= $count} {incr i} {
................................................................................
    incr row
    if {$row == 10} { set row 1 }
  }

  set {} ok
}


# This test case attempts to provoke a deadlock condition that existed in
# the unix VFS at one point. The problem occurred only while recovering a 
# very large wal file (one that requires a wal-index larger than the 
# initial default allocation of 64KB).
#
# When recovering a log file, mutexes are usually obtained and released
# as follows:
#
#   xShmGet                         ENTER mutexBuf
#   xShmLock(RECOVER)               ENTER mutexRecov
#     <do recovery work>
#   xShmLock(READ)                  LEAVE mutexRecov
#   xShmRelease                     LEAVE mutexBuf         
#
# However, if the initial wal-index allocation needs to be enlarged during
# the recovery process, the mutex operations become:
#
#   xShmGet                         ENTER mutexBuf
#   xShmLock(RECOVER)               ENTER mutexRecov
#     <do first part of recovery work>
#     xShmRelease                   LEAVE mutexBuf
#     xShmSize
#     xShmGet                       ENTER mutexBuf
#     <do second part of recovery work>
#   xShmLock(READ)                  LEAVE mutexRecov
#   xShmRelease                     LEAVE mutexBuf         
#
# If multiple threads attempt the above simultaneously, deadlock can occur.
#
do_thread_test walthread-5 -seconds $seconds(walthread-5) -init {

  proc log_file_size {nFrame pgsz} {
    expr {12 + ($pgsz+16)*$nFrame}
  }

  execsql {
    PRAGMA page_size = 1024;
    PRAGMA journal_mode = WAL;
    CREATE TABLE t1(x);
    BEGIN;
      INSERT INTO t1 VALUES(randomblob(900));
      INSERT INTO t1 SELECT randomblob(900) FROM t1;      /*     2 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;      /*     4 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;      /*     8 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;      /*    16 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;      /*    32 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;      /*    64 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;      /*   128 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;      /*   256 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;      /*   512 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;      /*  1024 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;      /*  2048 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;      /*  4096 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;      /*  8192 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;      /* 16384 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;      /* 32768 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;      /* 65536 */
    COMMIT;
  }

  file copy -force test.db-wal bak.db-wal
  file copy -force test.db bak.db
  db close

  file copy -force bak.db-wal test.db-wal
  file copy -force bak.db test.db

  if {[file size test.db-wal] < [log_file_size [expr 64*1024] 1024]} {
    error "Somehow failed to create a large log file"
  }
  puts "Database with large log file recovered. Now running clients..."
} -thread T 5 {
  db eval { SELECT count(*) FROM t1 }
}

finish_test