3
3
require "benchmark"
4
4
5
5
class IntegrationPendingTestCase < SQLite3 ::TestCase
6
+ class ThreadSynchronizer
7
+ def initialize
8
+ @main_to_thread = Queue . new
9
+ @thread_to_main = Queue . new
10
+ end
11
+
12
+ def send_to_thread state
13
+ @main_to_thread . push state
14
+ end
15
+
16
+ def send_to_main state
17
+ @thread_to_main . push state
18
+ end
19
+
20
+ def wait_for_thread expected_state
21
+ state = @thread_to_main . pop
22
+ raise "Invalid state #{ state } . #{ expected_state } is expected" if state != expected_state
23
+ end
24
+
25
+ def wait_for_main expected_state
26
+ state = @main_to_thread . pop
27
+ raise "Invalid state #{ state } . #{ expected_state } is expected" if state != expected_state
28
+ end
29
+
30
+ def close_thread
31
+ @thread_to_main . close
32
+ end
33
+
34
+ def close_main
35
+ @main_to_thread . close
36
+ end
37
+ end
38
+
6
39
def setup
7
40
@db = SQLite3 ::Database . new ( "test.db" )
8
41
@db . transaction do
@@ -19,19 +52,20 @@ def teardown
19
52
end
20
53
21
54
def test_busy_handler_impatient
22
- busy = Mutex . new
23
- busy . lock
55
+ synchronizer = ThreadSynchronizer . new
24
56
handler_call_count = 0
25
57
26
- t = Thread . new do
58
+ t = Thread . new ( synchronizer ) do | sync |
27
59
db2 = SQLite3 ::Database . open ( "test.db" )
28
60
db2 . transaction ( :exclusive ) do
29
- busy . lock
61
+ sync . send_to_main :ready_0
62
+ sync . wait_for_main :end_1
30
63
end
31
64
ensure
32
65
db2 &.close
66
+ sync . close_thread
33
67
end
34
- sleep 1
68
+ synchronizer . wait_for_thread :ready_0
35
69
36
70
@db . busy_handler do
37
71
handler_call_count += 1
@@ -42,34 +76,37 @@ def test_busy_handler_impatient
42
76
@db . execute "insert into foo (b) values ( 'from 2' )"
43
77
end
44
78
45
- busy . unlock
79
+ synchronizer . send_to_thread :end_1
80
+ synchronizer . close_main
46
81
t . join
47
82
48
83
assert_equal 1 , handler_call_count
49
84
end
50
85
51
86
def test_busy_timeout
52
87
@db . busy_timeout 1000
53
- busy = Mutex . new
54
- busy . lock
88
+ synchronizer = ThreadSynchronizer . new
55
89
56
- t = Thread . new do
90
+ t = Thread . new ( synchronizer ) do | sync |
57
91
db2 = SQLite3 ::Database . open ( "test.db" )
58
92
db2 . transaction ( :exclusive ) do
59
- busy . lock
93
+ sync . send_to_main :ready_0
94
+ sync . wait_for_main :end_1
60
95
end
61
96
ensure
62
97
db2 &.close
98
+ sync . close_thread
63
99
end
64
100
65
- sleep 1
101
+ synchronizer . wait_for_thread :ready_0
66
102
time = Benchmark . measure do
67
103
assert_raise ( SQLite3 ::BusyException ) do
68
104
@db . execute "insert into foo (b) values ( 'from 2' )"
69
105
end
70
106
end
71
107
72
- busy . unlock
108
+ synchronizer . send_to_thread :end_1
109
+ synchronizer . close_main
73
110
t . join
74
111
75
112
assert_operator time . real * 1000 , :>= , 1000
@@ -110,4 +147,38 @@ def test_busy_handler_timeout_releases_gvl
110
147
111
148
assert_operator work . size - work . find_index ( "|" ) , :> , 3
112
149
end
150
+
151
+ def test_busy_handler_outwait
152
+ synchronizer = ThreadSynchronizer . new
153
+ handler_call_count = 0
154
+
155
+ t = Thread . new ( synchronizer ) do |sync |
156
+ db2 = SQLite3 ::Database . open ( "test.db" )
157
+ db2 . transaction ( :exclusive ) do
158
+ sync . send_to_main :ready_0
159
+ sync . wait_for_main :busy_handler_called_1
160
+ end
161
+ sync . send_to_main :end_of_transaction_2
162
+ ensure
163
+ db2 &.close
164
+ sync . close_thread
165
+ end
166
+
167
+ @db . busy_handler do |count |
168
+ handler_call_count += 1
169
+ synchronizer . send_to_thread :busy_handler_called_1
170
+ synchronizer . wait_for_thread :end_of_transaction_2
171
+ true
172
+ end
173
+
174
+ synchronizer . wait_for_thread :ready_0
175
+ assert_nothing_raised do
176
+ @db . execute "insert into foo (b) values ( 'from 2' )"
177
+ end
178
+
179
+ synchronizer . close_main
180
+ t . join
181
+
182
+ assert_equal 1 , handler_call_count
183
+ end
113
184
end
0 commit comments