thread005.test 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. # 2009 March 11
  2. #
  3. # The author disclaims copyright to this source code. In place of
  4. # a legal notice, here is a blessing:
  5. #
  6. # May you do good and not evil.
  7. # May you find forgiveness for yourself and forgive others.
  8. # May you share freely, never taking more than you give.
  9. #
  10. #***********************************************************************
  11. #
  12. # Test a race-condition that shows up in shared-cache mode.
  13. #
  14. # $Id: thread005.test,v 1.5 2009/03/26 14:48:07 danielk1977 Exp $
  15. set testdir [file dirname $argv0]
  16. source $testdir/tester.tcl
  17. if {[run_thread_tests]==0} { finish_test ; return }
  18. ifcapable !shared_cache {
  19. finish_test
  20. return
  21. }
  22. db close
  23. # Use shared-cache mode for these tests.
  24. #
  25. set ::enable_shared_cache [sqlite3_enable_shared_cache]
  26. sqlite3_enable_shared_cache 1
  27. #-------------------------------------------------------------------------
  28. # This test attempts to hit the race condition fixed by commit [6363].
  29. #
  30. proc runsql {zSql {db {}}} {
  31. set rc SQLITE_OK
  32. while {$rc=="SQLITE_OK" && $zSql ne ""} {
  33. set STMT [sqlite3_prepare_v2 $db $zSql -1 zSql]
  34. while {[set rc [sqlite3_step $STMT]] eq "SQLITE_ROW"} { }
  35. set rc [sqlite3_finalize $STMT]
  36. }
  37. return $rc
  38. }
  39. do_test thread005-1.1 {
  40. sqlite3 db test.db
  41. db eval { CREATE TABLE t1(a, b) }
  42. db close
  43. } {}
  44. for {set ii 2} {$ii < 500} {incr ii} {
  45. unset -nocomplain finished
  46. thread_spawn finished(0) {sqlite3_open test.db}
  47. thread_spawn finished(1) {sqlite3_open test.db}
  48. if {![info exists finished(0)]} { vwait finished(0) }
  49. if {![info exists finished(1)]} { vwait finished(1) }
  50. do_test thread005-1.$ii {
  51. runsql { BEGIN } $finished(0)
  52. runsql { INSERT INTO t1 VALUES(1, 2) } $finished(0)
  53. # If the race-condition was hit, then $finished(0 and $finished(1)
  54. # will not use the same pager cache. In this case the next statement
  55. # can be executed succesfully. However, if the race-condition is not
  56. # hit, then $finished(1) will be blocked by the write-lock held by
  57. # $finished(0) on the shared-cache table t1 and the statement will
  58. # return SQLITE_LOCKED.
  59. #
  60. runsql { SELECT * FROM t1 } $finished(1)
  61. } {SQLITE_LOCKED}
  62. sqlite3_close $finished(0)
  63. sqlite3_close $finished(1)
  64. }
  65. #-------------------------------------------------------------------------
  66. # This test tries to exercise a race-condition that existed in shared-cache
  67. # mode at one point. The test uses two threads; each has a database connection
  68. # open on the same shared cache. The schema of the database is:
  69. #
  70. # CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);
  71. #
  72. # One thread is a reader and the other thread a reader and a writer. The
  73. # writer thread repeats the following transaction as fast as possible:
  74. #
  75. # BEGIN;
  76. # DELETE FROM t1 WHERE a = (SELECT max(a) FROM t1);
  77. # INSERT INTO t1 VALUES(NULL, NULL);
  78. # UPDATE t1 SET b = a WHERE a = (SELECT max(a) FROM t1);
  79. # SELECT count(*) FROM t1 WHERE b IS NULL;
  80. # COMMIT;
  81. #
  82. # The reader thread does the following over and over as fast as possible:
  83. #
  84. # BEGIN;
  85. # SELECT count(*) FROM t1 WHERE b IS NULL;
  86. # COMMIT;
  87. #
  88. # The test runs for 20 seconds or until one of the "SELECT count(*)"
  89. # statements returns a non-zero value. If an SQLITE_LOCKED error occurs,
  90. # the connection issues a ROLLBACK immediately to abandon the current
  91. # transaction.
  92. #
  93. # If everything is working correctly, the "SELECT count(*)" statements
  94. # should never return a value other than 0. The "INSERT" statement
  95. # executed by the writer adds a row with "b IS NULL" to the table, but
  96. # the subsequent UPDATE statement sets its "b" value to an integer
  97. # immediately afterwards.
  98. #
  99. # However, before the race-condition was fixed, if the reader's SELECT
  100. # statement hit an error (say an SQLITE_LOCKED) at the same time as the
  101. # writer was executing the UPDATE statement, then it could incorrectly
  102. # rollback the statement-transaction belonging to the UPDATE statement.
  103. # The UPDATE statement would still be reported as successful to the user,
  104. # but it would have no effect on the database contents.
  105. #
  106. # Note that it has so far only proved possible to hit this race-condition
  107. # when using an ATTACHed database. There doesn't seem to be any reason
  108. # for this, other than that operating on an ATTACHed database means there
  109. # are a few more mutex grabs and releases during the window of time open
  110. # for the race-condition. Maybe this encourages the scheduler to context
  111. # switch or something...
  112. #
  113. forcedelete test.db test2.db
  114. unset -nocomplain finished
  115. do_test thread005-2.1 {
  116. sqlite3 db test.db
  117. execsql { ATTACH 'test2.db' AS aux }
  118. execsql {
  119. CREATE TABLE aux.t1(a INTEGER PRIMARY KEY, b UNIQUE);
  120. INSERT INTO t1 VALUES(1, 1);
  121. INSERT INTO t1 VALUES(2, 2);
  122. }
  123. db close
  124. } {}
  125. set ThreadProgram {
  126. proc execsql {zSql {db {}}} {
  127. if {$db eq ""} {set db $::DB}
  128. set lRes [list]
  129. set rc SQLITE_OK
  130. while {$rc=="SQLITE_OK" && $zSql ne ""} {
  131. set STMT [sqlite3_prepare_v2 $db $zSql -1 zSql]
  132. while {[set rc [sqlite3_step $STMT]] eq "SQLITE_ROW"} {
  133. for {set i 0} {$i < [sqlite3_column_count $STMT]} {incr i} {
  134. lappend lRes [sqlite3_column_text $STMT 0]
  135. }
  136. }
  137. set rc [sqlite3_finalize $STMT]
  138. }
  139. if {$rc != "SQLITE_OK"} { error "$rc [sqlite3_errmsg $db]" }
  140. return $lRes
  141. }
  142. if {$isWriter} {
  143. set Sql {
  144. BEGIN;
  145. DELETE FROM t1 WHERE a = (SELECT max(a) FROM t1);
  146. INSERT INTO t1 VALUES(NULL, NULL);
  147. UPDATE t1 SET b = a WHERE a = (SELECT max(a) FROM t1);
  148. SELECT count(*) FROM t1 WHERE b IS NULL;
  149. COMMIT;
  150. }
  151. } else {
  152. set Sql {
  153. BEGIN;
  154. SELECT count(*) FROM t1 WHERE b IS NULL;
  155. COMMIT;
  156. }
  157. }
  158. set ::DB [sqlite3_open test.db]
  159. execsql { ATTACH 'test2.db' AS aux }
  160. set result "ok"
  161. set finish [expr [clock_seconds]+5]
  162. while {$result eq "ok" && [clock_seconds] < $finish} {
  163. set rc [catch {execsql $Sql} msg]
  164. if {$rc} {
  165. if {[string match "SQLITE_LOCKED*" $msg]} {
  166. catch { execsql ROLLBACK }
  167. } else {
  168. sqlite3_close $::DB
  169. error $msg
  170. }
  171. } elseif {$msg ne "0"} {
  172. set result "failed"
  173. }
  174. }
  175. sqlite3_close $::DB
  176. set result
  177. }
  178. # There is a race-condition in btree.c that means that if two threads
  179. # attempt to open the same database at roughly the same time, and there
  180. # does not already exist a shared-cache corresponding to that database,
  181. # then two shared-caches can be created instead of one. Things still more
  182. # or less work, but the two database connections do not use the same
  183. # shared-cache.
  184. #
  185. # If the threads run by this test hit this race-condition, the tests
  186. # fail (because SQLITE_BUSY may be unexpectedly returned instead of
  187. # SQLITE_LOCKED). To prevent this from happening, open a couple of
  188. # connections to test.db and test2.db now to make sure that there are
  189. # already shared-caches in memory for all databases opened by the
  190. # test threads.
  191. #
  192. sqlite3 db test.db
  193. sqlite3 db test2.db
  194. puts "Running thread-tests for ~20 seconds"
  195. thread_spawn finished(0) {set isWriter 0} $ThreadProgram
  196. thread_spawn finished(1) {set isWriter 1} $ThreadProgram
  197. if {![info exists finished(0)]} { vwait finished(0) }
  198. if {![info exists finished(1)]} { vwait finished(1) }
  199. catch { db close }
  200. catch { db2 close }
  201. do_test thread005-2.2 {
  202. list $finished(0) $finished(1)
  203. } {ok ok}
  204. do_test thread005-2.3 {
  205. sqlite3 db test.db
  206. execsql { ATTACH 'test2.db' AS aux }
  207. execsql { SELECT count(*) FROM t1 WHERE b IS NULL }
  208. } {0}
  209. sqlite3_enable_shared_cache $::enable_shared_cache
  210. finish_test