1 | /* |
2 | Copyright (c) 2012, Monty Program Ab |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; version 2 of the License. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ |
16 | |
17 | /* |
18 | This file does standalone APC system tests. |
19 | */ |
20 | #include <my_global.h> |
21 | #include <my_pthread.h> |
22 | #include <my_sys.h> |
23 | |
24 | #include <stdio.h> |
25 | |
26 | #include <tap.h> |
27 | |
28 | /* |
29 | A fake THD with enter_cond/exit_cond and some other members. |
30 | */ |
31 | PSI_stage_info stage_show_explain; |
32 | class THD |
33 | { |
34 | mysql_mutex_t* thd_mutex; |
35 | public: |
36 | bool killed; |
37 | |
38 | THD() : killed(FALSE) {} |
39 | inline const char* ENTER_COND(mysql_cond_t *cond, mysql_mutex_t* mutex, |
40 | PSI_stage_info*, PSI_stage_info*) |
41 | { |
42 | mysql_mutex_assert_owner(mutex); |
43 | thd_mutex= mutex; |
44 | return NULL; |
45 | } |
46 | inline void EXIT_COND(PSI_stage_info*) |
47 | { |
48 | mysql_mutex_unlock(thd_mutex); |
49 | } |
50 | }; |
51 | |
52 | #include "../sql/my_apc.h" |
53 | |
54 | #define MY_APC_STANDALONE 1 |
55 | #include "../sql/my_apc.cc" |
56 | |
57 | volatile bool started= FALSE; |
58 | volatile bool service_should_exit= FALSE; |
59 | volatile bool requestors_should_exit=FALSE; |
60 | |
61 | /* Counters for APC calls */ |
62 | int apcs_served= 0; |
63 | int apcs_missed=0; |
64 | int apcs_timed_out=0; |
65 | mysql_mutex_t apc_counters_mutex; |
66 | |
67 | inline void increment_counter(int *var) |
68 | { |
69 | mysql_mutex_lock(&apc_counters_mutex); |
70 | *var= *var+1; |
71 | mysql_mutex_unlock(&apc_counters_mutex); |
72 | } |
73 | |
74 | volatile bool have_errors= false; |
75 | |
76 | Apc_target apc_target; |
77 | mysql_mutex_t target_mutex; |
78 | |
79 | int int_rand(int size) |
80 | { |
81 | return (int) (0.5 + ((double)rand() / RAND_MAX) * size); |
82 | } |
83 | |
84 | /* |
85 | APC target thread (the one that will serve the APC requests). We will have |
86 | one target. |
87 | */ |
88 | void *test_apc_service_thread(void *ptr) |
89 | { |
90 | my_thread_init(); |
91 | mysql_mutex_init(0, &target_mutex, MY_MUTEX_INIT_FAST); |
92 | apc_target.init(&target_mutex); |
93 | apc_target.enable(); |
94 | started= TRUE; |
95 | diag("test_apc_service_thread started" ); |
96 | while (!service_should_exit) |
97 | { |
98 | //apc_target.disable(); |
99 | my_sleep(10000); |
100 | //apc_target.enable(); |
101 | for (int i = 0; i < 10 && !service_should_exit; i++) |
102 | { |
103 | apc_target.process_apc_requests(); |
104 | my_sleep(int_rand(30)); |
105 | } |
106 | } |
107 | apc_target.disable(); |
108 | apc_target.destroy(); |
109 | mysql_mutex_destroy(&target_mutex); |
110 | my_thread_end(); |
111 | pthread_exit(0); |
112 | return NULL; |
113 | } |
114 | |
115 | |
116 | /* |
117 | One APC request (to write 'value' into *where_to) |
118 | */ |
119 | class Apc_order : public Apc_target::Apc_call |
120 | { |
121 | public: |
122 | int value; // The value |
123 | int *where_to; // Where to write it |
124 | Apc_order(int a, int *b) : value(a), where_to(b) {} |
125 | |
126 | void call_in_target_thread() |
127 | { |
128 | my_sleep(int_rand(1000)); |
129 | *where_to = value; |
130 | increment_counter(&apcs_served); |
131 | } |
132 | }; |
133 | |
134 | |
135 | /* |
136 | APC requestor thread. It makes APC requests, and checks if they were actually |
137 | executed. |
138 | */ |
139 | void *test_apc_requestor_thread(void *ptr) |
140 | { |
141 | my_thread_init(); |
142 | diag("test_apc_requestor_thread started" ); |
143 | THD my_thd; |
144 | |
145 | while (!requestors_should_exit) |
146 | { |
147 | int dst_value= 0; |
148 | int src_value= int_rand(4*1000*100); |
149 | /* Create an APC to do "dst_value= src_value" assignment */ |
150 | Apc_order apc_order(src_value, &dst_value); |
151 | bool timed_out; |
152 | |
153 | mysql_mutex_lock(&target_mutex); |
154 | bool res= apc_target.make_apc_call(&my_thd, &apc_order, 60, &timed_out); |
155 | if (res) |
156 | { |
157 | if (timed_out) |
158 | increment_counter(&apcs_timed_out); |
159 | else |
160 | increment_counter(&apcs_missed); |
161 | |
162 | if (dst_value != 0) |
163 | { |
164 | diag("APC was done even though return value says it wasnt!" ); |
165 | have_errors= true; |
166 | } |
167 | } |
168 | else |
169 | { |
170 | if (dst_value != src_value) |
171 | { |
172 | diag("APC was not done even though return value says it was!" ); |
173 | have_errors= true; |
174 | } |
175 | } |
176 | //my_sleep(300); |
177 | } |
178 | diag("test_apc_requestor_thread exiting" ); |
179 | my_thread_end(); |
180 | return NULL; |
181 | } |
182 | |
183 | /* Number of APC requestor threads */ |
184 | const int N_THREADS=23; |
185 | |
186 | |
187 | int main(int args, char **argv) |
188 | { |
189 | pthread_t service_thr; |
190 | pthread_t request_thr[N_THREADS]; |
191 | int i; |
192 | |
193 | my_thread_global_init(); |
194 | |
195 | mysql_mutex_init(0, &apc_counters_mutex, MY_MUTEX_INIT_FAST); |
196 | |
197 | plan(1); |
198 | diag("Testing APC delivery and execution" ); |
199 | |
200 | pthread_create(&service_thr, NULL, test_apc_service_thread, (void*)NULL); |
201 | while (!started) |
202 | my_sleep(1000); |
203 | for (i = 0; i < N_THREADS; i++) |
204 | pthread_create(&request_thr[i], NULL, test_apc_requestor_thread, (void*)NULL); |
205 | |
206 | for (i = 0; i < 15; i++) |
207 | { |
208 | my_sleep(500*1000); |
209 | diag("%d APCs served %d missed" , apcs_served, apcs_missed); |
210 | } |
211 | diag("Shutting down requestors" ); |
212 | requestors_should_exit= TRUE; |
213 | for (i = 0; i < N_THREADS; i++) |
214 | pthread_join(request_thr[i], NULL); |
215 | |
216 | diag("Shutting down service" ); |
217 | service_should_exit= TRUE; |
218 | pthread_join(service_thr, NULL); |
219 | |
220 | mysql_mutex_destroy(&apc_counters_mutex); |
221 | |
222 | diag("Done" ); |
223 | my_thread_end(); |
224 | my_thread_global_end(); |
225 | |
226 | ok1(!have_errors); |
227 | return exit_status(); |
228 | } |
229 | |
230 | |