/ Check-in [7c102c7b]
Login

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

Overview
Comment:Fix for race condition in WAL locking code.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | mistake
Files: files | file ages | folders
SHA1: 7c102c7b5f90717f55d95e3d38f33684118784d6
User & Date: dan 2010-06-09 11:02:13
References
2010-06-09
11:28
Simpler fix for the race condition also fixed by [7c102c7b5f] check-in: 3c2de820 user: dan tags: trunk
Context
2010-06-09
11:02
Fix for race condition in WAL locking code. Closed-Leaf check-in: 7c102c7b user: dan tags: mistake
2010-06-08
15:50
Close database opened by tester.tcl when it is sourced in all.test. Because test scripts are now run in slave interpreters, this connection was not being closed by the first script run as it was previously. check-in: b072e9f6 user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/wal.c.

1788
1789
1790
1791
1792
1793
1794


1795

1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806


1807
1808

1809
1810
1811
1812
1813
1814
1815
....
1841
1842
1843
1844
1845
1846
1847

1848
1849
1850
1851
1852
1853
1854
  if( mxI==0 ){
    /* If we get here, it means that all of the aReadMark[] entries between
    ** 1 and WAL_NREADER-1 are zero.  Try to initialize aReadMark[1] to
    ** be mxFrame, then retry.
    */
    rc = walLockExclusive(pWal, WAL_READ_LOCK(1), 1);
    if( rc==SQLITE_OK ){


      pInfo->aReadMark[1] = pWal->hdr.mxFrame+1;

      walUnlockExclusive(pWal, WAL_READ_LOCK(1), 1);
      rc = WAL_RETRY;
    }else if( rc==SQLITE_BUSY ){
      rc = WAL_RETRY;
    }
    return rc;
  }else{
    if( mxReadMark < pWal->hdr.mxFrame ){
      for(i=1; i<WAL_NREADER; i++){
        rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
        if( rc==SQLITE_OK ){


          mxReadMark = pInfo->aReadMark[i] = pWal->hdr.mxFrame+1;
          mxI = i;

          walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
          break;
        }else if( rc!=SQLITE_BUSY ){
          return rc;
        }
      }
    }
................................................................................
    sqlite3OsShmBarrier(pWal->pDbFd);
    if( pInfo->aReadMark[mxI]!=mxReadMark
     || memcmp((void *)pHdr, &pWal->hdr, sizeof(WalIndexHdr))
    ){
      walUnlockShared(pWal, WAL_READ_LOCK(mxI));
      return WAL_RETRY;
    }else{

      pWal->readLock = mxI;
    }
  }
  return rc;
}

/*







>
>
|
>







|



>
>
|
|
>







 







>







1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
....
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
  if( mxI==0 ){
    /* If we get here, it means that all of the aReadMark[] entries between
    ** 1 and WAL_NREADER-1 are zero.  Try to initialize aReadMark[1] to
    ** be mxFrame, then retry.
    */
    rc = walLockExclusive(pWal, WAL_READ_LOCK(1), 1);
    if( rc==SQLITE_OK ){
      sqlite3OsShmBarrier(pWal->pDbFd);
      if( 0==memcmp((void *)pHdr, &pWal->hdr, sizeof(WalIndexHdr)) ){
        pInfo->aReadMark[1] = pWal->hdr.mxFrame+1;
      }
      walUnlockExclusive(pWal, WAL_READ_LOCK(1), 1);
      rc = WAL_RETRY;
    }else if( rc==SQLITE_BUSY ){
      rc = WAL_RETRY;
    }
    return rc;
  }else{
    if( mxReadMark<=pWal->hdr.mxFrame ){
      for(i=1; i<WAL_NREADER; i++){
        rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
        if( rc==SQLITE_OK ){
          sqlite3OsShmBarrier(pWal->pDbFd);
          if( 0==memcmp((void *)pHdr, &pWal->hdr, sizeof(WalIndexHdr)) ){
            mxReadMark = pInfo->aReadMark[i] = pWal->hdr.mxFrame+1;
            mxI = i;
          }
          walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
          break;
        }else if( rc!=SQLITE_BUSY ){
          return rc;
        }
      }
    }
................................................................................
    sqlite3OsShmBarrier(pWal->pDbFd);
    if( pInfo->aReadMark[mxI]!=mxReadMark
     || memcmp((void *)pHdr, &pWal->hdr, sizeof(WalIndexHdr))
    ){
      walUnlockShared(pWal, WAL_READ_LOCK(mxI));
      return WAL_RETRY;
    }else{
      assert( mxReadMark<=(pWal->hdr.mxFrame+1) );
      pWal->readLock = mxI;
    }
  }
  return rc;
}

