From 2ab2d55f017b0945a8722ccb0d6e5133b0df15a4 Mon Sep 17 00:00:00 2001 From: drh <drh@noemail.net> Date: Wed, 15 May 2013 16:08:33 +0000 Subject: [PATCH] Fix the sharedA.test module so that it does not attempt to run TCL callbacks on a different thread from where the interpreter was originally created. FossilOrigin-Name: 65ff754e3521aa8ee9135d235166cac2a8f57ebd --- manifest | 14 +++++------ manifest.uuid | 2 +- test/sharedA.test | 64 ++++++++++++++++++++++++++++++++++++----------- 3 files changed, 58 insertions(+), 22 deletions(-) diff --git a/manifest b/manifest index 024b7eeade..cda4f88975 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\srun\ssharedA.test\sif\sthe\ssystem\sis\snot\sthreadsafe. -D 2013-05-15T15:53:52.221 +C Fix\sthe\ssharedA.test\smodule\sso\sthat\sit\sdoes\snot\sattempt\sto\srun\sTCL\scallbacks\non\sa\sdifferent\sthread\sfrom\swhere\sthe\sinterpreter\swas\soriginally\screated. +D 2013-05-15T16:08:33.601 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in ce81671efd6223d19d4c8c6b88ac2c4134427111 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -738,7 +738,7 @@ F test/shared6.test 866bb4982c45ce216c61ded5e8fde4e7e2f3ffa9 F test/shared7.test 960760bc8d03e1419e70dea69cf41db62853616e F test/shared8.test b27befbefbe7f4517f1d6b7ff8f64a41ec74165d F test/shared9.test 5f2a8f79b4d6c7d107a01ffa1ed05ae7e6333e21 -F test/sharedA.test 8eddeda2407bdcd335ec8b8ba731aefc0e3e8232 +F test/sharedA.test 0cdf1a76dfa00e6beee66af5b534b1e8df2720f5 F test/shared_err.test 0079c05c97d88cfa03989b7c20a8b266983087aa F test/sharedlock.test ffa0a3c4ac192145b310f1254f8afca4d553eabf F test/shell1.test 4a2f57952719972c6f862134463f8712e953c038 @@ -1064,7 +1064,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 47dd65a890955f333d431e275f3f4d95d34a5ba5 -R 7b8246f3bd54a0ac6d93356e2396ef91 -U dan -Z d923d1c201be4d3e0bbb63e5d84a5ca9 +P d484eaf8d6dfaf2c1065b93b2a52a6db91c09fa4 +R 2b3d38cfaeff54ce7cad59c19d675f6e +U drh +Z adae8d677b3886690161b439bdcd47db diff --git a/manifest.uuid b/manifest.uuid index 7ab7a5a608..2eaef6834a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d484eaf8d6dfaf2c1065b93b2a52a6db91c09fa4 \ No newline at end of file +65ff754e3521aa8ee9135d235166cac2a8f57ebd \ No newline at end of file diff --git a/test/sharedA.test b/test/sharedA.test index e0031ccc03..146fb26be0 100644 --- a/test/sharedA.test +++ b/test/sharedA.test @@ -89,11 +89,31 @@ do_test 1.3 { #------------------------------------------------------------------------- # +# sqlite3RollbackAll() loops through all attached b-trees and rolls +# back each one separately. Then if the SQLITE_InternChanges flag is +# set, it resets the schema. Both of the above steps must be done +# while holding a mutex, otherwise another thread might slip in and +# try to use the new schema with the old data. +# +# The following sequence of tests attempt to verify that the actions +# taken by sqlite3RollbackAll() are thread-atomic (that they cannot be +# interrupted by a separate thread.) +# +# Note that a TCL interpreter can only be used within the thread in which +# it was originally created (because it uses thread-local-storage). +# The tvfs callbacks must therefore only run on the main thread. +# There is some trickery in the read_callback procedure to ensure that +# this is the case. +# testvfs tvfs -tvfs filter xRead -tvfs script read_callback -proc read_callback {args} { } +# Set up two databases and two database connections. +# +# db1: main(test.db), two(test2.db) +# db2: main(test.db) +# +# The cache for test.db is shared between db1 and db2. +# do_test 2.1 { forcedelete test.db test.db2 sqlite3 db1 test.db -vfs tvfs @@ -112,12 +132,12 @@ do_test 2.1 { db2 eval { SELECT * FROM t1 } } {1 2 3} +# Create a prepared statement on db2 that will attempt a schema change +# in test.db. Meanwhile, start a transaction on db1 that changes +# the schema of test.db and that creates a rollback journal on test2.db +# do_test 2.2 { set ::STMT [sqlite3_prepare db2 "CREATE INDEX i1 ON t1(x)" -1 tail] - set {} {} -} {} - -do_test 2.3 { db1 eval { BEGIN; CREATE INDEX i1 ON t1(x); @@ -125,21 +145,38 @@ do_test 2.3 { } } {} -set ::bFired 0 +# Set up a callback that will cause db2 to try to execute its +# schema change when db1 accesses the journal file of test2.db. +# +# This callback will be invoked after the content of test.db has +# be rolled back but before the schema has been reset. If the +# sqlite3RollbackAll() operation is not thread-atomic, then the +# db2 statement in the callback will see old content with the newer +# schema, which is wrong. +# +tvfs filter xRead +tvfs script read_callback +unset -nocomplain ::some_time_laster +unset -nocomplain ::thread_result proc read_callback {call file args} { - if { $::bFired==0 && [string match *test.db2-journal $file] } { + if {[string match *test.db2-journal $file]} { + tvfs filter {} ;# Ensure that tvfs callbacks to do run on the + # child thread sqlthread spawn ::thread_result [subst -nocommands { sqlite3_step $::STMT set rc [sqlite3_finalize $::STMT] }] - after 1000 { set ::bFired 1 } - vwait ::bFired + after 1000 { set ::some_time_later 1 } + vwait ::some_time_later } } -do_test 2.4 { db1 eval ROLLBACK } {} +do_test 2.3 { db1 eval ROLLBACK } {} +# Verify that the db2 statement invoked by the callback detected the +# schema change. +# if {[info exists ::thread_result]==0} { vwait ::thread_result } -do_test 2.5 { +do_test 2.4 { list $::thread_result [sqlite3_errmsg db2] } {SQLITE_SCHEMA {database schema has changed}} @@ -149,4 +186,3 @@ tvfs delete sqlite3_enable_shared_cache $::enable_shared_cache finish_test -