xref: /sqlite-3.40.0/test/thread003.test (revision 18472fa7)
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