/*

Changes to test/wal2.test.

49
50
51
52
53
54
55












56
57
58
59
60
61
62
...
163
164
165
166
167
168
169

170
171
172
173
174
175
176
...
224
225
226
227
228
229
230

231
232
233
234
235
236
237
...
889
890
891
892
893
894
895

896
897
898
899
900
901
902
903
  set ints [set_tvfs_hdr $file]
  set v [lindex $ints $idx]
  incr v $incrval
  lset ints $idx $v
  set_tvfs_hdr $file $ints
}














#-------------------------------------------------------------------------
# Test case wal2-1.*:
#
# Set up a small database containing a single table. The database is not
# checkpointed during the test - all content resides in the log file.
#
................................................................................
# After this, the header is corrupted again and the reader is allowed
# to run recovery. This time, it sees an up-to-date snapshot of the
# database file.
#
set WRITER [list 0 1 lock exclusive]
set LOCKS  [list \
  {0 1 lock exclusive} {0 1 unlock exclusive} \

  {4 1 lock shared}    {4 1 unlock shared}    \
]
do_test wal2-2.0 {

  testvfs tvfs
  tvfs script tvfs_cb
  proc tvfs_cb {method args} {
................................................................................
        }
      }
      if {$method == "xShmLock"} {
        set lock [lindex $args 2]
        lappend ::locks $lock
        if {$lock == $::WRITER} {
          set_tvfs_hdr $::shm_file $::oldhdr

        }
      }
      return SQLITE_OK
    }

    execsql { SELECT count(a), sum(a) FROM t1 } db2
  } $res0
................................................................................
  5  $wih(2)                $wih(1)                {Barton Deakin Watson}   \
  6  $wih(2)                $wih(2)                {Barton Deakin Watson}   \
  7  $wih(1)                $wih(1)                {Barton Deakin}          \
  8  {0 0 0 0 0 0 0 0 0 0} {0 0 0 0 0 0 0 0 0 0}   {Barton Deakin Watson}   \
] {
  do_test wal2-9.$tn {
    set_tvfs_hdr $::filename $hdr1 $hdr2

    execsql { SELECT * FROM x } db2
  } $res
}

db2 close
db close

finish_test







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







 







>







 







>







 







>








49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
...
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
...
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
...
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
  set ints [set_tvfs_hdr $file]
  set v [lindex $ints $idx]
  incr v $incrval
  lset ints $idx $v
  set_tvfs_hdr $file $ints
}

proc clear_tvfs_readmarks {file} {

  set blob [tvfs shm $file]
  binary scan $blob i21 ints
  lappend ints 0 0 0 0 0 

  binary scan $blob a104a* dummy tail
  set blob [binary format i26a* $ints $tail]
  tvfs shm $file $blob

}


#-------------------------------------------------------------------------
# Test case wal2-1.*:
#
# Set up a small database containing a single table. The database is not
# checkpointed during the test - all content resides in the log file.
#
................................................................................
# After this, the header is corrupted again and the reader is allowed
# to run recovery. This time, it sees an up-to-date snapshot of the
# database file.
#
set WRITER [list 0 1 lock exclusive]
set LOCKS  [list \
  {0 1 lock exclusive} {0 1 unlock exclusive} \
  {4 1 lock exclusive} {4 1 unlock exclusive} \
  {4 1 lock shared}    {4 1 unlock shared}    \
]
do_test wal2-2.0 {

  testvfs tvfs
  tvfs script tvfs_cb
  proc tvfs_cb {method args} {
................................................................................
        }
      }
      if {$method == "xShmLock"} {
        set lock [lindex $args 2]
        lappend ::locks $lock
        if {$lock == $::WRITER} {
          set_tvfs_hdr $::shm_file $::oldhdr
          clear_tvfs_readmarks $::shm_file
        }
      }
      return SQLITE_OK
    }

    execsql { SELECT count(a), sum(a) FROM t1 } db2
  } $res0
................................................................................
  5  $wih(2)                $wih(1)                {Barton Deakin Watson}   \
  6  $wih(2)                $wih(2)                {Barton Deakin Watson}   \
  7  $wih(1)                $wih(1)                {Barton Deakin}          \
  8  {0 0 0 0 0 0 0 0 0 0} {0 0 0 0 0 0 0 0 0 0}   {Barton Deakin Watson}   \
] {
  do_test wal2-9.$tn {
    set_tvfs_hdr $::filename $hdr1 $hdr2
    clear_tvfs_readmarks $::shm_file
    execsql { SELECT * FROM x } db2
  } $res
}

db2 close
db close

finish_test