Test that a race condition can cause a "BEGIN EXCLUSIVE" to return
SQLITE_BUSY_SNAPSHOT in wal mode.

FossilOrigin-Name: 5a12db75d1da65daa92413a6b5892309e9d9479bb3610764e1015abe5bf28dbe
diff --git a/manifest b/manifest
index f66a139..2357a1b 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\sproblem\sin\sthe\sxInverse\scallback\sfor\sthe\sbuilt-in\ssum()\swindow\sfunction.
-D 2018-07-02T17:45:59.790
+C Test\sthat\sa\srace\scondition\scan\scause\sa\s"BEGIN\sEXCLUSIVE"\sto\sreturn\nSQLITE_BUSY_SNAPSHOT\sin\swal\smode.
+D 2018-07-03T20:17:27.649
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6
@@ -1583,6 +1583,7 @@
 F test/waloverwrite.test dad2f26567f1b45174e54fbf9a8dc1cb876a7f03
 F test/walpersist.test 8c6b7e3ec1ba91b5e4dc4e0921d6d3f87cd356a6
 F test/walprotocol.test a112aba0b79e3adeaa485fed09484b32c654e97df58e454aa8489ac2cd57bf84
+F test/walprotocol2.test ad92f036102f722b2d7f7ca7c1faf72d75f501a50c80a92d7fd87f8b1da6608c
 F test/walro.test cb438d05ba0d191f10b688e39c4f0cd5b71569a1d1f4440e5bdf3c6880e08c20
 F test/walro2.test 0e79dd15cbdb4f482c01ea248373669c732414a726b357d04846a816afafb768
 F test/walrofault.test c70cb6e308c443867701856cce92ad8288cd99488fa52afab77cca6cfd51af68
@@ -1744,7 +1745,7 @@
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 693b4350d741391226a33ab6a05eaad61e8ef1590176f01e8aed2a212e2d6419
-R 2a905c5056bd462d63ab558902828293
+P b6563647382634588ebe5c6a3c35c65a321dc1b3732c809d48ce46759b9dd80f
+R 85d71bbec8ad4dcf2f8e9507c907b291
 U dan
-Z e86ae3bb9d5c5564eb7cd669366300ff
+Z 38cb85199c9d8fc14bf46fba2bfb3f49
diff --git a/manifest.uuid b/manifest.uuid
index 88034b2..e6f611a 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-b6563647382634588ebe5c6a3c35c65a321dc1b3732c809d48ce46759b9dd80f
\ No newline at end of file
+5a12db75d1da65daa92413a6b5892309e9d9479bb3610764e1015abe5bf28dbe
\ No newline at end of file
diff --git a/test/walprotocol2.test b/test/walprotocol2.test
new file mode 100644
index 0000000..ea1c9a2
--- /dev/null
+++ b/test/walprotocol2.test
@@ -0,0 +1,97 @@
+# 2018 July 4
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/lock_common.tcl
+source $testdir/wal_common.tcl
+ifcapable !wal {finish_test ; return }
+
+set testprefix walprotocol2
+
+#-------------------------------------------------------------------------
+# When recovering the contents of a WAL file, a process obtains the WRITER
+# lock, then locks all other bytes before commencing recovery. If it fails
+# to lock all other bytes (because some other process is holding a read
+# lock) it should retry up to 100 times. Then return SQLITE_PROTOCOL to the 
+# caller. Test this (test case 1.3).
+#
+# Also test the effect of hitting an SQLITE_BUSY while attempting to obtain
+# the WRITER lock (should be the same). Test case 1.4.
+# 
+do_execsql_test 1.0 {
+  PRAGMA journal_mode = wal;
+  CREATE TABLE x(y);
+  INSERT INTO x VALUES('z');
+} {wal}
+
+db close
+
+proc lock_callback {method filename handle lock} {
+  # puts "$method $filename $handle $lock"
+}
+testvfs T
+T filter xShmLock 
+T script lock_callback
+
+sqlite3 db  test.db -vfs T
+sqlite3 db2 test.db -vfs T
+
+do_execsql_test 2.0 {
+  SELECT * FROM x;
+} {z}
+do_execsql_test -db db2 2.1 {
+  SELECT * FROM x;
+} {z}
+
+#---------------------------------------------------------------
+# Attempt a "BEGIN EXCLUSIVE" using connection handle [db]. This
+# causes SQLite to open a read transaction, then a write transaction.
+# Rig the xShmLock() callback so that just before the EXCLUSIVE lock
+# for the write transaction is taken, connection [db2] jumps in and
+# modifies the database. This causes the "BEGIN EXCLUSIVE" to throw
+# an SQLITE_BUSY_SNAPSHOT error.
+#
+proc lock_callback {method filename handle lock} {
+  if {$lock=="0 1 lock exclusive"} {
+    proc lock_callback {method filename handle lock} {}
+    db2 eval { INSERT INTO x VALUES('y') }
+  }
+}
+do_catchsql_test 2.2 {
+  BEGIN EXCLUSIVE;
+} {1 {database is locked}}
+do_test 2.3 {
+  sqlite3_extended_errcode db
+} {SQLITE_BUSY_SNAPSHOT}
+
+#---------------------------------------------------------------
+# Same again, but with a busy-handler. This time, following the
+# SQLITE_BUSY_SNAPSHOT error the busy-handler is invoked and then the 
+# whole thing retried from the beginning. This time it succeeds.
+#
+proc lock_callback {method filename handle lock} {
+  if {$lock=="0 1 lock exclusive"} {
+    proc lock_callback {method filename handle lock} {}
+    db2 eval { INSERT INTO x VALUES('x') }
+  }
+}
+db timeout 10
+do_catchsql_test 2.4 {
+  BEGIN EXCLUSIVE;
+} {0 {}}
+do_execsql_test 2.5 {
+  SELECT * FROM x;
+  COMMIT;
+} {z y x}
+
+finish_test