1# 2007 September 10 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# This file contains tests that attempt to break the pcache module 13# by bombarding it with simultaneous requests from multiple threads. 14# 15# $Id: thread003.test,v 1.5 2008/10/07 15:25:49 drh Exp $ 16 17set testdir [file dirname $argv0] 18 19source $testdir/tester.tcl 20source $testdir/thread_common.tcl 21if {[info commands sqlthread] eq ""} { 22 finish_test 23 return 24} 25ifcapable !mutex { 26 finish_test 27 return 28} 29 30# Set up a couple of different databases full of pseudo-randomly 31# generated data. 32# 33do_test thread003.1.1 { 34 execsql { 35 BEGIN; 36 CREATE TABLE t1(a, b, c); 37 } 38 for {set ii 0} {$ii < 5000} {incr ii} { 39 execsql {INSERT INTO t1 VALUES($ii, randomblob(200), randomblob(200))} 40 } 41 execsql { 42 CREATE INDEX i1 ON t1(a, b); 43 COMMIT; 44 } 45} {} 46do_test thread003.1.2 { 47 expr {([file size test.db] / 1024) > 2000} 48} {1} 49do_test thread003.1.3 { 50 db close 51 file delete -force test2.db 52 sqlite3 db test2.db 53} {} 54do_test thread003.1.4 { 55 execsql { 56 BEGIN; 57 CREATE TABLE t1(a, b, c); 58 } 59 for {set ii 0} {$ii < 5000} {incr ii} { 60 execsql {INSERT INTO t1 VALUES($ii, randomblob(200), randomblob(200))} 61 } 62 execsql { 63 CREATE INDEX i1 ON t1(a, b); 64 COMMIT; 65 } 66} {} 67do_test thread003.1.5 { 68 expr {([file size test.db] / 1024) > 2000} 69} {1} 70do_test thread003.1.6 { 71 db close 72} {} 73 74 75# This test opens a connection on each of the large (>2MB) database files 76# created by the previous block. The connections do not share a cache. 77# Both "cache_size" parameters are set to 15, so there is a maximum of 78# 30 pages available globally. 79# 80# Then, in separate threads, the databases are randomly queried over and 81# over again. This will force the connections to recycle clean pages from 82# each other. If there is a thread-safety problem, a segfault or assertion 83# failure may eventually occur. 84# 85set nSecond 30 86puts "Starting thread003.2 (should run for ~$nSecond seconds)" 87do_test thread003.2 { 88 foreach zFile {test.db test2.db} { 89 set SCRIPT [format { 90 set iEnd [expr {[clock_seconds] + %d}] 91 set ::DB [sqlthread open %s] 92 93 # Set the cache size to 15 pages per cache. 30 available globally. 94 execsql { PRAGMA cache_size = 15 } 95 96 while {[clock_seconds] < $iEnd} { 97 set iQuery [expr {int(rand()*5000)}] 98 execsql " SELECT * FROM t1 WHERE a = $iQuery " 99 } 100 101 sqlite3_close $::DB 102 expr 1 103 } $nSecond $zFile] 104 105 unset -nocomplain finished($zFile) 106 thread_spawn finished($zFile) $thread_procs $SCRIPT 107 } 108 foreach zFile {test.db test2.db} { 109 if {![info exists finished($zFile)]} { 110 vwait finished($zFile) 111 } 112 } 113 expr 0 114} {0} 115 116# This test is the same as the test above, except that each thread also 117# writes to the database. This causes pages to be moved back and forth 118# between the caches internal dirty and clean lists, which is another 119# opportunity for a thread-related bug to present itself. 120# 121set nSecond 30 122puts "Starting thread003.3 (should run for ~$nSecond seconds)" 123do_test thread003.3 { 124 foreach zFile {test.db test2.db} { 125 set SCRIPT [format { 126 set iStart [clock_seconds] 127 set iEnd [expr {[clock_seconds] + %d}] 128 set ::DB [sqlthread open %s] 129 130 # Set the cache size to 15 pages per cache. 30 available globally. 131 execsql { PRAGMA cache_size = 15 } 132 133 while {[clock_seconds] < $iEnd} { 134 set iQuery [expr {int(rand()*5000)}] 135 execsql "SELECT * FROM t1 WHERE a = $iQuery" 136 execsql "UPDATE t1 SET b = randomblob(200) 137 WHERE a < $iQuery AND a > $iQuery + 20 138 " 139 } 140 141 sqlite3_close $::DB 142 expr 1 143 } $nSecond $zFile] 144 145 unset -nocomplain finished($zFile) 146 thread_spawn finished($zFile) $thread_procs $SCRIPT 147 } 148 foreach zFile {test.db test2.db} { 149 if {![info exists finished($zFile)]} { 150 vwait finished($zFile) 151 } 152 } 153 expr 0 154} {0} 155 156# In this test case, one thread is continually querying the database. 157# The other thread does not have a database connection, but calls 158# sqlite3_release_memory() over and over again. 159# 160set nSecond 30 161puts "Starting thread003.3 (should run for ~$nSecond seconds)" 162unset -nocomplain finished(1) 163unset -nocomplain finished(2) 164do_test thread003.4 { 165 thread_spawn finished(1) $thread_procs [format { 166 set iEnd [expr {[clock_seconds] + %d}] 167 set ::DB [sqlthread open test.db] 168 169 # Set the cache size to 15 pages per cache. 30 available globally. 170 execsql { PRAGMA cache_size = 15 } 171 172 while {[clock_seconds] < $iEnd} { 173 set iQuery [expr {int(rand()*5000)}] 174 execsql "SELECT * FROM t1 WHERE a = $iQuery" 175 } 176 177 sqlite3_close $::DB 178 expr 1 179 } $nSecond] 180 thread_spawn finished(2) [format { 181 set iEnd [expr {[clock_seconds] + %d}] 182 183 while {[clock_seconds] < $iEnd} { 184 sqlite3_release_memory 1000 185 } 186 } $nSecond] 187 188 foreach ii {1 2} { 189 if {![info exists finished($ii)]} { 190 vwait finished($ii) 191 } 192 } 193 expr 0 194} {0} 195 196finish_test 197